diff --git a/docs/tutorial/parameter-types/pydantic-types.md b/docs/tutorial/parameter-types/pydantic-types.md
new file mode 100644
index 0000000000..6092113f57
--- /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!}
+ ```
\ No newline at end of file
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..580625fcc9
--- /dev/null
+++ b/docs_src/parameter_types/pydantic_types/tutorial001.py
@@ -0,0 +1,8 @@
+import typer
+from pydantic import EmailStr
+
+def main(email_arg: EmailStr):
+ typer.echo(f"email_arg: {email_arg}")
+
+if __name__ == "__main__":
+ typer.run(main)
\ No newline at end of file
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..f7faf0ef34
--- /dev/null
+++ b/docs_src/parameter_types/pydantic_types/tutorial001_an.py
@@ -0,0 +1,9 @@
+import typer
+from typing_extensions import Annotated
+from pydantic import EmailStr
+
+def main(email_arg: Annotated[EmailStr, typer.Argument()]):
+ typer.echo(f"email_arg: {email_arg}")
+
+if __name__ == "__main__":
+ typer.run(main)
\ No newline at end of file
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..e06beb11c0
--- /dev/null
+++ b/docs_src/parameter_types/pydantic_types/tutorial002.py
@@ -0,0 +1,8 @@
+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)
\ No newline at end of file
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..4243abb066
--- /dev/null
+++ b/docs_src/parameter_types/pydantic_types/tutorial002_an.py
@@ -0,0 +1,9 @@
+import typer
+from typing_extensions import Annotated
+from pydantic import EmailStr
+
+def main(email_opt: Annotated[EmailStr, typer.Option()]="tiangolo@gmail.com"):
+ typer.echo(f"email_opt: {email_opt}")
+
+if __name__ == "__main__":
+ typer.run(main)
\ No newline at end of file
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..ef92f507b3
--- /dev/null
+++ b/docs_src/parameter_types/pydantic_types/tutorial003.py
@@ -0,0 +1,9 @@
+import typer
+from typing import List
+from pydantic import AnyHttpUrl
+
+def main(urls: List[AnyHttpUrl] = typer.Option([],"--url")):
+ typer.echo(f"urls: {urls}")
+
+if __name__ == "__main__":
+ typer.run(main)
\ No newline at end of file
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..1c28461307
--- /dev/null
+++ b/docs_src/parameter_types/pydantic_types/tutorial003_an.py
@@ -0,0 +1,10 @@
+import typer
+from typing import List
+from typing_extensions import Annotated
+from pydantic import AnyHttpUrl
+
+def main(urls: Annotated[List[AnyHttpUrl], typer.Option("--url", default_factory=list)]):
+ typer.echo(f"urls: {urls}")
+
+if __name__ == "__main__":
+ typer.run(main)
\ No newline at end of file
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..7a82f59afc
--- /dev/null
+++ b/docs_src/parameter_types/pydantic_types/tutorial004.py
@@ -0,0 +1,13 @@
+import typer
+from typing import Tuple
+from pydantic import EmailStr, AnyHttpUrl
+
+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)
\ No newline at end of file
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..5aa822668a
--- /dev/null
+++ b/docs_src/parameter_types/pydantic_types/tutorial004_an.py
@@ -0,0 +1,15 @@
+import typer
+from typing import Tuple
+from typing_extensions import Annotated
+from pydantic import EmailStr, AnyHttpUrl
+
+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)
\ No newline at end of file
diff --git a/mkdocs.yml b/mkdocs.yml
index 8022a19589..cde225de6f 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -61,6 +61,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 d3c6d940ae..ceac6d9114 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -64,10 +64,14 @@ dev = [
"flake8 >=3.8.3,<4.0.0",
"pre-commit >=2.17.0,<3.0.0",
]
+pydantic = [
+ "pydantic[email] >=2.0.0",
+]
all = [
"colorama >=0.4.3,<0.5.0",
"shellingham >=1.3.0,<2.0.0",
"rich >=10.11.0,<14.0.0",
+ "pydantic[email] >=2.0.0",
]
[tool.isort]
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..ef86df6d33
--- /dev/null
+++ b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial001.py
@@ -0,0 +1,36 @@
+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"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
\ No newline at end of file
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..0d1cc548d6
--- /dev/null
+++ b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial001_an.py
@@ -0,0 +1,36 @@
+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"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
\ No newline at end of file
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..2e0a4b3f5f
--- /dev/null
+++ b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial002.py
@@ -0,0 +1,36 @@
+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"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
\ No newline at end of file
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..50cab01a4b
--- /dev/null
+++ b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial002_an.py
@@ -0,0 +1,36 @@
+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"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
\ No newline at end of file
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..536b0dcc65
--- /dev/null
+++ b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial003.py
@@ -0,0 +1,37 @@
+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"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
\ No newline at end of file
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..fff42974c1
--- /dev/null
+++ b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial003_an.py
@@ -0,0 +1,37 @@
+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"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
\ No newline at end of file
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..9d0823d1ee
--- /dev/null
+++ b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial004.py
@@ -0,0 +1,39 @@
+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"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
\ No newline at end of file
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..fe35d353d9
--- /dev/null
+++ b/tests/test_tutorial/test_parameter_types/test_pydantic_types/test_tutorial004_an.py
@@ -0,0 +1,39 @@
+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"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
\ No newline at end of file
diff --git a/typer/main.py b/typer/main.py
index 9de5f5960d..3da4562862 100644
--- a/typer/main.py
+++ b/typer/main.py
@@ -46,6 +46,31 @@
except ImportError: # pragma: nocover
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_):
+ """Create a convertor for a parameter annotated with a pydantic type."""
+
+ @pydantic.validate_call
+ def internal_convertor(value: type_):
+ return value
+
+ def convertor(value: str):
+ 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: nocover
+ 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__"
@@ -608,6 +633,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
@@ -776,6 +803,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
@@ -784,6 +813,9 @@ def lenient_issubclass(
) -> bool:
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,
@@ -817,6 +849,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:
@@ -831,16 +864,12 @@ 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
- ), "List types with complex sub-types are not currently supported"
+ 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
- ), "Tuple types with complex sub-types are not currently supported"
+ 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)
)
@@ -896,7 +925,7 @@ def get_click_param(
required=required,
default=default_value,
callback=get_param_callback(
- callback=parameter_info.callback, convertor=convertor
+ callback=callback, convertor=convertor
),
metavar=parameter_info.metavar,
expose_value=parameter_info.expose_value,
@@ -930,7 +959,7 @@ def get_click_param(
# Parameter
default=default_value,
callback=get_param_callback(
- callback=parameter_info.callback, convertor=convertor
+ callback=callback, convertor=convertor
),
metavar=parameter_info.metavar,
expose_value=parameter_info.expose_value,