Skip to content

Commit

Permalink
feat: support postponed type hint evaluation (#150)
Browse files Browse the repository at this point in the history
  • Loading branch information
eonu authored Sep 23, 2024
1 parent 52442e9 commit fb9164d
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 11 deletions.
9 changes: 0 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -778,15 +778,6 @@ This installs Feud with the optional dependencies:

To install Feud without any optional dependencies, simply run `pip install feud`.

> [!CAUTION]
> Feud **will break** if used with postponed type hint evaluation ([PEP563](https://peps.python.org/pep-0563/)), i.e.:
>
> ```python
> from __future__ import annotations
> ```
>
> This is because Feud relies on type hint evaluation in order to determine the expected input type for command parameters.
### Improved formatting with Rich

Below is a comparison of Feud with and without `rich-click`.
Expand Down
4 changes: 2 additions & 2 deletions feud/_internal/_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def decorate( # noqa: PLR0915
var_positional: str | None = None
params: list[click.Parameter] = []

sig: inspect.signature = inspect.signature(func)
sig: inspect.signature = inspect.signature(func, eval_str=True)

for i, (param_name, param_spec) in enumerate(sig.parameters.items()):
# store names of positional arguments
Expand Down Expand Up @@ -248,7 +248,7 @@ def build_command_state( # noqa: PLR0915

state.description: str | None = _docstring.get_description(doc)

sig: inspect.Signature = inspect.signature(func)
sig: inspect.Signature = inspect.signature(func, eval_str=True)

for param, spec in sig.parameters.items():
meta = ParameterSpec()
Expand Down
91 changes: 91 additions & 0 deletions tests/unit/test_core/test_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,97 @@ def command(
assert e.default is True


def test_full_signature_string_hints(
capsys: pytest.CaptureFixture,
) -> None:
@feud.command
def command(
a: "float",
/,
b: "str",
*c: "t.PositiveInt",
d: "int",
e: "bool" = True,
**f: "float",
) -> None:
"""Does something.
Parameters
----------
a:
Test 1.
b:
Test 2.
*c:
Test 3.
d:
Test 4.
e:
Test 5.
**f:
Test 6.
"""
return a, b, c, d, e, f

# check params (**f should be ignored)
params = command.params
assert len(params) == 5

a = command.params[0]
assert isinstance(a, click.Argument)
assert a.name == "a"
assert a.type == click.FLOAT
assert a.opts == ["a"]
assert a.secondary_opts == []
assert a.nargs == 1
assert a.required
assert a.default is None

b = command.params[1]
assert isinstance(b, click.Argument)
assert b.name == "b"
assert b.type == click.STRING
assert b.opts == ["b"]
assert b.secondary_opts == []
assert b.nargs == 1
assert b.required
assert b.default is None

c = command.params[2]
assert isinstance(c, click.Argument)
assert c.name == "c"
assert isinstance(c.type, click.IntRange)
assert c.type.min == 0
assert c.type.min_open
assert c.opts == ["c"]
assert c.secondary_opts == []
assert c.nargs == -1
assert not c.required
assert c.default is None

d = command.params[3]
assert isinstance(d, click.Option)
assert d.name == "d"
assert d.help == "Test 4."
assert d.type == click.INT
assert d.opts == ["--d"]
assert d.secondary_opts == []
assert d.nargs == 1
assert d.required
assert d.default is None

e = command.params[4]
assert isinstance(e, click.Option)
assert e.name == "e"
assert e.help == "Test 5."
assert e.type == click.BOOL
assert e.opts == ["--e"]
assert e.secondary_opts == ["--no-e"]
assert e.nargs == 1
assert not e.required
assert e.default is True


def test_argument_default() -> None:
@feud.command
def f(
Expand Down

0 comments on commit fb9164d

Please sign in to comment.