📝 Update docs examples for custom param types using Annotated
, fix overloads for typer.Argument
(#594)
* 📝 Add source examples for custom parameter types with Annotated * ✅ Add tests for custom parameters with Annotated * 📝 Update docs for custom parameters with Annotated * ♻️ Fix overloads default in Argument after Annotated * ✅ Fix test for custom param types
This commit is contained in:
parent
8c59b68c62
commit
58522bf0fc
9 changed files with 169 additions and 12 deletions
|
@ -11,10 +11,20 @@ There are two ways to achieve this:
|
|||
|
||||
`typer.Argument` and `typer.Option` can create custom parameter types with a `parser` <abbr title="something that can be called like a function">callable</abbr>.
|
||||
|
||||
=== "Python 3.6+"
|
||||
|
||||
```Python hl_lines="12-13 17 18"
|
||||
{!../docs_src/parameter_types/custom_types/tutorial001.py!}
|
||||
```
|
||||
```Python hl_lines="13-14 18-19"
|
||||
{!> ../docs_src/parameter_types/custom_types/tutorial001_an.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.6+ non-Annotated"
|
||||
|
||||
!!! tip
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
```Python hl_lines="12-13 17-18"
|
||||
{!> ../docs_src/parameter_types/custom_types/tutorial001.py!}
|
||||
```
|
||||
|
||||
The function (or callable) that you pass to the parameter `parser` will receive the input value as a string and should return the parsed value with your own custom type.
|
||||
|
||||
|
@ -22,6 +32,17 @@ The function (or callable) that you pass to the parameter `parser` will receive
|
|||
|
||||
If you already have a <a href="https://click.palletsprojects.com/en/8.1.x/parameters/#implementing-custom-types" class="external-link" target="_blank">Click Custom Type</a>, you can use it in `typer.Argument()` and `typer.Option()` with the `click_type` parameter.
|
||||
|
||||
```Python hl_lines="13-17 21 22"
|
||||
{!../docs_src/parameter_types/custom_types/tutorial002.py!}
|
||||
```
|
||||
=== "Python 3.6+"
|
||||
|
||||
```Python hl_lines="14-18 22-25"
|
||||
{!> ../docs_src/parameter_types/custom_types/tutorial002_an.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.6+ non-Annotated"
|
||||
|
||||
!!! tip
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
```Python hl_lines="13-17 21-22"
|
||||
{!> ../docs_src/parameter_types/custom_types/tutorial002.py!}
|
||||
```
|
||||
|
|
|
@ -14,7 +14,7 @@ def parse_custom_class(value: str):
|
|||
|
||||
|
||||
def main(
|
||||
custom_arg: CustomClass = typer.Argument("X", parser=parse_custom_class),
|
||||
custom_arg: CustomClass = typer.Argument(parser=parse_custom_class),
|
||||
custom_opt: CustomClass = typer.Option("Y", parser=parse_custom_class),
|
||||
):
|
||||
print(f"custom_arg is {custom_arg}")
|
||||
|
|
26
docs_src/parameter_types/custom_types/tutorial001_an.py
Normal file
26
docs_src/parameter_types/custom_types/tutorial001_an.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
import typer
|
||||
from typing_extensions import Annotated
|
||||
|
||||
|
||||
class CustomClass:
|
||||
def __init__(self, value: str):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return f"<CustomClass: value={self.value}>"
|
||||
|
||||
|
||||
def parse_custom_class(value: str):
|
||||
return CustomClass(value * 2)
|
||||
|
||||
|
||||
def main(
|
||||
custom_arg: Annotated[CustomClass, typer.Argument(parser=parse_custom_class)],
|
||||
custom_opt: Annotated[CustomClass, typer.Option(parser=parse_custom_class)] = "Foo",
|
||||
):
|
||||
print(f"custom_arg is {custom_arg}")
|
||||
print(f"--custom-opt is {custom_opt}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
typer.run(main)
|
|
@ -18,8 +18,8 @@ class CustomClassParser(click.ParamType):
|
|||
|
||||
|
||||
def main(
|
||||
custom_arg: CustomClass = typer.Argument("X", click_type=CustomClassParser()),
|
||||
custom_opt: CustomClass = typer.Option("Y", click_type=CustomClassParser()),
|
||||
custom_arg: CustomClass = typer.Argument(click_type=CustomClassParser()),
|
||||
custom_opt: CustomClass = typer.Option("Foo", click_type=CustomClassParser()),
|
||||
):
|
||||
print(f"custom_arg is {custom_arg}")
|
||||
print(f"--custom-opt is {custom_opt}")
|
||||
|
|
32
docs_src/parameter_types/custom_types/tutorial002_an.py
Normal file
32
docs_src/parameter_types/custom_types/tutorial002_an.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
import click
|
||||
import typer
|
||||
from typing_extensions import Annotated
|
||||
|
||||
|
||||
class CustomClass:
|
||||
def __init__(self, value: str):
|
||||
self.value = value
|
||||
|
||||
def __repr__(self):
|
||||
return f"<CustomClass: value={self.value}>"
|
||||
|
||||
|
||||
class CustomClassParser(click.ParamType):
|
||||
name = "CustomClass"
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
return CustomClass(value * 3)
|
||||
|
||||
|
||||
def main(
|
||||
custom_arg: Annotated[CustomClass, typer.Argument(click_type=CustomClassParser())],
|
||||
custom_opt: Annotated[
|
||||
CustomClass, typer.Option(click_type=CustomClassParser())
|
||||
] = "Foo",
|
||||
):
|
||||
print(f"custom_arg is {custom_arg}")
|
||||
print(f"--custom-opt is {custom_opt}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
typer.run(main)
|
|
@ -0,0 +1,39 @@
|
|||
import subprocess
|
||||
import sys
|
||||
|
||||
import typer
|
||||
from typer.testing import CliRunner
|
||||
|
||||
from docs_src.parameter_types.custom_types import tutorial001_an as mod
|
||||
|
||||
runner = CliRunner()
|
||||
|
||||
app = typer.Typer()
|
||||
app.command()(mod.main)
|
||||
|
||||
|
||||
def test_help():
|
||||
result = runner.invoke(app, ["--help"])
|
||||
assert result.exit_code == 0
|
||||
|
||||
|
||||
def test_parse_custom_type():
|
||||
result = runner.invoke(app, ["0", "--custom-opt", "1"])
|
||||
assert "custom_arg is <CustomClass: value=00>" in result.output
|
||||
assert "custom-opt is <CustomClass: value=11>" in result.output
|
||||
|
||||
|
||||
def test_parse_custom_type_with_default():
|
||||
result = runner.invoke(app, ["0"])
|
||||
assert "custom_arg is <CustomClass: value=00>" in result.output
|
||||
assert "custom-opt is <CustomClass: value=FooFoo>" in result.output
|
||||
|
||||
|
||||
def test_script():
|
||||
result = subprocess.run(
|
||||
[sys.executable, "-m", "coverage", "run", mod.__file__, "--help"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
encoding="utf-8",
|
||||
)
|
||||
assert "Usage" in result.stdout
|
|
@ -26,7 +26,7 @@ def test_parse_custom_type():
|
|||
def test_parse_custom_type_with_default():
|
||||
result = runner.invoke(app, ["0"])
|
||||
assert "custom_arg is <CustomClass: value=000>" in result.output
|
||||
assert "custom-opt is <CustomClass: value=YYY>" in result.output
|
||||
assert "custom-opt is <CustomClass: value=FooFooFoo>" in result.output
|
||||
|
||||
|
||||
def test_script():
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import subprocess
|
||||
import sys
|
||||
|
||||
import typer
|
||||
from typer.testing import CliRunner
|
||||
|
||||
from docs_src.parameter_types.custom_types import tutorial002_an as mod
|
||||
|
||||
runner = CliRunner()
|
||||
|
||||
app = typer.Typer()
|
||||
app.command()(mod.main)
|
||||
|
||||
|
||||
def test_help():
|
||||
result = runner.invoke(app, ["--help"])
|
||||
assert result.exit_code == 0
|
||||
|
||||
|
||||
def test_parse_custom_type():
|
||||
result = runner.invoke(app, ["0", "--custom-opt", "1"])
|
||||
assert "custom_arg is <CustomClass: value=000>" in result.output
|
||||
assert "custom-opt is <CustomClass: value=111>" in result.output
|
||||
|
||||
|
||||
def test_parse_custom_type_with_default():
|
||||
result = runner.invoke(app, ["0"])
|
||||
assert "custom_arg is <CustomClass: value=000>" in result.output
|
||||
assert "custom-opt is <CustomClass: value=FooFooFoo>" in result.output
|
||||
|
||||
|
||||
def test_script():
|
||||
result = subprocess.run(
|
||||
[sys.executable, "-m", "coverage", "run", mod.__file__, "--help"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
encoding="utf-8",
|
||||
)
|
||||
assert "Usage" in result.stdout
|
|
@ -254,7 +254,7 @@ def Option(
|
|||
@overload
|
||||
def Argument(
|
||||
# Parameter
|
||||
default: Optional[Any],
|
||||
default: Optional[Any] = ...,
|
||||
*,
|
||||
callback: Optional[Callable[..., Any]] = None,
|
||||
metavar: Optional[str] = None,
|
||||
|
@ -308,7 +308,7 @@ def Argument(
|
|||
@overload
|
||||
def Argument(
|
||||
# Parameter
|
||||
default: Optional[Any],
|
||||
default: Optional[Any] = ...,
|
||||
*,
|
||||
callback: Optional[Callable[..., Any]] = None,
|
||||
metavar: Optional[str] = None,
|
||||
|
|
Loading…
Reference in a new issue