From 821b06dc594d8b96eb0b51750877be1d39611cca Mon Sep 17 00:00:00 2001 From: Kinuax Date: Wed, 15 May 2024 11:48:36 +0200 Subject: [PATCH 1/9] Drop support for Python 3.7 --- .github/workflows/test.yml | 2 +- docs/tutorial/arguments/help.md | 32 +-- docs/tutorial/options/callback-and-context.md | 16 +- docs/tutorial/options/help.md | 16 +- docs/tutorial/options/name.md | 20 +- docs/tutorial/options/password.md | 8 +- docs/tutorial/options/prompt.md | 12 +- docs/tutorial/options/required.md | 6 +- docs/tutorial/options/version.md | 12 +- docs/tutorial/testing.md | 4 +- pyproject.toml | 3 +- typer/_typing.py | 220 +++++------------- typer/core.py | 6 +- typer/rich_utils.py | 7 +- 14 files changed, 128 insertions(+), 236 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cab9076193..c48502f30b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,7 @@ jobs: python-version: [ "3.11" ] include: - os: ubuntu-latest - python-version: "3.7" + python-version: "3.8" - os: macos-latest python-version: "3.8" - os: windows-latest diff --git a/docs/tutorial/arguments/help.md b/docs/tutorial/arguments/help.md index bbbb5e0eff..e514c568da 100644 --- a/docs/tutorial/arguments/help.md +++ b/docs/tutorial/arguments/help.md @@ -12,13 +12,13 @@ Now that you also know how to use `typer.Argument()`, let's use it to add docume You can use the `help` parameter to add a help text for a *CLI argument*: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="5" {!> ../docs_src/arguments/help/tutorial001_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -50,13 +50,13 @@ Options: And of course, you can also combine that `help` with the docstring: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="5-8" {!> ../docs_src/arguments/help/tutorial002_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -90,13 +90,13 @@ Options: If you have a *CLI argument* with a default value, like `"World"`: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="5" {!> ../docs_src/arguments/help/tutorial003_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -128,13 +128,13 @@ Options: But you can disable that if you want to, with `show_default=False`: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="7" {!> ../docs_src/arguments/help/tutorial004_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -173,13 +173,13 @@ Options: You can use the same `show_default` to pass a custom string (instead of a `bool`) to customize the default value to be shown in the help text: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="9" {!> ../docs_src/arguments/help/tutorial005_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -231,13 +231,13 @@ But you can customize it with the `metavar` parameter for `typer.Argument()`. For example, let's say you don't want to have the default of `NAME`, you want to have `username`, in lowercase, and you really want ✨ emojis ✨ everywhere: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="5" {!> ../docs_src/arguments/help/tutorial006_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -270,13 +270,13 @@ You might want to show the help information for *CLI arguments* in different pan If you have installed Rich as described in the docs for [Printing and Colors](../printing.md){.internal-link target=_blank}, you can set the `rich_help_panel` parameter to the name of the panel where you want this *CLI argument* to be shown: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="8 12" {!> ../docs_src/arguments/help/tutorial007_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -326,13 +326,13 @@ If you want, you can make a *CLI argument* **not** show up in the `Arguments` se You will probably not want to do this normally, but it's possible: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="5" {!> ../docs_src/arguments/help/tutorial008_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. diff --git a/docs/tutorial/options/callback-and-context.md b/docs/tutorial/options/callback-and-context.md index 4179eeff94..653180934a 100644 --- a/docs/tutorial/options/callback-and-context.md +++ b/docs/tutorial/options/callback-and-context.md @@ -6,13 +6,13 @@ In those cases you can use a *CLI parameter* callback function. For example, you could do some validation before the rest of the code is executed. -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="7-10 13" {!> ../docs_src/options/callback/tutorial001_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -105,13 +105,13 @@ But the main **important point** is that it is all based on values printed by yo Let's say that when the callback is running, we want to show a message saying that it's validating the name: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="8" {!> ../docs_src/options/callback/tutorial002_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -153,13 +153,13 @@ But you can access the context by declaring a function parameter of type `typer. The "context" has some additional data about the current execution of your program: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="7-9" {!> ../docs_src/options/callback/tutorial003_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -198,13 +198,13 @@ Hello Camila The same way you can access the `typer.Context` by declaring a function parameter with its value, you can declare another function parameter with type `typer.CallbackParam` to get the specific Click `Parameter` object. -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="7 10" {!> ../docs_src/options/callback/tutorial004_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. diff --git a/docs/tutorial/options/help.md b/docs/tutorial/options/help.md index 772daaf1bb..12112fd1b3 100644 --- a/docs/tutorial/options/help.md +++ b/docs/tutorial/options/help.md @@ -2,13 +2,13 @@ You already saw how to add a help text for *CLI arguments* with the `help` param Let's now do the same for *CLI options*: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="7-8" {!> ../docs_src/options/help/tutorial001_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -67,13 +67,13 @@ The same as with *CLI arguments*, you can put the help for some *CLI options* in If you have installed Rich as described in the docs for [Printing and Colors](../printing.md){.internal-link target=_blank}, you can set the `rich_help_panel` parameter to the name of the panel you want for each *CLI option*: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="11 17" {!> ../docs_src/options/help/tutorial002_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -126,13 +126,13 @@ If you are in a hurry you can jump there, but otherwise, it would be better to c You can tell Typer to not show the default value in the help text with `show_default=False`: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="5" {!> ../docs_src/options/help/tutorial003_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -173,13 +173,13 @@ Options: You can use the same `show_default` to pass a custom string (instead of a `bool`) to customize the default value to be shown in the help text: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="7" {!> ../docs_src/options/help/tutorial004_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. diff --git a/docs/tutorial/options/name.md b/docs/tutorial/options/name.md index 2392edcaa5..2803add2e1 100644 --- a/docs/tutorial/options/name.md +++ b/docs/tutorial/options/name.md @@ -26,7 +26,7 @@ Let's say the function parameter name is `user_name` as above, but you want the You can pass the *CLI option* name that you want to have in the following positional argument passed to `typer.Option()`: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="5" {!> ../docs_src/options/name/tutorial001_an.py!} @@ -34,7 +34,7 @@ You can pass the *CLI option* name that you want to have in the following positi Here you are passing the string `"--name"` as the first positional argument to `typer.Option()`. -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -194,13 +194,13 @@ You can overwrite the *CLI option* name to use as in the previous example, but y For example, extending the previous example, let's add a *CLI option* short name `-n`: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="5" {!> ../docs_src/options/name/tutorial002_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -238,13 +238,13 @@ Hello Camila If you only declare a short name like `-n` then that will be the only *CLI option* name. And neither `--name` nor `--user-name` will be available. -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="5" {!> ../docs_src/options/name/tutorial003_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -279,13 +279,13 @@ Hello Camila Continuing with the example above, as **Typer** allows you to declare a *CLI option* as having only a short name, if you want to have the default long name plus a short name, you have to declare both explicitly: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="5" {!> ../docs_src/options/name/tutorial004_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -326,13 +326,13 @@ You can create multiple short names and use them together. You don't have to do anything special for it to work (apart from declaring those short versions): -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="6-7" {!> ../docs_src/options/name/tutorial005_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. diff --git a/docs/tutorial/options/password.md b/docs/tutorial/options/password.md index abe33e5411..febf73d169 100644 --- a/docs/tutorial/options/password.md +++ b/docs/tutorial/options/password.md @@ -1,12 +1,12 @@ Apart from having a prompt, you can make a *CLI option* have a `confirmation_prompt=True`: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="7" {!> ../docs_src/options/password/tutorial001_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -41,13 +41,13 @@ You can achieve the same using `hide_input=True`. And if you combine it with `confirmation_prompt=True` you can easily receive a password with double confirmation: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="8" {!> ../docs_src/options/password/tutorial002_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. diff --git a/docs/tutorial/options/prompt.md b/docs/tutorial/options/prompt.md index 9f7472eac4..f144a0c35e 100644 --- a/docs/tutorial/options/prompt.md +++ b/docs/tutorial/options/prompt.md @@ -1,12 +1,12 @@ It's also possible to, instead of just showing an error, ask for the missing value with `prompt=True`: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="5" {!> ../docs_src/options/prompt/tutorial001_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -35,13 +35,13 @@ Hello Camila Gutiérrez You can also set a custom prompt, passing the string that you want to use instead of just `True`: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="7" {!> ../docs_src/options/prompt/tutorial002_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -74,13 +74,13 @@ You can do it passing the parameter `confirmation_prompt=True`. Let's say it's a CLI app to delete a project: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="6" {!> ../docs_src/options/prompt/tutorial003_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. diff --git a/docs/tutorial/options/required.md b/docs/tutorial/options/required.md index 311fbd93ae..4594438ee5 100644 --- a/docs/tutorial/options/required.md +++ b/docs/tutorial/options/required.md @@ -11,7 +11,7 @@ To make a *CLI option* required, you can put `typer.Option()` inside of `Annotat Let's make `--lastname` a required *CLI option*: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="5" {!> ../docs_src/options/required/tutorial001_an.py!} @@ -19,7 +19,7 @@ Let's make `--lastname` a required *CLI option*: The same way as with `typer.Argument()`, the old style of using the function parameter default value is also supported, in that case you would just not pass anything to the `default` parameter. -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" ```Python hl_lines="4" {!> ../docs_src/options/required/tutorial001.py!} @@ -27,7 +27,7 @@ The same way as with `typer.Argument()`, the old style of using the function par Or you can explictily pass `...` to `typer.Option(default=...)`: -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" ```Python hl_lines="4" {!> ../docs_src/options/required/tutorial002.py!} diff --git a/docs/tutorial/options/version.md b/docs/tutorial/options/version.md index 497a30262c..f1b8a4e76c 100644 --- a/docs/tutorial/options/version.md +++ b/docs/tutorial/options/version.md @@ -6,13 +6,13 @@ It would show the version of your CLI program and then it would terminate it. Ev Let's see a first version of how it could look like: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="9-12 17-19" {!> ../docs_src/options/version/tutorial001_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -65,13 +65,13 @@ Awesome CLI Version: 0.1.0 But now let's say that the `--name` *CLI option* that we declared before `--version` is required, and it has a callback that could exit the program: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="15-17 22-24" {!> ../docs_src/options/version/tutorial002_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. @@ -108,13 +108,13 @@ For those cases, we can mark a *CLI parameter* (a *CLI option* or *CLI argument* That will tell **Typer** (actually Click) that it should process this *CLI parameter* before the others: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="23-26" {!> ../docs_src/options/version/tutorial003_an.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. diff --git a/docs/tutorial/testing.md b/docs/tutorial/testing.md index 2df3460ba4..9c88e41981 100644 --- a/docs/tutorial/testing.md +++ b/docs/tutorial/testing.md @@ -103,13 +103,13 @@ test_main.py . If you have a CLI with prompts, like: -=== "Python 3.7+" +=== "Python 3.8+" ```Python hl_lines="8" {!> ../docs_src/testing/app02_an/main.py!} ``` -=== "Python 3.7+ non-Annotated" +=== "Python 3.8+ non-Annotated" !!! tip Prefer to use the `Annotated` version if possible. diff --git a/pyproject.toml b/pyproject.toml index 24e25e94e1..47c4f5d02d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ description = "Typer, build great CLIs. Easy to code. Based on Python type hints authors = [ {name = "Sebastián Ramírez", email = "tiangolo@gmail.com"}, ] -requires-python = ">=3.7" +requires-python = ">=3.8" classifiers = [ "Intended Audience :: Information Technology", "Intended Audience :: System Administrators", @@ -24,7 +24,6 @@ classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", diff --git a/typer/_typing.py b/typer/_typing.py index b1d2483da0..c9bcf18565 100644 --- a/typer/_typing.py +++ b/typer/_typing.py @@ -1,15 +1,19 @@ # Copied from pydantic 1.9.2 (the latest version to support python 3.6.) # https://github.com/pydantic/pydantic/blob/v1.9.2/pydantic/typing.py +# Edited after dropping support for python 3.7 # mypy: ignore-errors import sys +from collections.abc import Callable as Callable from os import PathLike from typing import ( TYPE_CHECKING, AbstractSet, Any, + Callable as TypingCallable, ClassVar, Dict, + ForwardRef, Generator, List, Mapping, @@ -22,6 +26,8 @@ Union, _eval_type, cast, + get_args as _typing_get_args, + get_origin as _typing_get_origin, get_type_hints, ) @@ -45,28 +51,7 @@ TypesUnionType = () -if sys.version_info < (3, 7): - if TYPE_CHECKING: - - class ForwardRef: - def __init__(self, arg: Any): - pass - - def _eval_type(self, globalns: Any, localns: Any) -> Any: - pass - - else: - from typing import _ForwardRef as ForwardRef -else: - from typing import ForwardRef - - -if sys.version_info < (3, 7): - - def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any: - return type_._eval_type(globalns, localns) - -elif sys.version_info < (3, 9): +if sys.version_info[:2] == (3, 8): def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any: return type_._evaluate(globalns, localns) @@ -79,7 +64,7 @@ def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any: return cast(Any, type_)._evaluate(globalns, localns, set()) -if sys.version_info < (3, 9): +if sys.version_info[:2] == (3, 8): # Ensure we always get all the whole `Annotated` hint, not just the annotated type. # For 3.6 to 3.8, `get_type_hints` doesn't recognize `typing_extensions.Annotated`, # so it already returns the full annotation @@ -91,17 +76,8 @@ def get_all_type_hints(obj: Any, globalns: Any = None, localns: Any = None) -> A return get_type_hints(obj, globalns, localns, include_extras=True) -if sys.version_info < (3, 7): - from typing import Callable as Callable - - AnyCallable = Callable[..., Any] - NoArgAnyCallable = Callable[[], Any] -else: - from collections.abc import Callable as Callable - from typing import Callable as TypingCallable - - AnyCallable = TypingCallable[..., Any] - NoArgAnyCallable = TypingCallable[[], Any] +AnyCallable = TypingCallable[..., Any] +NoArgAnyCallable = TypingCallable[[], Any] # Annotated[...] is implemented by returning an instance of one of these classes, depending on @@ -109,97 +85,49 @@ def get_all_type_hints(obj: Any, globalns: Any = None, localns: Any = None) -> A AnnotatedTypeNames = {"AnnotatedMeta", "_AnnotatedAlias"} -if sys.version_info < (3, 8): - - def get_origin(t: Type[Any]) -> Optional[Type[Any]]: - if type(t).__name__ in AnnotatedTypeNames: - return cast( - Type[Any], Annotated - ) # mypy complains about _SpecialForm in py3.6 - return getattr(t, "__origin__", None) - -else: - from typing import get_origin as _typing_get_origin - - def get_origin(tp: Type[Any]) -> Optional[Type[Any]]: - """ - We can't directly use `typing.get_origin` since we need a fallback to support - custom generic classes like `ConstrainedList` - It should be useless once https://github.com/cython/cython/issues/3537 is - solved and https://github.com/samuelcolvin/pydantic/pull/1753 is merged. - """ - if type(tp).__name__ in AnnotatedTypeNames: - return cast(Type[Any], Annotated) # mypy complains about _SpecialForm - return _typing_get_origin(tp) or getattr(tp, "__origin__", None) - - -if sys.version_info < (3, 7): # noqa: C901 (ignore complexity) - - def get_args(t: Type[Any]) -> Tuple[Any, ...]: - """Simplest get_args compatibility layer possible. - - The Python 3.6 typing module does not have `_GenericAlias` so - this won't work for everything. In particular this will not - support the `generics` module (we don't support generic models in - python 3.6). - - """ - if type(t).__name__ in AnnotatedTypeNames: - return t.__args__ + t.__metadata__ - return getattr(t, "__args__", ()) - -elif sys.version_info < (3, 8): # noqa: C901 - from typing import _GenericAlias - - def get_args(t: Type[Any]) -> Tuple[Any, ...]: - """Compatibility version of get_args for python 3.7. - - Mostly compatible with the python 3.8 `typing` module version - and able to handle almost all use cases. - """ - if type(t).__name__ in AnnotatedTypeNames: - return t.__args__ + t.__metadata__ - if isinstance(t, _GenericAlias): - res = t.__args__ - if t.__origin__ is Callable and res and res[0] is not Ellipsis: - res = (list(res[:-1]), res[-1]) - return res - return getattr(t, "__args__", ()) - -else: - from typing import get_args as _typing_get_args - - def _generic_get_args(tp: Type[Any]) -> Tuple[Any, ...]: - """ - In python 3.9, `typing.Dict`, `typing.List`, ... - do have an empty `__args__` by default (instead of the generic ~T for example). - In order to still support `Dict` for example and consider it as `Dict[Any, Any]`, - we retrieve the `_nparams` value that tells us how many parameters it needs. - """ - if hasattr(tp, "_nparams"): - return (Any,) * tp._nparams - return () +def get_origin(tp: Type[Any]) -> Optional[Type[Any]]: + """ + We can't directly use `typing.get_origin` since we need a fallback to support + custom generic classes like `ConstrainedList` + It should be useless once https://github.com/cython/cython/issues/3537 is + solved and https://github.com/samuelcolvin/pydantic/pull/1753 is merged. + """ + if type(tp).__name__ in AnnotatedTypeNames: + return cast(Type[Any], Annotated) # mypy complains about _SpecialForm + return _typing_get_origin(tp) or getattr(tp, "__origin__", None) - def get_args(tp: Type[Any]) -> Tuple[Any, ...]: - """Get type arguments with all substitutions performed. - For unions, basic simplifications used by Union constructor are performed. - Examples:: - get_args(Dict[str, int]) == (str, int) - get_args(int) == () - get_args(Union[int, Union[T, int], str][int]) == (int, str) - get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) - get_args(Callable[[], T][int]) == ([], int) - """ - if type(tp).__name__ in AnnotatedTypeNames: - return tp.__args__ + tp.__metadata__ - # the fallback is needed for the same reasons as `get_origin` (see above) - return ( - _typing_get_args(tp) or getattr(tp, "__args__", ()) or _generic_get_args(tp) - ) +def _generic_get_args(tp: Type[Any]) -> Tuple[Any, ...]: + """ + In python 3.9, `typing.Dict`, `typing.List`, ... + do have an empty `__args__` by default (instead of the generic ~T for example). + In order to still support `Dict` for example and consider it as `Dict[Any, Any]`, + we retrieve the `_nparams` value that tells us how many parameters it needs. + """ + if hasattr(tp, "_nparams"): + return (Any,) * tp._nparams + return () + +def get_args(tp: Type[Any]) -> Tuple[Any, ...]: + """Get type arguments with all substitutions performed. + + For unions, basic simplifications used by Union constructor are performed. + Examples:: + get_args(Dict[str, int]) == (str, int) + get_args(int) == () + get_args(Union[int, Union[T, int], str][int]) == (int, str) + get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) + get_args(Callable[[], T][int]) == ([], int) + """ + if type(tp).__name__ in AnnotatedTypeNames: + return tp.__args__ + tp.__metadata__ + # the fallback is needed for the same reasons as `get_origin` (see above) + return ( + _typing_get_args(tp) or getattr(tp, "__args__", ()) or _generic_get_args(tp) + ) -if sys.version_info < (3, 9): +if sys.version_info[:2] == (3, 8): def convert_generics(tp: Type[Any]) -> Type[Any]: """Python 3.9 and older only supports generics from `typing` module. @@ -275,7 +203,7 @@ def is_union(tp: Optional[Type[Any]]) -> bool: WithArgsTypes = (typing._GenericAlias, types.GenericAlias, types.UnionType) -if sys.version_info < (3, 9): +if sys.version_info[:2] == (3, 8): StrPath = Union[str, PathLike] else: StrPath = Union[str, PathLike] @@ -320,8 +248,6 @@ def is_union(tp: Optional[Type[Any]]) -> bool: "is_new_type", "new_type_supertype", "is_classvar", - "update_field_forward_refs", - "update_model_forward_refs", "TupleGenerator", "DictStrAny", "DictAny", @@ -351,17 +277,7 @@ def is_union(tp: Optional[Type[Any]]) -> bool: NONE_TYPES: Tuple[Any, Any, Any] = (None, NoneType, Literal[None]) -if sys.version_info < (3, 8): - # Even though this implementation is slower, we need it for python 3.6/3.7: - # In python 3.6/3.7 "Literal" is not a builtin type and uses a different - # mechanism. - # for this reason `Literal[None] is Literal[None]` evaluates to `False`, - # breaking the faster implementation used for the other python versions. - - def is_none_type(type_: Any) -> bool: - return type_ in NONE_TYPES - -elif sys.version_info[:2] == (3, 8): +if sys.version_info[:2] == (3, 8): # We can use the fast implementation for 3.8 but there is a very weird bug # where it can fail for `Literal[None]`. # We just need to redefine a useless `Literal[None]` inside the function body to fix this @@ -431,10 +347,8 @@ def resolve_annotations( 1, ): value = ForwardRef(value, is_argument=False, is_class=True) - elif sys.version_info >= (3, 7): - value = ForwardRef(value, is_argument=False) else: - value = ForwardRef(value) + value = ForwardRef(value, is_argument=False) try: value = _eval_type(value, base_globals, None) except NameError: @@ -448,25 +362,15 @@ def is_callable_type(type_: Type[Any]) -> bool: return type_ is Callable or get_origin(type_) is Callable -if sys.version_info >= (3, 7): - - def is_literal_type(type_: Type[Any]) -> bool: - return Literal is not None and get_origin(type_) is Literal - - def literal_values(type_: Type[Any]) -> Tuple[Any, ...]: - return get_args(type_) - -else: - - def is_literal_type(type_: Type[Any]) -> bool: - return ( - Literal is not None - and hasattr(type_, "__values__") - and type_ == Literal[type_.__values__] - ) +def is_literal_type(type_: Type[Any]) -> bool: + return ( + Literal is not None + and hasattr(type_, "__values__") + and type_ == Literal[type_.__values__] + ) - def literal_values(type_: Type[Any]) -> Tuple[Any, ...]: - return type_.__values__ +def literal_values(type_: Type[Any]) -> Tuple[Any, ...]: + return type_.__values__ def all_literal_values(type_: Type[Any]) -> Tuple[Any, ...]: @@ -522,9 +426,7 @@ def _check_classvar(v: Optional[Type[Any]]) -> bool: if v is None: return False - return v.__class__ == ClassVar.__class__ and ( - sys.version_info < (3, 7) or getattr(v, "_name", None) == "ClassVar" - ) + return v.__class__ == ClassVar.__class__ and getattr(v, "_name", None) == "ClassVar" def is_classvar(ann_type: Type[Any]) -> bool: diff --git a/typer/core.py b/typer/core.py index 31fece5a76..b85ce81a22 100644 --- a/typer/core.py +++ b/typer/core.py @@ -9,6 +9,7 @@ Callable, Dict, List, + Literal, MutableMapping, Optional, Sequence, @@ -26,11 +27,6 @@ import click.types import click.utils -if sys.version_info >= (3, 8): - from typing import Literal -else: - from typing_extensions import Literal - try: import rich diff --git a/typer/rich_utils.py b/typer/rich_utils.py index cf0538e914..a3facbc2ce 100644 --- a/typer/rich_utils.py +++ b/typer/rich_utils.py @@ -1,11 +1,10 @@ # Extracted and modified from https://github.com/ewels/rich-click import inspect -import sys from collections import defaultdict from gettext import gettext as _ from os import getenv -from typing import Any, DefaultDict, Dict, Iterable, List, Optional, Union +from typing import Any, DefaultDict, Dict, Iterable, List, Literal, Optional, Union import click from rich import box @@ -21,10 +20,6 @@ from rich.text import Text from rich.theme import Theme -if sys.version_info >= (3, 8): - from typing import Literal -else: - from typing_extensions import Literal # Default styles STYLE_OPTION = "bold cyan" From 314f504bf8f9a22662c6dcb38d95fdd4cc52921e Mon Sep 17 00:00:00 2001 From: Kinuax Date: Wed, 15 May 2024 11:58:17 +0200 Subject: [PATCH 2/9] Fix linting --- typer/_typing.py | 10 ++++++++-- typer/rich_utils.py | 1 - 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/typer/_typing.py b/typer/_typing.py index c9bcf18565..595a46d063 100644 --- a/typer/_typing.py +++ b/typer/_typing.py @@ -10,7 +10,6 @@ TYPE_CHECKING, AbstractSet, Any, - Callable as TypingCallable, ClassVar, Dict, ForwardRef, @@ -26,9 +25,16 @@ Union, _eval_type, cast, + get_type_hints, +) +from typing import ( + Callable as TypingCallable, +) +from typing import ( get_args as _typing_get_args, +) +from typing import ( get_origin as _typing_get_origin, - get_type_hints, ) from typing_extensions import Annotated, Literal diff --git a/typer/rich_utils.py b/typer/rich_utils.py index a3facbc2ce..2c9656069f 100644 --- a/typer/rich_utils.py +++ b/typer/rich_utils.py @@ -20,7 +20,6 @@ from rich.text import Text from rich.theme import Theme - # Default styles STYLE_OPTION = "bold cyan" STYLE_SWITCH = "bold green" From caf601e33910c7de4a647a6891ed00fb56f7a624 Mon Sep 17 00:00:00 2001 From: Kinuax Date: Wed, 15 May 2024 12:04:46 +0200 Subject: [PATCH 3/9] Fix linting --- typer/_typing.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/typer/_typing.py b/typer/_typing.py index 595a46d063..8e52d7dc6d 100644 --- a/typer/_typing.py +++ b/typer/_typing.py @@ -114,6 +114,7 @@ def _generic_get_args(tp: Type[Any]) -> Tuple[Any, ...]: return (Any,) * tp._nparams return () + def get_args(tp: Type[Any]) -> Tuple[Any, ...]: """Get type arguments with all substitutions performed. @@ -128,9 +129,7 @@ def get_args(tp: Type[Any]) -> Tuple[Any, ...]: if type(tp).__name__ in AnnotatedTypeNames: return tp.__args__ + tp.__metadata__ # the fallback is needed for the same reasons as `get_origin` (see above) - return ( - _typing_get_args(tp) or getattr(tp, "__args__", ()) or _generic_get_args(tp) - ) + return _typing_get_args(tp) or getattr(tp, "__args__", ()) or _generic_get_args(tp) if sys.version_info[:2] == (3, 8): @@ -375,6 +374,7 @@ def is_literal_type(type_: Type[Any]) -> bool: and type_ == Literal[type_.__values__] ) + def literal_values(type_: Type[Any]) -> Tuple[Any, ...]: return type_.__values__ From 36c4439973a3dd03085392c10a127c6966b178d5 Mon Sep 17 00:00:00 2001 From: Kinuax Date: Fri, 17 May 2024 14:07:35 +0200 Subject: [PATCH 4/9] Remove duplicated Python version --- .github/workflows/test.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c48502f30b..0ae1c6c0b6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,8 +19,6 @@ jobs: os: [ ubuntu-latest, windows-latest, macos-latest ] python-version: [ "3.11" ] include: - - os: ubuntu-latest - python-version: "3.8" - os: macos-latest python-version: "3.8" - os: windows-latest From 5facc1119bb58e63739c7a801dfef09d5c5b566f Mon Sep 17 00:00:00 2001 From: Kinuax Date: Mon, 20 May 2024 20:37:27 +0200 Subject: [PATCH 5/9] Fix branching --- typer/_typing.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/typer/_typing.py b/typer/_typing.py index 8e52d7dc6d..4dea3790ac 100644 --- a/typer/_typing.py +++ b/typer/_typing.py @@ -368,15 +368,11 @@ def is_callable_type(type_: Type[Any]) -> bool: def is_literal_type(type_: Type[Any]) -> bool: - return ( - Literal is not None - and hasattr(type_, "__values__") - and type_ == Literal[type_.__values__] - ) + return Literal is not None and get_origin(type_) is Literal def literal_values(type_: Type[Any]) -> Tuple[Any, ...]: - return type_.__values__ + return get_args(type_) def all_literal_values(type_: Type[Any]) -> Tuple[Any, ...]: From 175240af921185e036a8405802f08ae4303b8c6c Mon Sep 17 00:00:00 2001 From: svlandeg Date: Tue, 3 Sep 2024 11:27:17 +0200 Subject: [PATCH 6/9] remove leftover from merge conflict --- typer/core.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/typer/core.py b/typer/core.py index c4a03b05d7..53be9a4639 100644 --- a/typer/core.py +++ b/typer/core.py @@ -27,11 +27,6 @@ import click.types import click.utils -if sys.version_info >= (3, 8): - from typing import Literal -else: - from typing_extensions import Literal - MarkupMode = Literal["markdown", "rich", None] try: From a0741b3942dea258f07dccfb01f20583e6d99b65 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Tue, 3 Sep 2024 11:35:02 +0200 Subject: [PATCH 7/9] update more documentation files with 3.8 reference --- docs/tutorial/arguments/default.md | 8 ++--- docs/tutorial/arguments/envvar.md | 12 +++---- docs/tutorial/commands/help.md | 18 +++++----- docs/tutorial/commands/options.md | 4 +-- .../arguments-with-multiple-values.md | 4 +-- .../multiple-values/multiple-options.md | 8 ++--- .../options-with-multiple-values.md | 4 +-- docs/tutorial/options-autocompletion.md | 36 +++++++++---------- docs/tutorial/parameter-types/bool.md | 16 ++++----- docs/tutorial/parameter-types/custom-types.md | 6 ++-- docs/tutorial/parameter-types/datetime.md | 4 +-- docs/tutorial/parameter-types/enum.md | 8 ++--- docs/tutorial/parameter-types/file.md | 20 +++++------ docs/tutorial/parameter-types/number.md | 12 +++---- docs/tutorial/parameter-types/path.md | 8 ++--- 15 files changed, 84 insertions(+), 84 deletions(-) diff --git a/docs/tutorial/arguments/default.md b/docs/tutorial/arguments/default.md index 60a041abb1..cca543f44a 100644 --- a/docs/tutorial/arguments/default.md +++ b/docs/tutorial/arguments/default.md @@ -8,7 +8,7 @@ That way the *CLI argument* will be optional *and also* have a default value. We can also use `typer.Argument()` to make a *CLI argument* have a default value other than `None`: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="5" {!> ../docs_src/arguments/default/tutorial001_an.py!} @@ -16,7 +16,7 @@ We can also use `typer.Argument()` to make a *CLI argument* have a default value //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -72,7 +72,7 @@ Hello Camila And we can even make the default value be dynamically generated by passing a function as the `default_factory` argument: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="7-8 11" {!> ../docs_src/arguments/default/tutorial002_an.py!} @@ -80,7 +80,7 @@ And we can even make the default value be dynamically generated by passing a fun //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip diff --git a/docs/tutorial/arguments/envvar.md b/docs/tutorial/arguments/envvar.md index a53d42685e..92db7e6af3 100644 --- a/docs/tutorial/arguments/envvar.md +++ b/docs/tutorial/arguments/envvar.md @@ -10,7 +10,7 @@ You can learn more about environment variables in the [Environment Variables](.. To do that, use the `envvar` parameter for `typer.Argument()`: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="5" {!> ../docs_src/arguments/envvar/tutorial001_an.py!} @@ -18,7 +18,7 @@ To do that, use the `envvar` parameter for `typer.Argument()`: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -75,7 +75,7 @@ Hello Mr. Czernobog You are not restricted to a single environment variable, you can declare a list of environment variables that could be used to get a value if it was not passed in the command line: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="6" {!> ../docs_src/arguments/envvar/tutorial002_an.py!} @@ -83,7 +83,7 @@ You are not restricted to a single environment variable, you can declare a list //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -130,7 +130,7 @@ Hello Mr. Anubis By default, environment variables used will be shown in the help text, but you can disable them with `show_envvar=False`: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="7" {!> ../docs_src/arguments/envvar/tutorial003_an.py!} @@ -138,7 +138,7 @@ By default, environment variables used will be shown in the help text, but you c //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip diff --git a/docs/tutorial/commands/help.md b/docs/tutorial/commands/help.md index 5a6668038a..e010a1ea07 100644 --- a/docs/tutorial/commands/help.md +++ b/docs/tutorial/commands/help.md @@ -4,7 +4,7 @@ The same as before, you can add help for the commands in the docstrings and the And the `typer.Typer()` application receives a parameter `help` that you can pass with the main help text for your CLI program: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="4 9-11 22 26-30 43 47-51 60-62" {!> ../docs_src/commands/help/tutorial001_an.py!} @@ -12,7 +12,7 @@ And the `typer.Typer()` application receives a parameter `help` that you can pas //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -216,7 +216,7 @@ By default, `rich_markup_mode` is `None` if Rich is not installed, and `"rich"` If you set `rich_markup_mode="rich"` when creating the `typer.Typer()` app, you will be able to use Rich Console Markup in the docstring, and even in the help for the *CLI arguments* and options: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="4 10 14-16 21 24 27" {!> ../docs_src/commands/help/tutorial004_an.py!} @@ -224,7 +224,7 @@ If you set `rich_markup_mode="rich"` when creating the `typer.Typer()` app, you //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -299,7 +299,7 @@ $ python main.py delete --help If you set `rich_markup_mode="markdown"` when creating the `typer.Typer()` app, you will be able to use Markdown in the docstring: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="4 9 12-20 25 27-28" {!> ../docs_src/commands/help/tutorial005_an.py!} @@ -307,7 +307,7 @@ If you set `rich_markup_mode="markdown"` when creating the `typer.Typer()` app, //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -395,7 +395,7 @@ If you installed ../docs_src/commands/help/tutorial006.py!} @@ -442,7 +442,7 @@ The same way, you can configure the panels for *CLI arguments* and *CLI options* And of course, in the same application you can also set the `rich_help_panel` for commands. -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="15 21 27 37" {!> ../docs_src/commands/help/tutorial007_an.py!} @@ -450,7 +450,7 @@ And of course, in the same application you can also set the `rich_help_panel` fo //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip diff --git a/docs/tutorial/commands/options.md b/docs/tutorial/commands/options.md index a9c21b66b9..62455994ed 100644 --- a/docs/tutorial/commands/options.md +++ b/docs/tutorial/commands/options.md @@ -4,7 +4,7 @@ Commands can also have their own *CLI options*. In fact, each command can have different *CLI arguments* and *CLI options*: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="8 14-17 27-29 38" {!> ../docs_src/commands/options/tutorial001_an.py!} @@ -12,7 +12,7 @@ In fact, each command can have different *CLI arguments* and *CLI options*: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip diff --git a/docs/tutorial/multiple-values/arguments-with-multiple-values.md b/docs/tutorial/multiple-values/arguments-with-multiple-values.md index b48484c105..ced64eecfc 100644 --- a/docs/tutorial/multiple-values/arguments-with-multiple-values.md +++ b/docs/tutorial/multiple-values/arguments-with-multiple-values.md @@ -39,7 +39,7 @@ A `List` can only be used in the last command (if there are subcommands), as thi If you want a specific number of values and types, you can use a tuple, and it can even have default values: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="8-10" {!> ../docs_src/multiple_values/arguments_with_multiple_values/tutorial002_an.py!} @@ -47,7 +47,7 @@ If you want a specific number of values and types, you can use a tuple, and it c //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip diff --git a/docs/tutorial/multiple-values/multiple-options.md b/docs/tutorial/multiple-values/multiple-options.md index 0c876eab52..6e4ca52c52 100644 --- a/docs/tutorial/multiple-values/multiple-options.md +++ b/docs/tutorial/multiple-values/multiple-options.md @@ -6,7 +6,7 @@ For example, let's say you want to accept several users in a single execution. For this, use the standard Python `typing.List` to declare it as a `list` of `str`: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="1 7" {!> ../docs_src/multiple_values/multiple_options/tutorial001_an.py!} @@ -14,7 +14,7 @@ For this, use the standard Python `typing.List` to declare it as a `list` of `st //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -60,7 +60,7 @@ Processing user: Morty The same way, you can use other types and they will be converted by **Typer** to their declared type: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="7" {!> ../docs_src/multiple_values/multiple_options/tutorial002_an.py!} @@ -68,7 +68,7 @@ The same way, you can use other types and they will be converted by **Typer** to //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip diff --git a/docs/tutorial/multiple-values/options-with-multiple-values.md b/docs/tutorial/multiple-values/options-with-multiple-values.md index 1d3f5deff4..cd8eb11584 100644 --- a/docs/tutorial/multiple-values/options-with-multiple-values.md +++ b/docs/tutorial/multiple-values/options-with-multiple-values.md @@ -6,7 +6,7 @@ You can set the number of values and types to anything you want, but it has to b For this, use the standard Python `typing.Tuple`: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="1 7" {!> ../docs_src/multiple_values/options_with_multiple_values/tutorial001_an.py!} @@ -14,7 +14,7 @@ For this, use the standard Python `typing.Tuple`: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip diff --git a/docs/tutorial/options-autocompletion.md b/docs/tutorial/options-autocompletion.md index 43777d04fb..bfc5ee0cc0 100644 --- a/docs/tutorial/options-autocompletion.md +++ b/docs/tutorial/options-autocompletion.md @@ -16,7 +16,7 @@ To check it quickly without creating a new Python package, use the `typer` comma Then let's create small example program: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python {!> ../docs_src/options_autocompletion/tutorial001_an.py!} @@ -24,7 +24,7 @@ Then let's create small example program: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -70,7 +70,7 @@ Right now we get completion for the *CLI option* names, but not for the values. We can provide completion for the values creating an `autocompletion` function, similar to the `callback` functions from [CLI Option Callback and Context](./options/callback-and-context.md){.internal-link target=_blank}: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="5-6 15" {!> ../docs_src/options_autocompletion/tutorial002_an.py!} @@ -78,7 +78,7 @@ We can provide completion for the values creating an `autocompletion` function, //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -119,7 +119,7 @@ Modify the `complete_name()` function to receive a parameter of type `str`, it w Then we can check and return only the values that start with the incomplete value from the command line: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="7-12" {!> ../docs_src/options_autocompletion/tutorial003_an.py!} @@ -127,7 +127,7 @@ Then we can check and return only the values that start with the incomplete valu //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -178,7 +178,7 @@ In the `complete_name()` function, instead of providing one `str` per completion So, in the end, we return a `list` of `tuples` of `str`: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="4-8 11-17" {!> ../docs_src/options_autocompletion/tutorial004_an.py!} @@ -186,7 +186,7 @@ So, in the end, we return a `list` of `tuples` of `str`: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -239,7 +239,7 @@ Instead of creating and returning a list with values (`str` or `tuple`), we can That way our function will be a generator that **Typer** (actually Click) can iterate: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="11-14" {!> ../docs_src/options_autocompletion/tutorial005_an.py!} @@ -247,7 +247,7 @@ That way our function will be a ../docs_src/options_autocompletion/tutorial006_an.py!} @@ -303,7 +303,7 @@ For this we use a `List` of `str`: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -340,7 +340,7 @@ But you can access the context by declaring a function parameter of type `typer. And from that context you can get the current values for each parameter. -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="13-14 16" {!> ../docs_src/options_autocompletion/tutorial007_an.py!} @@ -348,7 +348,7 @@ And from that context you can get the current values for each parameter. //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -436,7 +436,7 @@ You can print to "standard error" with a **Rich** `Console(stderr=True)`. Using `stderr=True` tells **Rich** that the output should be shown in "standard error". -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="13 16-17" {!> ../docs_src/options_autocompletion/tutorial008_an.py!} @@ -444,7 +444,7 @@ Using `stderr=True` tells **Rich** that the output should be shown in "standard //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -504,7 +504,7 @@ But it's probably useful only in very advanced use cases. Of course, you can declare everything if you need it, the context, the raw *CLI parameters*, and the incomplete `str`: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="16" {!> ../docs_src/options_autocompletion/tutorial009_an.py!} @@ -512,7 +512,7 @@ Of course, you can declare everything if you need it, the context, the raw *CLI //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip diff --git a/docs/tutorial/parameter-types/bool.md b/docs/tutorial/parameter-types/bool.md index 14adc6a6dd..13d089ae72 100644 --- a/docs/tutorial/parameter-types/bool.md +++ b/docs/tutorial/parameter-types/bool.md @@ -10,7 +10,7 @@ Let's say that we want a `--force` *CLI option* only, we want to discard `--no-f We can do that by specifying the exact name we want: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="5" {!> ../docs_src/parameter_types/bool/tutorial001_an.py!} @@ -18,7 +18,7 @@ We can do that by specifying the exact name we want: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -78,7 +78,7 @@ We might want to instead have `--accept` and `--reject`. We can do that by passing a single `str` with the 2 names for the `bool` *CLI option* separated by `/`: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="7" {!> ../docs_src/parameter_types/bool/tutorial002_an.py!} @@ -86,7 +86,7 @@ We can do that by passing a single `str` with the 2 names for the `bool` *CLI op //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -139,7 +139,7 @@ The same way, you can declare short versions of the names for these *CLI options For example, let's say we want `-f` for `--force` and `-F` for `--no-force`: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="5" {!> ../docs_src/parameter_types/bool/tutorial003_an.py!} @@ -147,7 +147,7 @@ For example, let's say we want `-f` for `--force` and `-F` for `--no-force`: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -195,7 +195,7 @@ If you want to (although it might not be a good idea), you can declare only *CLI To do that, use a space and a single `/` and pass the negative name after: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="5" {!> ../docs_src/parameter_types/bool/tutorial004_an.py!} @@ -203,7 +203,7 @@ To do that, use a space and a single `/` and pass the negative name after: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip diff --git a/docs/tutorial/parameter-types/custom-types.md b/docs/tutorial/parameter-types/custom-types.md index b048c85bc9..7758134543 100644 --- a/docs/tutorial/parameter-types/custom-types.md +++ b/docs/tutorial/parameter-types/custom-types.md @@ -13,7 +13,7 @@ There are two ways to achieve this: `typer.Argument` and `typer.Option` can create custom parameter types with a `parser` callable. -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="13-14 18-19" {!> ../docs_src/parameter_types/custom_types/tutorial001_an.py!} @@ -21,7 +21,7 @@ There are two ways to achieve this: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -41,7 +41,7 @@ The function (or callable) that you pass to the parameter `parser` will receive If you already have a Click Custom Type, you can use it in `typer.Argument()` and `typer.Option()` with the `click_type` parameter. -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="14-18 22-25" {!> ../docs_src/parameter_types/custom_types/tutorial002_an.py!} diff --git a/docs/tutorial/parameter-types/datetime.md b/docs/tutorial/parameter-types/datetime.md index 84f3524eee..600ce0bf59 100644 --- a/docs/tutorial/parameter-types/datetime.md +++ b/docs/tutorial/parameter-types/datetime.md @@ -60,7 +60,7 @@ For example, let's imagine that you want to accept an ISO formatted datetime, bu ...It's a crazy example, but let's say you also needed that strange format: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="11" {!> ../docs_src/parameter_types/datetime/tutorial002_an.py!} @@ -68,7 +68,7 @@ For example, let's imagine that you want to accept an ISO formatted datetime, bu //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip diff --git a/docs/tutorial/parameter-types/enum.md b/docs/tutorial/parameter-types/enum.md index 288eee5a92..1bfd607015 100644 --- a/docs/tutorial/parameter-types/enum.md +++ b/docs/tutorial/parameter-types/enum.md @@ -56,7 +56,7 @@ Error: Invalid value for '--network': 'CONV' is not one of 'simple', 'conv', 'ls You can make an `Enum` (choice) *CLI parameter* be case-insensitive with the `case_sensitive` parameter: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="15" {!> ../docs_src/parameter_types/enum/tutorial002_an.py!} @@ -64,7 +64,7 @@ You can make an `Enum` (choice) *CLI parameter* be case-insensitive with the `ca //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -100,7 +100,7 @@ Training neural network of type: lstm A *CLI parameter* can also take a list of `Enum` values: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="14" {!> ../docs_src/parameter_types/enum/tutorial003_an.py!} @@ -108,7 +108,7 @@ A *CLI parameter* can also take a list of `Enum` values: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip diff --git a/docs/tutorial/parameter-types/file.md b/docs/tutorial/parameter-types/file.md index 274962cf07..e02f977929 100644 --- a/docs/tutorial/parameter-types/file.md +++ b/docs/tutorial/parameter-types/file.md @@ -47,7 +47,7 @@ content = b"la cig\xc3\xbce\xc3\xb1a trae al ni\xc3\xb1o" You will get all the correct editor support, attributes, methods, etc for the file-like object:` -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="5" {!> ../docs_src/parameter_types/file/tutorial001_an.py!} @@ -55,7 +55,7 @@ You will get all the correct editor support, attributes, methods, etc for the fi //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -94,7 +94,7 @@ Config line: some more settings For writing text, you can use `typer.FileTextWrite`: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="5-6" {!> ../docs_src/parameter_types/file/tutorial002_an.py!} @@ -102,7 +102,7 @@ For writing text, you can use `typer.FileTextWrite`: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -158,7 +158,7 @@ You will receive `bytes` from it. It's useful for reading binary files like images: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="5" {!> ../docs_src/parameter_types/file/tutorial003_an.py!} @@ -166,7 +166,7 @@ It's useful for reading binary files like images: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -207,7 +207,7 @@ Have in mind that you have to pass `bytes` to its `.write()` method, not `str`. If you have a `str`, you have to encode it first to get `bytes`. -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="5" {!> ../docs_src/parameter_types/file/tutorial004_an.py!} @@ -215,7 +215,7 @@ If you have a `str`, you have to encode it first to get `bytes`. //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -277,7 +277,7 @@ You can override the `mode` from the defaults above. For example, you could use `mode="a"` to write "appending" to the same file: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="5" {!> ../docs_src/parameter_types/file/tutorial005_an.py!} @@ -285,7 +285,7 @@ For example, you could use `mode="a"` to write "appending" to the same file: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip diff --git a/docs/tutorial/parameter-types/number.md b/docs/tutorial/parameter-types/number.md index 062fa7bce3..a7d8fc4640 100644 --- a/docs/tutorial/parameter-types/number.md +++ b/docs/tutorial/parameter-types/number.md @@ -2,7 +2,7 @@ You can define numeric validations with `max` and `min` values for `int` and `float` *CLI parameters*: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="6-8" {!> ../docs_src/parameter_types/number/tutorial001_an.py!} @@ -10,7 +10,7 @@ You can define numeric validations with `max` and `min` values for `int` and `fl //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -93,7 +93,7 @@ You might want to, instead of showing an error, use the closest minimum or maxim You can do it with the `clamp` parameter: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="6-8" {!> ../docs_src/parameter_types/number/tutorial002_an.py!} @@ -101,7 +101,7 @@ You can do it with the `clamp` parameter: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -142,7 +142,7 @@ ID is 5 You can make a *CLI option* work as a counter with the `counter` parameter: -//// tab | Python 3.7+ +//// tab | Python 3.8+ ```Python hl_lines="5" {!> ../docs_src/parameter_types/number/tutorial003_an.py!} @@ -150,7 +150,7 @@ You can make a *CLI option* work as a counter with the `counter` parameter: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip diff --git a/docs/tutorial/parameter-types/path.md b/docs/tutorial/parameter-types/path.md index 0268630bcd..b481a7de03 100644 --- a/docs/tutorial/parameter-types/path.md +++ b/docs/tutorial/parameter-types/path.md @@ -4,7 +4,7 @@ You can declare a *CLI parameter* to be a standard Python ../docs_src/parameter_types/path/tutorial001_an.py!} @@ -12,7 +12,7 @@ This is what you would do for directory paths, file paths, etc: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip @@ -85,7 +85,7 @@ All these parameters come directly from ../docs_src/parameter_types/path/tutorial002_an.py!} @@ -93,7 +93,7 @@ For example: //// -//// tab | Python 3.7+ non-Annotated +//// tab | Python 3.8+ non-Annotated /// tip From 767a11e4254354e43320b2abc92607b5f2c16c49 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 09:12:03 +0000 Subject: [PATCH 8/9] =?UTF-8?q?=F0=9F=8E=A8=20[pre-commit.ci]=20Auto=20for?= =?UTF-8?q?mat=20from=20pre-commit.com=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- typer/rich_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/typer/rich_utils.py b/typer/rich_utils.py index 27107d3290..cec224ce51 100644 --- a/typer/rich_utils.py +++ b/typer/rich_utils.py @@ -2,7 +2,6 @@ import inspect import io -import sys from collections import defaultdict from gettext import gettext as _ from os import getenv From 0e63bb4cddd386ca16a782c2cb6c5608602dc062 Mon Sep 17 00:00:00 2001 From: Kinuax Date: Mon, 14 Apr 2025 22:34:40 +0200 Subject: [PATCH 9/9] Restore rich_utils --- typer/rich_utils.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/typer/rich_utils.py b/typer/rich_utils.py index 501e8d359f..4a8ffbeeb0 100644 --- a/typer/rich_utils.py +++ b/typer/rich_utils.py @@ -2,6 +2,7 @@ import inspect import io +import sys from collections import defaultdict from gettext import gettext as _ from os import getenv @@ -21,7 +22,10 @@ from rich.text import Text from rich.theme import Theme -from ._typing import Literal +if sys.version_info >= (3, 9): + from typing import Literal +else: + from typing_extensions import Literal # Default styles STYLE_OPTION = "bold cyan"