Skip to content

Commit

Permalink
feat: port entry points to click cli; add tests (#483)
Browse files Browse the repository at this point in the history
* chore: add click to install dependencies

Note, black uses click, so gets installed with lint deps (pip install -e .[lint], but not without.

* feat: use click for fill and other pytest-based commands

* docs: update docs for new click --help flag

Now fill only shows EEST-specific help when --help is provided.

* refactor: rename entry_points folder to cli

* refactor: port pyspelling and markdownlint to click

* refactor: port order_fixtures to click

* chore: fix import in docstring

* feat: return pytest exit code from click command

* fix: return 0 from fill help command, as pytest does

* tests(fw): add tests for fill/pytest click cli

* tests: add tests for order fixtures

* chore: add typehints

* feat: port hasher to click

* feat: port evm_bytes_to_python to click

* docs: update changelog
  • Loading branch information
danceratopz authored Apr 1, 2024
1 parent 392c7fe commit c6a2d33
Show file tree
Hide file tree
Showing 20 changed files with 413 additions and 173 deletions.
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Test fixtures for use by clients are available for each release on the [Github r

- 🐞 Fix CI by using Golang 1.21 in Github Actions to build geth ([#484](https://github.com/ethereum/execution-spec-tests/pull/484)).
- 💥 "Merge" has been renamed to "Paris" in the "network" field of the Blockchain tests, and in the "post" field of the State tests ([#480](https://github.com/ethereum/execution-spec-tests/pull/480)).
- ✨ Port entry point scripts to use [click](https://click.palletsprojects.com) and add tests ([#483](https://github.com/ethereum/execution-spec-tests/pull/483)).

## 🔜 [v2.1.1](https://github.com/ethereum/execution-spec-tests/releases/tag/v2.1.1) - 2024-03-09

Expand Down
8 changes: 5 additions & 3 deletions docs/getting_started/executing_tests_command_line.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The execution-spec-tests test framework uses the [pytest framework](https://docs
The command-line options specific to filling tests can be listed via:

```console
fill --test-help
fill --help
```

See [Custom `fill` Command-Line Options](#custom-fill-command-line-options) for all options.
Expand Down Expand Up @@ -103,10 +103,12 @@ fill -s # Print stdout from tests to the console during execution

## Custom `fill` Command-Line Options

Options added by the execution-spec-tests pytest plugins can be listed with:
To see all the options available to fill, including pytest and pytest plugin options, use `--pytest-help`.

To list the options that only specific to fill, use:

```console
fill --test-help
fill --help
```

Output:
Expand Down
16 changes: 8 additions & 8 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ install_requires =
coincurve>=18.0.0,<19
trie==2.1.1
semver==3.0.1
click>=8.0.0,<9

[options.package_data]
ethereum_test_tools =
Expand All @@ -44,14 +45,13 @@ evm_transition_tool =

[options.entry_points]
console_scripts =
fill = entry_points.fill:main
tf = entry_points.tf:main
order_fixtures = entry_points.order_fixtures:main
pyspelling_soft_fail = entry_points.pyspelling_soft_fail:main
markdownlintcli2_soft_fail = entry_points.markdownlintcli2_soft_fail:main
create_whitelist_for_flake8_spelling = entry_points.create_whitelist_for_flake8_spelling:main
evm_bytes_to_python = entry_points.evm_bytes_to_python:main
hasher = entry_points.hasher:main
fill = cli.pytest_commands:fill
tf = cli.pytest_commands:tf
pyspelling_soft_fail = cli.tox_helpers:pyspelling
markdownlintcli2_soft_fail = cli.tox_helpers:markdownlint
order_fixtures = cli.order_fixtures:order_fixtures
evm_bytes_to_python = cli.evm_bytes_to_python:main
hasher = cli.hasher:main

[options.extras_require]
test =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
Define an entry point wrapper for pytest.
"""

import sys
from typing import Any, List, Optional

import click

from ethereum_test_tools import Macro
from ethereum_test_tools import Opcodes as Op

Expand Down Expand Up @@ -39,19 +40,17 @@ def process_evm_bytes(evm_bytes_hex_string: Any) -> str: # noqa: D103
return " + ".join(opcodes_strings)


def print_help(): # noqa: D103
print("Usage: evm_bytes_to_python <EVM bytes hex string>")

@click.command()
@click.argument("evm_bytes_hex_string")
def main(evm_bytes_hex_string: str):
"""
Convert the given EVM bytes hex string to an EEST Opcodes.
def main(): # noqa: D103
if len(sys.argv) != 2:
print_help()
sys.exit(1)
if sys.argv[1] in ["-h", "--help"]:
print_help()
sys.exit(0)
evm_bytes_hex_string = sys.argv[1]
print(process_evm_bytes(evm_bytes_hex_string))
\b
EVM_BYTES_HEX_STRING: A hex string representing EVM bytes to be processed.
""" # noqa: D301
processed_output = process_evm_bytes(evm_bytes_hex_string)
click.echo(processed_output)


if __name__ == "__main__":
Expand Down
39 changes: 21 additions & 18 deletions src/entry_points/hasher.py → src/cli/hasher.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
Simple CLI tool to hash a directory of JSON fixtures.
"""

import argparse
import hashlib
import json
from dataclasses import dataclass, field
from enum import IntEnum, auto
from pathlib import Path
from typing import Dict, List, Optional

import click


class HashableItemType(IntEnum):
"""
Expand Down Expand Up @@ -106,30 +107,32 @@ def from_folder(cls, *, folder_path: Path, parents: List[str] = []) -> "Hashable
return cls(type=HashableItemType.FOLDER, items=items, parents=parents)


def main() -> None:
@click.command()
@click.argument(
"folder_path_str", type=click.Path(exists=True, file_okay=False, dir_okay=True, readable=True)
)
@click.option("--files", "-f", is_flag=True, help="Print hash of files")
@click.option("--tests", "-t", is_flag=True, help="Print hash of tests")
@click.option("--root", "-r", is_flag=True, help="Only print hash of root folder")
def main(folder_path_str: str, files: bool, tests: bool, root: bool) -> None:
"""
Main function.
Hash folders of JSON fixtures and print their hashes.
"""
parser = argparse.ArgumentParser(description="Hash folders of JSON fixtures.")

parser.add_argument("folder_path", type=Path, help="The path to the JSON fixtures directory")
parser.add_argument("--files", "-f", action="store_true", help="Print hash of files")
parser.add_argument("--tests", "-t", action="store_true", help="Print hash of tests")
parser.add_argument("--root", "-r", action="store_true", help="Only print hash of root folder")

args = parser.parse_args()

item = HashableItem.from_folder(folder_path=args.folder_path)
folder_path: Path = Path(folder_path_str)
item = HashableItem.from_folder(folder_path=folder_path)

if args.root:
if root:
print(f"0x{item.hash().hex()}")
return

print_type: Optional[HashableItemType] = None

if args.files:
if files:
print_type = HashableItemType.FILE
elif args.tests:
elif tests:
print_type = HashableItemType.TEST

item.print(name=args.folder_path.name, print_type=print_type)
item.print(name=folder_path.name, print_type=print_type)


if __name__ == "__main__":
main()
45 changes: 27 additions & 18 deletions src/entry_points/order_fixtures.py → src/cli/order_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,25 @@
Functions and CLI interface for recursively ordering and sorting .json files.
example: Usage
```
python order_fixtures.py -i input_dir -o output_dir
# or using the entry point
order_fixtures -i input_dir -o output_dir
```
The CLI interface takes the paths of an input directory and an output
directory. It recursively processes each .json file in the input directory and
its subdirectories, and sorts lists and dictionaries alphabetically and
writes the sorted output to .json files to the corresponding locations in the
output directory.
"""

import argparse
import json
from pathlib import Path
from typing import Any, Dict, List, Union, cast

import click


def recursive_sort(item: Union[Dict[str, Any], List[Any]]) -> Union[Dict[str, Any], List[Any]]:
"""
Expand Down Expand Up @@ -95,23 +96,31 @@ def process_directory(input_dir: Path, output_dir: Path):
order_fixture(child, output_dir / child.name)


def main():
@click.command()
@click.option(
"--input",
"-i",
"input_dir",
type=click.Path(exists=True, file_okay=False, dir_okay=True, readable=True),
required=True,
help="The input directory",
)
@click.option(
"--output",
"-o",
"output_dir",
type=click.Path(writable=True, file_okay=False, dir_okay=True),
required=True,
help="The output directory",
)
def order_fixtures(input_dir, output_dir):
"""
Main function.
Returns:
None.
Order json fixture by key recursively from the input directory.
"""
parser = argparse.ArgumentParser(description="Order fixtures.")
parser.add_argument(
"--input", "-i", dest="input_dir", type=Path, required=True, help="The input directory"
)
parser.add_argument(
"--output", "-o", dest="output_dir", type=Path, required=True, help="The output directory"
)
args = parser.parse_args()
process_directory(args.input_dir, args.output_dir)
input_dir = Path(input_dir)
output_dir = Path(output_dir)
process_directory(input_dir, output_dir)


if __name__ == "__main__":
main()
order_fixtures()
116 changes: 116 additions & 0 deletions src/cli/pytest_commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""
CLI entry points for the main pytest-based commands provided by
execution-spec-tests.
These can be directly accessed in a prompt if the user has directly installed
the package via:
```
python -m venv venv
source venv/bin/activate
pip install -e .
# or
pip install -e .[doc,lint,test]
```
Then, the entry points can be executed via:
```
fill --help
# for example, or
fill --collect-only
```
They can also be executed (and debugged) directly in an interactive python
shell:
```
from src.cli.pytest_commands import fill
from click.testing import CliRunner
runner = CliRunner()
result = runner.invoke(fill, ["--help"])
print(result.output)
```
"""

import sys
from typing import Any, Callable, List

import click
import pytest

# Define a custom type for decorators, which are functions that return functions.
Decorator = Callable[[Callable[..., Any]], Callable[..., Any]]


@click.command(context_settings=dict(ignore_unknown_options=True))
def tf() -> None:
"""
The `tf` command, deprecated as of 2023-06.
"""
print(
"The `tf` command-line tool has been superseded by `fill`. Try:\n\n"
"fill --help\n\n"
"or see the online docs:\n"
"https://ethereum.github.io/execution-spec-tests/getting_started/executing_tests_command_line/" # noqa: E501
)
sys.exit(1)


def common_click_options(func: Callable[..., Any]) -> Decorator:
"""
Define common click options for fill and other pytest-based commands.
Note that we don't verify any other options here, rather pass them
directly to the pytest command for processing.
"""
func = click.option(
"-h",
"--help",
"help_flag",
is_flag=True,
default=False,
expose_value=True,
help="Show help message.",
)(func)

func = click.option(
"--pytest-help",
"pytest_help_flag",
is_flag=True,
default=False,
expose_value=True,
help="Show pytest's help message.",
)(func)

return click.argument("pytest_args", nargs=-1, type=click.UNPROCESSED)(func)


def handle_help_flags(
pytest_args: List[str], help_flag: bool, pytest_help_flag: bool
) -> List[str]:
"""
Modify the arguments passed to the click CLI command before forwarding to
the pytest command.
This is to make `--help` more useful because `pytest --help` is extremely
verbose and lists all flags from pytest and pytest plugins.
"""
if help_flag:
return ["--test-help"]
elif pytest_help_flag:
return ["--help"]
else:
return list(pytest_args)


@click.command(context_settings=dict(ignore_unknown_options=True))
@common_click_options
def fill(pytest_args: List[str], help_flag: bool, pytest_help_flag: bool) -> None:
"""
Entry point for the fill command.
"""
args = handle_help_flags(pytest_args, help_flag, pytest_help_flag)
result = pytest.main(args)
sys.exit(result)
3 changes: 3 additions & 0 deletions src/cli/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
Tests for scripts and apps in `cli` .
"""
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Test suite for `entry_points.evm_bytes_to_python` module.
Test suite for `cli.evm_bytes_to_python` module.
"""

import pytest
Expand Down
Loading

0 comments on commit c6a2d33

Please sign in to comment.