diff --git a/docs/tutorial/parameter-types/pydantic-types.md b/docs/tutorial/parameter-types/pydantic-types.md
new file mode 100644
index 0000000000..fe54ef3afb
--- /dev/null
+++ b/docs/tutorial/parameter-types/pydantic-types.md
@@ -0,0 +1,84 @@
+Pydantic types such as [AnyUrl](https://docs.pydantic.dev/latest/api/networks/#pydantic.networks.AnyUrl) or [EmailStr](https://docs.pydantic.dev/latest/api/networks/#pydantic.networks.EmailStr) can be very convenient to describe and validate some parameters.
+
+You can add pydantic from typer's optional dependencies
+
+
+
+```console
+// Pydantic comes with typer[all]
+$ pip install "typer[all]"
+---> 100%
+Successfully installed typer rich pydantic
+
+// Alternatively, you can install Pydantic independently
+$ pip install pydantic
+---> 100%
+Successfully installed pydantic
+```
+
+
+
+
+You can then use them as parameter types.
+
+=== "Python 3.6+ Argument"
+
+ ```Python hl_lines="5"
+ {!> ../docs_src/parameter_types/pydantic_types/tutorial001_an.py!}
+ ```
+
+=== "Python 3.6+ Argument non-Annotated"
+
+ !!! tip
+ Prefer to use the `Annotated` version if possible.
+
+ ```Python hl_lines="4"
+ {!> ../docs_src/parameter_types/pydantic_types/tutorial001.py!}
+ ```
+
+=== "Python 3.6+ Option"
+
+ ```Python hl_lines="5"
+ {!> ../docs_src/parameter_types/pydantic_types/tutorial002_an.py!}
+ ```
+
+=== "Python 3.6+ Option non-Annotated"
+
+ !!! tip
+ Prefer to use the `Annotated` version if possible.
+
+ ```Python hl_lines="4"
+ {!> ../docs_src/parameter_types/pydantic_types/tutorial002.py!}
+ ```
+
+These types are also supported in lists or tuples
+
+=== "Python 3.6+ list"
+
+ ```Python hl_lines="6"
+ {!> ../docs_src/parameter_types/pydantic_types/tutorial003_an.py!}
+ ```
+
+=== "Python 3.6+ list non-Annotated"
+
+ !!! tip
+ Prefer to use the `Annotated` version if possible.
+
+ ```Python hl_lines="5"
+ {!> ../docs_src/parameter_types/pydantic_types/tutorial003.py!}
+ ```
+
+=== "Python 3.6+ tuple"
+
+ ```Python hl_lines="6"
+ {!> ../docs_src/parameter_types/pydantic_types/tutorial004_an.py!}
+ ```
+
+=== "Python 3.6+ tuple non-Annotated"
+
+ !!! tip
+ Prefer to use the `Annotated` version if possible.
+
+ ```Python hl_lines="5"
+ {!> ../docs_src/parameter_types/pydantic_types/tutorial004.py!}
+ ```
diff --git a/docs_src/parameter_types/pydantic_types/__init__.py b/docs_src/parameter_types/pydantic_types/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/docs_src/parameter_types/pydantic_types/tutorial001.py b/docs_src/parameter_types/pydantic_types/tutorial001.py
new file mode 100644
index 0000000000..4aec54161a
--- /dev/null
+++ b/docs_src/parameter_types/pydantic_types/tutorial001.py
@@ -0,0 +1,10 @@
+import typer
+from pydantic import EmailStr
+
+
+def main(email_arg: EmailStr):
+ typer.echo(f"email_arg: {email_arg}")
+
+
+if __name__ == "__main__":
+ typer.run(main)
diff --git a/docs_src/parameter_types/pydantic_types/tutorial001_an.py b/docs_src/parameter_types/pydantic_types/tutorial001_an.py
new file mode 100644
index 0000000000..c92dbce546
--- /dev/null
+++ b/docs_src/parameter_types/pydantic_types/tutorial001_an.py
@@ -0,0 +1,11 @@
+import typer
+from pydantic import EmailStr
+from typing_extensions import Annotated
+
+
+def main(email_arg: Annotated[EmailStr, typer.Argument()]):
+ typer.echo(f"email_arg: {email_arg}")
+
+
+if __name__ == "__main__":
+ typer.run(main)
diff --git a/docs_src/parameter_types/pydantic_types/tutorial002.py b/docs_src/parameter_types/pydantic_types/tutorial002.py
new file mode 100644
index 0000000000..14ef540743
--- /dev/null
+++ b/docs_src/parameter_types/pydantic_types/tutorial002.py
@@ -0,0 +1,10 @@
+import typer
+from pydantic import EmailStr
+
+
+def main(email_opt: EmailStr = typer.Option("tiangolo@gmail.com")):
+ typer.echo(f"email_opt: {email_opt}")
+
+
+if __name__ == "__main__":
+ typer.run(main)
diff --git a/docs_src/parameter_types/pydantic_types/tutorial002_an.py b/docs_src/parameter_types/pydantic_types/tutorial002_an.py
new file mode 100644
index 0000000000..bcf7cf5e15
--- /dev/null
+++ b/docs_src/parameter_types/pydantic_types/tutorial002_an.py
@@ -0,0 +1,11 @@
+import typer
+from pydantic import EmailStr
+from typing_extensions import Annotated
+
+
+def main(email_opt: Annotated[EmailStr, typer.Option()] = "tiangolo@gmail.com"):
+ typer.echo(f"email_opt: {email_opt}")
+
+
+if __name__ == "__main__":
+ typer.run(main)
diff --git a/docs_src/parameter_types/pydantic_types/tutorial003.py b/docs_src/parameter_types/pydantic_types/tutorial003.py
new file mode 100644
index 0000000000..c1b13964be
--- /dev/null
+++ b/docs_src/parameter_types/pydantic_types/tutorial003.py
@@ -0,0 +1,12 @@
+from typing import List
+
+import typer
+from pydantic import AnyHttpUrl
+
+
+def main(urls: List[AnyHttpUrl] = typer.Option([], "--url")):
+ typer.echo(f"urls: {urls}")
+
+
+if __name__ == "__main__":
+ typer.run(main)
diff --git a/docs_src/parameter_types/pydantic_types/tutorial003_an.py b/docs_src/parameter_types/pydantic_types/tutorial003_an.py
new file mode 100644
index 0000000000..61b816243e
--- /dev/null
+++ b/docs_src/parameter_types/pydantic_types/tutorial003_an.py
@@ -0,0 +1,15 @@
+from typing import List
+
+import typer
+from pydantic import AnyHttpUrl
+from typing_extensions import Annotated
+
+
+def main(
+ urls: Annotated[List[AnyHttpUrl], typer.Option("--url", default_factory=list)],
+):
+ typer.echo(f"urls: {urls}")
+
+
+if __name__ == "__main__":
+ typer.run(main)
diff --git a/docs_src/parameter_types/pydantic_types/tutorial004.py b/docs_src/parameter_types/pydantic_types/tutorial004.py
new file mode 100644
index 0000000000..66b7b71a25
--- /dev/null
+++ b/docs_src/parameter_types/pydantic_types/tutorial004.py
@@ -0,0 +1,20 @@
+from typing import Tuple
+
+import typer
+from pydantic import AnyHttpUrl, EmailStr
+
+
+def main(
+ user: Tuple[str, int, EmailStr, AnyHttpUrl] = typer.Option(
+ ..., help="User name, age, email and social media URL"
+ ),
+):
+ name, age, email, url = user
+ typer.echo(f"name: {name}")
+ typer.echo(f"age: {age}")
+ typer.echo(f"email: {email}")
+ typer.echo(f"url: {url}")
+
+
+if __name__ == "__main__":
+ typer.run(main)
diff --git a/docs_src/parameter_types/pydantic_types/tutorial004_an.py b/docs_src/parameter_types/pydantic_types/tutorial004_an.py
new file mode 100644
index 0000000000..9fa0ee5494
--- /dev/null
+++ b/docs_src/parameter_types/pydantic_types/tutorial004_an.py
@@ -0,0 +1,22 @@
+from typing import Tuple
+
+import typer
+from pydantic import AnyHttpUrl, EmailStr
+from typing_extensions import Annotated
+
+
+def main(
+ user: Annotated[
+ Tuple[str, int, EmailStr, AnyHttpUrl],
+ typer.Option(help="User name, age, email and social media URL"),
+ ],
+):
+ name, age, email, url = user
+ typer.echo(f"name: {name}")
+ typer.echo(f"age: {age}")
+ typer.echo(f"email: {email}")
+ typer.echo(f"url: {url}")
+
+
+if __name__ == "__main__":
+ typer.run(main)
diff --git a/mkdocs.yml b/mkdocs.yml
index 77024d83bb..5a0b02bbb5 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -66,6 +66,7 @@ nav:
- Path: tutorial/parameter-types/path.md
- File: tutorial/parameter-types/file.md
- Custom Types: tutorial/parameter-types/custom-types.md
+ - Pydantic Types: tutorial/parameter-types/pydantic-types.md
- SubCommands - Command Groups:
- SubCommands - Command Groups - Intro: tutorial/subcommands/index.md
- Add Typer: tutorial/subcommands/add-typer.md
diff --git a/pyproject.toml b/pyproject.toml
index c9e793e1ed..32a52a9a8d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -44,6 +44,7 @@ homepage = "https://github.com/tiangolo/typer"
standard = [
"shellingham >=1.3.0",
"rich >=10.11.0",
+ "pydantic[email] >=2.0.0",
]
[tool.pdm]
diff --git a/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial001.py b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial001.py
new file mode 100644
index 0000000000..e8d226088b
--- /dev/null
+++ b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial001.py
@@ -0,0 +1,38 @@
+import subprocess
+import sys
+
+import typer
+from typer.testing import CliRunner
+
+from docs_src.parameter_types.pydantic_types import tutorial001 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_email_arg():
+ result = runner.invoke(app, ["tiangolo@gmail.com"])
+ assert result.exit_code == 0
+ assert "email_arg: tiangolo@gmail.com" in result.output
+
+
+def test_email_arg_invalid():
+ result = runner.invoke(app, ["invalid"])
+ assert result.exit_code != 0
+ assert "value is not a valid email address" in result.output
+
+
+def test_script():
+ result = subprocess.run(
+ [sys.executable, "-m", "coverage", "run", mod.__file__, "--help"],
+ capture_output=True,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
diff --git a/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial001_an.py b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial001_an.py
new file mode 100644
index 0000000000..167c1ce3a8
--- /dev/null
+++ b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial001_an.py
@@ -0,0 +1,38 @@
+import subprocess
+import sys
+
+import typer
+from typer.testing import CliRunner
+
+from docs_src.parameter_types.pydantic_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_email_arg():
+ result = runner.invoke(app, ["tiangolo@gmail.com"])
+ assert result.exit_code == 0
+ assert "email_arg: tiangolo@gmail.com" in result.output
+
+
+def test_email_arg_invalid():
+ result = runner.invoke(app, ["invalid"])
+ assert result.exit_code != 0
+ assert "value is not a valid email address" in result.output
+
+
+def test_script():
+ result = subprocess.run(
+ [sys.executable, "-m", "coverage", "run", mod.__file__, "--help"],
+ capture_output=True,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
diff --git a/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial002.py b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial002.py
new file mode 100644
index 0000000000..265e1d3191
--- /dev/null
+++ b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial002.py
@@ -0,0 +1,38 @@
+import subprocess
+import sys
+
+import typer
+from typer.testing import CliRunner
+
+from docs_src.parameter_types.pydantic_types import tutorial002 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_email_opt():
+ result = runner.invoke(app, ["--email-opt", "tiangolo@gmail.com"])
+ assert result.exit_code == 0
+ assert "email_opt: tiangolo@gmail.com" in result.output
+
+
+def test_email_opt_invalid():
+ result = runner.invoke(app, ["--email-opt", "invalid"])
+ assert result.exit_code != 0
+ assert "value is not a valid email address" in result.output
+
+
+def test_script():
+ result = subprocess.run(
+ [sys.executable, "-m", "coverage", "run", mod.__file__, "--help"],
+ capture_output=True,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
diff --git a/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial002_an.py b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial002_an.py
new file mode 100644
index 0000000000..1d0475d009
--- /dev/null
+++ b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial002_an.py
@@ -0,0 +1,38 @@
+import subprocess
+import sys
+
+import typer
+from typer.testing import CliRunner
+
+from docs_src.parameter_types.pydantic_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_email_opt():
+ result = runner.invoke(app, ["--email-opt", "tiangolo@gmail.com"])
+ assert result.exit_code == 0
+ assert "email_opt: tiangolo@gmail.com" in result.output
+
+
+def test_email_opt_invalid():
+ result = runner.invoke(app, ["--email-opt", "invalid"])
+ assert result.exit_code != 0
+ assert "value is not a valid email address" in result.output
+
+
+def test_script():
+ result = subprocess.run(
+ [sys.executable, "-m", "coverage", "run", mod.__file__, "--help"],
+ capture_output=True,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
diff --git a/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial003.py b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial003.py
new file mode 100644
index 0000000000..b9a9018e04
--- /dev/null
+++ b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial003.py
@@ -0,0 +1,41 @@
+import subprocess
+import sys
+
+import typer
+from typer.testing import CliRunner
+
+from docs_src.parameter_types.pydantic_types import tutorial003 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_url_list():
+ result = runner.invoke(
+ app, ["--url", "https://example.com", "--url", "https://example.org"]
+ )
+ assert result.exit_code == 0
+ assert "https://example.com" in result.output
+ assert "https://example.org" in result.output
+
+
+def test_url_invalid():
+ result = runner.invoke(app, ["--url", "invalid", "--url", "https://example.org"])
+ assert result.exit_code != 0
+ assert "Input should be a valid URL" in result.output
+
+
+def test_script():
+ result = subprocess.run(
+ [sys.executable, "-m", "coverage", "run", mod.__file__, "--help"],
+ capture_output=True,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
diff --git a/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial003_an.py b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial003_an.py
new file mode 100644
index 0000000000..487fffc55b
--- /dev/null
+++ b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial003_an.py
@@ -0,0 +1,41 @@
+import subprocess
+import sys
+
+import typer
+from typer.testing import CliRunner
+
+from docs_src.parameter_types.pydantic_types import tutorial003_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_url_list():
+ result = runner.invoke(
+ app, ["--url", "https://example.com", "--url", "https://example.org"]
+ )
+ assert result.exit_code == 0
+ assert "https://example.com" in result.output
+ assert "https://example.org" in result.output
+
+
+def test_url_invalid():
+ result = runner.invoke(app, ["--url", "invalid", "--url", "https://example.org"])
+ assert result.exit_code != 0
+ assert "Input should be a valid URL" in result.output
+
+
+def test_script():
+ result = subprocess.run(
+ [sys.executable, "-m", "coverage", "run", mod.__file__, "--help"],
+ capture_output=True,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
diff --git a/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial004.py b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial004.py
new file mode 100644
index 0000000000..e51c2b5b89
--- /dev/null
+++ b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial004.py
@@ -0,0 +1,45 @@
+import subprocess
+import sys
+
+import typer
+from typer.testing import CliRunner
+
+from docs_src.parameter_types.pydantic_types import tutorial004 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_tuple():
+ result = runner.invoke(
+ app, ["--user", "Camila", "23", "camila@example.org", "https://example.com"]
+ )
+ assert result.exit_code == 0
+ assert "name: Camila" in result.output
+ assert "age: 23" in result.output
+ assert "email: camila@example.org" in result.output
+ assert "url: https://example.com" in result.output
+
+
+def test_tuple_invalid():
+ result = runner.invoke(
+ app, ["--user", "Camila", "23", "invalid", "https://example.com"]
+ )
+ assert result.exit_code != 0
+ assert "value is not a valid email address" in result.output
+
+
+def test_script():
+ result = subprocess.run(
+ [sys.executable, "-m", "coverage", "run", mod.__file__, "--help"],
+ capture_output=True,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
diff --git a/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial004_an.py b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial004_an.py
new file mode 100644
index 0000000000..dde6671976
--- /dev/null
+++ b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial004_an.py
@@ -0,0 +1,45 @@
+import subprocess
+import sys
+
+import typer
+from typer.testing import CliRunner
+
+from docs_src.parameter_types.pydantic_types import tutorial004_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_tuple():
+ result = runner.invoke(
+ app, ["--user", "Camila", "23", "camila@example.org", "https://example.com"]
+ )
+ assert result.exit_code == 0
+ assert "name: Camila" in result.output
+ assert "age: 23" in result.output
+ assert "email: camila@example.org" in result.output
+ assert "url: https://example.com" in result.output
+
+
+def test_tuple_invalid():
+ result = runner.invoke(
+ app, ["--user", "Camila", "23", "invalid", "https://example.com"]
+ )
+ assert result.exit_code != 0
+ assert "value is not a valid email address" in result.output
+
+
+def test_script():
+ result = subprocess.run(
+ [sys.executable, "-m", "coverage", "run", mod.__file__, "--help"],
+ capture_output=True,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
diff --git a/typer/completion.py b/typer/completion.py
index 1220a1b545..90c4984b30 100644
--- a/typer/completion.py
+++ b/typer/completion.py
@@ -135,7 +135,7 @@ def shell_complete(
click.echo(f"Shell {shell} not supported.", err=True)
return 1
- comp = comp_cls(cli, ctx_args, prog_name, complete_var)
+ comp = comp_cls(cli, dict(ctx_args), prog_name, complete_var)
if instruction == "source":
click.echo(comp.source())
diff --git a/typer/main.py b/typer/main.py
index 9db26975ca..a6ad970d69 100644
--- a/typer/main.py
+++ b/typer/main.py
@@ -12,6 +12,7 @@
from uuid import UUID
import click
+from typing_extensions import TypeAlias
from .completion import get_completion_inspect_parameters
from .core import MarkupMode, TyperArgument, TyperCommand, TyperGroup, TyperOption
@@ -46,6 +47,40 @@
except ImportError: # pragma: no cover
rich = None # type: ignore
+try:
+ import pydantic
+
+ def is_pydantic_type(type_: Any) -> bool:
+ return type_.__module__.startswith("pydantic") and not lenient_issubclass(
+ type_, pydantic.BaseModel
+ )
+
+ def pydantic_convertor(type_: type) -> Callable[[str], Any]:
+ """Create a convertor for a parameter annotated with a pydantic type."""
+ T: TypeAlias = type_ # type: ignore[valid-type]
+
+ @pydantic.validate_call
+ def internal_convertor(value: T) -> T:
+ return value
+
+ def convertor(value: str) -> T:
+ try:
+ return internal_convertor(value)
+ except pydantic.ValidationError as e:
+ error_message = e.errors(
+ include_context=False, include_input=False, include_url=False
+ )[0]["msg"]
+ raise click.BadParameter(error_message) from e
+
+ return convertor
+
+except ImportError: # pragma: no cover
+ pydantic = None # type: ignore
+
+ def is_pydantic_type(type_: Any) -> bool:
+ return False
+
+
_original_except_hook = sys.excepthook
_typer_developer_exception_attr_name = "__typer_developer_exception__"
@@ -610,6 +645,8 @@ def determine_type_convertor(type_: Any) -> Optional[Callable[[Any], Any]]:
convertor = param_path_convertor
if lenient_issubclass(type_, Enum):
convertor = generate_enum_convertor(type_)
+ if is_pydantic_type(type_):
+ convertor = pydantic_convertor(type_)
return convertor
@@ -785,6 +822,8 @@ def get_click_type(
[item.value for item in annotation],
case_sensitive=parameter_info.case_sensitive,
)
+ elif is_pydantic_type(annotation):
+ return click.STRING
raise RuntimeError(f"Type not yet supported: {annotation}") # pragma: no cover
@@ -794,6 +833,13 @@ def lenient_issubclass(
return isinstance(cls, type) and issubclass(cls, class_or_tuple)
+def is_complex_subtype(type_: Any) -> bool:
+ # For pydantic types, such as `AnyUrl`, there's an extra `Annotated` layer that we don't need to treat as complex
+ return getattr(type_, "__origin__", None) is not None and not is_pydantic_type(
+ type_
+ )
+
+
def get_click_param(
param: ParamMeta,
) -> Tuple[Union[click.Argument, click.Option], Any]:
@@ -826,6 +872,7 @@ def get_click_param(
parameter_type: Any = None
is_flag = None
origin = getattr(main_type, "__origin__", None)
+ callback = parameter_info.callback
if origin is not None:
# Handle Optional[SomeType]
if origin is Union:
@@ -840,15 +887,15 @@ def get_click_param(
# Handle Tuples and Lists
if lenient_issubclass(origin, List):
main_type = main_type.__args__[0]
- assert not getattr(
- main_type, "__origin__", None
+ assert not is_complex_subtype(
+ main_type
), "List types with complex sub-types are not currently supported"
is_list = True
elif lenient_issubclass(origin, Tuple): # type: ignore
types = []
for type_ in main_type.__args__:
- assert not getattr(
- type_, "__origin__", None
+ assert not is_complex_subtype(
+ type_
), "Tuple types with complex sub-types are not currently supported"
types.append(
get_click_type(annotation=type_, parameter_info=parameter_info)
@@ -906,9 +953,7 @@ def get_click_param(
# Parameter
required=required,
default=default_value,
- callback=get_param_callback(
- callback=parameter_info.callback, convertor=convertor
- ),
+ callback=get_param_callback(callback=callback, convertor=convertor),
metavar=parameter_info.metavar,
expose_value=parameter_info.expose_value,
is_eager=parameter_info.is_eager,
@@ -940,9 +985,7 @@ def get_click_param(
hidden=parameter_info.hidden,
# Parameter
default=default_value,
- callback=get_param_callback(
- callback=parameter_info.callback, convertor=convertor
- ),
+ callback=get_param_callback(callback=callback, convertor=convertor),
metavar=parameter_info.metavar,
expose_value=parameter_info.expose_value,
is_eager=parameter_info.is_eager,