From 7d6db83b2d9b8a62b2044908f701260e03d53f86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 18:25:11 -0500 Subject: [PATCH 1/6] =?UTF-8?q?=E2=AC=86=20Bump=20ruff=20from=200.2.0=20to?= =?UTF-8?q?=200.6.1=20(#938)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ⬆ Bump ruff from 0.2.0 to 0.6.1 Bumps [ruff](https://github.com/astral-sh/ruff) from 0.2.0 to 0.6.1. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/v0.2.0...0.6.1) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: svlandeg Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- requirements-tests.txt | 2 +- tests/test_ambiguous_params.py | 15 +++++---------- typer/main.py | 8 ++++---- typer/params.py | 12 ++++-------- typer/rich_utils.py | 6 +++--- 6 files changed, 18 insertions(+), 27 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3289dd0950..4ea5b873a6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.2.0 + rev: v0.6.1 hooks: - id: ruff args: diff --git a/requirements-tests.txt b/requirements-tests.txt index ac6377c10d..0cacd0ecbe 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -6,7 +6,7 @@ coverage[toml] >=6.2,<8.0 pytest-xdist >=1.32.0,<4.0.0 pytest-sugar >=0.9.4,<1.1.0 mypy ==1.4.1 -ruff ==0.2.0 +ruff ==0.6.1 # Needed explicitly by typer-slim rich >=10.11.0 shellingham >=1.3.0 diff --git a/tests/test_ambiguous_params.py b/tests/test_ambiguous_params.py index 4dbf09b569..0693c8e9aa 100644 --- a/tests/test_ambiguous_params.py +++ b/tests/test_ambiguous_params.py @@ -29,8 +29,7 @@ def test_forbid_default_value_in_annotated_argument(): # This test case only works with `typer.Argument`. `typer.Option` uses positionals # for param_decls too. @app.command() - def cmd(my_param: Annotated[str, typer.Argument("foo")]): - ... # pragma: no cover + def cmd(my_param: Annotated[str, typer.Argument("foo")]): ... # pragma: no cover with pytest.raises(AnnotatedParamWithDefaultValueError) as excinfo: runner.invoke(app) @@ -64,8 +63,7 @@ def test_forbid_annotated_param_and_default_param(param, param_info_type): app = typer.Typer() @app.command() - def cmd(my_param: Annotated[str, param()] = param("foo")): - ... # pragma: no cover + def cmd(my_param: Annotated[str, param()] = param("foo")): ... # pragma: no cover with pytest.raises(MixedAnnotatedAndDefaultStyleError) as excinfo: runner.invoke(app) @@ -83,8 +81,7 @@ def test_forbid_multiple_typer_params_in_annotated(): @app.command() def cmd( my_param: Annotated[str, typer.Argument(), typer.Argument()], - ): - ... # pragma: no cover + ): ... # pragma: no cover with pytest.raises(MultipleTyperAnnotationsError) as excinfo: runner.invoke(app) @@ -121,8 +118,7 @@ def make_string(): @app.command() def cmd( my_param: Annotated[str, param(default_factory=make_string)] = "hello", - ): - ... # pragma: no cover + ): ... # pragma: no cover with pytest.raises(DefaultFactoryAndDefaultValueError) as excinfo: runner.invoke(app) @@ -171,8 +167,7 @@ def make_string(): @app.command() def cmd( my_param: str = param("hi", default_factory=make_string), - ): - ... # pragma: no cover + ): ... # pragma: no cover with pytest.raises(DefaultFactoryAndDefaultValueError) as excinfo: runner.invoke(app) diff --git a/typer/main.py b/typer/main.py index 013939bf20..c385c48c5f 100644 --- a/typer/main.py +++ b/typer/main.py @@ -715,9 +715,9 @@ def get_click_type( elif parameter_info.parser is not None: return click.types.FuncParamType(parameter_info.parser) - elif annotation == str: + elif annotation is str: return click.STRING - elif annotation == int: + elif annotation is int: if parameter_info.min is not None or parameter_info.max is not None: min_ = None max_ = None @@ -728,7 +728,7 @@ def get_click_type( return click.IntRange(min=min_, max=max_, clamp=parameter_info.clamp) else: return click.INT - elif annotation == float: + elif annotation is float: if parameter_info.min is not None or parameter_info.max is not None: return click.FloatRange( min=parameter_info.min, @@ -737,7 +737,7 @@ def get_click_type( ) else: return click.FLOAT - elif annotation == bool: + elif annotation is bool: return click.BOOL elif annotation == UUID: return click.UUID diff --git a/typer/params.py b/typer/params.py index 710a4cf136..2fd025c90d 100644 --- a/typer/params.py +++ b/typer/params.py @@ -68,8 +68,7 @@ def Option( path_type: Union[None, Type[str], Type[bytes]] = None, # Rich settings rich_help_panel: Union[str, None] = None, -) -> Any: - ... +) -> Any: ... # Overload for Option created with custom type 'click_type' @@ -132,8 +131,7 @@ def Option( path_type: Union[None, Type[str], Type[bytes]] = None, # Rich settings rich_help_panel: Union[str, None] = None, -) -> Any: - ... +) -> Any: ... def Option( @@ -305,8 +303,7 @@ def Argument( path_type: Union[None, Type[str], Type[bytes]] = None, # Rich settings rich_help_panel: Union[str, None] = None, -) -> Any: - ... +) -> Any: ... # Overload for Argument created with custom type 'click_type' @@ -361,8 +358,7 @@ def Argument( path_type: Union[None, Type[str], Type[bytes]] = None, # Rich settings rich_help_panel: Union[str, None] = None, -) -> Any: - ... +) -> Any: ... def Argument( diff --git a/typer/rich_utils.py b/typer/rich_utils.py index cf0538e914..900f4b4e87 100644 --- a/typer/rich_utils.py +++ b/typer/rich_utils.py @@ -68,9 +68,9 @@ STYLE_ABORTED = "red" _TERMINAL_WIDTH = getenv("TERMINAL_WIDTH") MAX_WIDTH = int(_TERMINAL_WIDTH) if _TERMINAL_WIDTH else None -COLOR_SYSTEM: Optional[ - Literal["auto", "standard", "256", "truecolor", "windows"] -] = "auto" # Set to None to disable colors +COLOR_SYSTEM: Optional[Literal["auto", "standard", "256", "truecolor", "windows"]] = ( + "auto" # Set to None to disable colors +) _TYPER_FORCE_DISABLE_TERMINAL = getenv("_TYPER_FORCE_DISABLE_TERMINAL") FORCE_TERMINAL = ( True From 127d074446e267645d4ef42fd39c650735d4c304 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 28 Aug 2024 23:25:30 +0000 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index ad028087d5..395a185361 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -8,6 +8,7 @@ ### Internal +* ⬆ Bump ruff from 0.2.0 to 0.6.1. PR [#938](https://github.com/fastapi/typer/pull/938) by [@dependabot[bot]](https://github.com/apps/dependabot). * 👷 Update `latest-changes` GitHub Action. PR [#955](https://github.com/fastapi/typer/pull/955) by [@tiangolo](https://github.com/tiangolo). ## 0.12.5 From 76824fc369c57e50c533888807559a5f2382258a Mon Sep 17 00:00:00 2001 From: Sofie Van Landeghem Date: Thu, 29 Aug 2024 01:26:35 +0200 Subject: [PATCH 3/6] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20typo=20in=20func?= =?UTF-8?q?tion=20name=20`=5Fmake=5Frich=5Ftext`=20(#959)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- typer/rich_utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/typer/rich_utils.py b/typer/rich_utils.py index 900f4b4e87..905c5a3dbf 100644 --- a/typer/rich_utils.py +++ b/typer/rich_utils.py @@ -144,7 +144,7 @@ def _get_rich_console(stderr: bool = False) -> Console: ) -def _make_rich_rext( +def _make_rich_text( *, text: str, style: str = "", markup_mode: MarkupMode ) -> Union[Markdown, Text]: """Take a string, remove indentations, and return styled text. @@ -194,7 +194,7 @@ def _get_help_text( # Remove single linebreaks if markup_mode != MARKUP_MODE_MARKDOWN and not first_line.startswith("\b"): first_line = first_line.replace("\n", " ") - yield _make_rich_rext( + yield _make_rich_text( text=first_line.strip(), style=STYLE_HELPTEXT_FIRST_LINE, markup_mode=markup_mode, @@ -217,7 +217,7 @@ def _get_help_text( # Join with double linebreaks if markdown remaining_lines = "\n\n".join(remaining_paragraphs) - yield _make_rich_rext( + yield _make_rich_text( text=remaining_lines, style=STYLE_HELPTEXT, markup_mode=markup_mode, @@ -272,7 +272,7 @@ def _get_parameter_help( for x in paragraphs ] items.append( - _make_rich_rext( + _make_rich_text( text="\n".join(paragraphs).strip(), style=STYLE_OPTION_HELP, markup_mode=markup_mode, @@ -331,7 +331,7 @@ def _make_command_help( paragraphs[0] = paragraphs[0].replace("\n", " ") elif paragraphs[0].startswith("\b"): paragraphs[0] = paragraphs[0].replace("\b\n", "") - return _make_rich_rext( + return _make_rich_text( text=paragraphs[0].strip(), style=STYLE_OPTION_HELP, markup_mode=markup_mode, @@ -674,7 +674,7 @@ def rich_format_help( # Remove single linebreaks, replace double with single lines = obj.epilog.split("\n\n") epilogue = "\n".join([x.replace("\n", " ").strip() for x in lines]) - epilogue_text = _make_rich_rext(text=epilogue, markup_mode=markup_mode) + epilogue_text = _make_rich_text(text=epilogue, markup_mode=markup_mode) console.print(Padding(Align(epilogue_text, pad=False), 1)) From fc2a63462565c5f01f9c805de2fd63b73459878f Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 28 Aug 2024 23:27:10 +0000 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 395a185361..d0a1d428a7 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -6,6 +6,10 @@ * ✨ Show help items in order of definition. PR [#944](https://github.com/fastapi/typer/pull/944) by [@svlandeg](https://github.com/svlandeg). +### Refactors + +* ✏️ Fix typo in function name `_make_rich_text`. PR [#959](https://github.com/fastapi/typer/pull/959) by [@svlandeg](https://github.com/svlandeg). + ### Internal * ⬆ Bump ruff from 0.2.0 to 0.6.1. PR [#938](https://github.com/fastapi/typer/pull/938) by [@dependabot[bot]](https://github.com/apps/dependabot). From 67a9eee9967d553446cb09f6367073271434db01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Todorovich?= Date: Wed, 28 Aug 2024 20:32:10 -0300 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unused=20functional?= =?UTF-8?q?ity=20from=20`=5Ftyping.py`=20file=20(#805)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: svlandeg --- pyproject.toml | 3 - typer/_typing.py | 477 +---------------------------------------------- typer/main.py | 3 +- typer/utils.py | 5 +- 4 files changed, 7 insertions(+), 481 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b64b18d911..ce9d61afa3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -194,9 +194,6 @@ ignore = [ # Loop control variable `x` not used within loop body "docs_src/using_click/tutorial001.py" = ["B007"] -# TODO: refactor _typing.py, remove unnecessary code -"typer/_typing.py" = ["UP036", "F822"] - [tool.ruff.lint.isort] known-third-party = ["typer", "click"] # For docs_src/subcommands/tutorial003/ diff --git a/typer/_typing.py b/typer/_typing.py index 30cd612de9..147a50540b 100644 --- a/typer/_typing.py +++ b/typer/_typing.py @@ -1,304 +1,39 @@ # 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 +# Reduced drastically to only include Typer-specific 3.7+ functionality # mypy: ignore-errors import sys -from collections.abc import Callable as Callable -from os import PathLike from typing import ( - TYPE_CHECKING, - AbstractSet, Any, - ClassVar, - Dict, - ForwardRef, - Generator, - List, - Mapping, - NewType, + Callable, Optional, - Sequence, - Set, Tuple, Type, Union, - _eval_type, - cast, - get_type_hints, ) -from typing import Callable as TypingCallable - -from typing_extensions import Annotated, Literal - -AnyCallable = TypingCallable[..., Any] -NoArgAnyCallable = TypingCallable[[], Any] - -try: - from typing import _TypingBase as typing_base # type: ignore -except ImportError: - from typing import _Final as typing_base # type: ignore - -try: - from typing import GenericAlias as TypingGenericAlias # type: ignore -except ImportError: - # python < 3.9 does not have GenericAlias (list[int], tuple[str, ...] and so on) - TypingGenericAlias = () - -try: - from types import UnionType as TypesUnionType # type: ignore -except ImportError: - # python < 3.10 does not have UnionType (str | int, byte | bool and so on) - TypesUnionType = () - - -if sys.version_info < (3, 9): - - def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any: - return type_._evaluate(globalns, localns) - -else: - - def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any: - # Even though it is the right signature for python 3.9, mypy complains with - # `error: Too many arguments for "_evaluate" of "ForwardRef"` hence the cast... - return cast(Any, type_)._evaluate(globalns, localns, set()) - - -if sys.version_info < (3, 9): - # Ensure we always get all the whole `Annotated` hint, not just the annotated type. - # For 3.7 to 3.8, `get_type_hints` doesn't recognize `typing_extensions.Annotated`, - # so it already returns the full annotation - get_all_type_hints = get_type_hints - -else: - - def get_all_type_hints(obj: Any, globalns: Any = None, localns: Any = None) -> Any: - return get_type_hints(obj, globalns, localns, include_extras=True) - - -# Annotated[...] is implemented by returning an instance of one of these classes, depending on -# python/typing_extensions version. -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, 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_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): - - def convert_generics(tp: Type[Any]) -> Type[Any]: - """Python 3.9 and older only supports generics from `typing` module. - They convert strings to ForwardRef automatically. - - Examples:: - typing.List['Hero'] == typing.List[ForwardRef('Hero')] - """ - return tp - -else: - from typing import _UnionGenericAlias # type: ignore - - from typing_extensions import _AnnotatedAlias - - def convert_generics(tp: Type[Any]) -> Type[Any]: - """ - Recursively searches for `str` type hints and replaces them with ForwardRef. - - Examples:: - convert_generics(list['Hero']) == list[ForwardRef('Hero')] - convert_generics(dict['Hero', 'Team']) == dict[ForwardRef('Hero'), ForwardRef('Team')] - convert_generics(typing.Dict['Hero', 'Team']) == typing.Dict[ForwardRef('Hero'), ForwardRef('Team')] - convert_generics(list[str | 'Hero'] | int) == list[str | ForwardRef('Hero')] | int - """ - origin = get_origin(tp) - if not origin or not hasattr(tp, "__args__"): - return tp - - args = get_args(tp) - - # typing.Annotated needs special treatment - if origin is Annotated: - return _AnnotatedAlias(convert_generics(args[0]), args[1:]) - - # recursively replace `str` instances inside of `GenericAlias` with `ForwardRef(arg)` - converted = tuple( - ForwardRef(arg) - if isinstance(arg, str) and isinstance(tp, TypingGenericAlias) - else convert_generics(arg) - for arg in args - ) - - if converted == args: - return tp - elif isinstance(tp, TypingGenericAlias): - return TypingGenericAlias(origin, converted) - elif isinstance(tp, TypesUnionType): - # recreate types.UnionType (PEP604, Python >= 3.10) - return _UnionGenericAlias(origin, converted) - else: - try: - setattr(tp, "__args__", converted) # noqa: B010 - except AttributeError: - pass - return tp +from typing_extensions import Literal, get_args, get_origin if sys.version_info < (3, 10): def is_union(tp: Optional[Type[Any]]) -> bool: return tp is Union - WithArgsTypes = (TypingGenericAlias,) - else: import types - import typing def is_union(tp: Optional[Type[Any]]) -> bool: return tp is Union or tp is types.UnionType # noqa: E721 - WithArgsTypes = (typing._GenericAlias, types.GenericAlias, types.UnionType) - - -if sys.version_info < (3, 9): - StrPath = Union[str, PathLike] -else: - StrPath = Union[str, PathLike] - # TODO: Once we switch to Cython 3 to handle generics properly - # (https://github.com/cython/cython/issues/2753), use following lines instead - # of the one above - # # os.PathLike only becomes subscriptable from Python 3.9 onwards - # StrPath = Union[str, PathLike[str]] - - -if TYPE_CHECKING: - # Only in Pydantic - # from .fields import ModelField - - TupleGenerator = Generator[Tuple[str, Any], None, None] - DictStrAny = Dict[str, Any] - DictAny = Dict[Any, Any] - SetStr = Set[str] - ListStr = List[str] - IntStr = Union[int, str] - AbstractSetIntStr = AbstractSet[IntStr] - DictIntStrAny = Dict[IntStr, Any] - MappingIntStrAny = Mapping[IntStr, Any] - CallableGenerator = Generator[AnyCallable, None, None] - ReprArgs = Sequence[Tuple[Optional[str], Any]] - AnyClassMethod = classmethod[Any] __all__ = ( - "ForwardRef", - "Callable", - "AnyCallable", - "NoArgAnyCallable", "NoneType", "is_none_type", - "display_as_type", - "resolve_annotations", "is_callable_type", "is_literal_type", "all_literal_values", - "is_namedtuple", - "is_typeddict", - "is_new_type", - "new_type_supertype", - "is_classvar", - "update_field_forward_refs", - "update_model_forward_refs", - "TupleGenerator", - "DictStrAny", - "DictAny", - "SetStr", - "ListStr", - "IntStr", - "AbstractSetIntStr", - "DictIntStrAny", - "CallableGenerator", - "ReprArgs", - "AnyClassMethod", - "CallableGenerator", - "WithArgsTypes", - "get_args", - "get_origin", - "get_sub_types", - "typing_base", - "get_all_type_hints", "is_union", - "StrPath", ) @@ -339,66 +74,6 @@ def is_none_type(type_: Any) -> bool: return False -def display_as_type(v: Type[Any]) -> str: - if ( - not isinstance(v, typing_base) - and not isinstance(v, WithArgsTypes) - and not isinstance(v, type) - ): - v = v.__class__ - - if is_union(get_origin(v)): - return f'Union[{", ".join(map(display_as_type, get_args(v)))}]' - - if isinstance(v, WithArgsTypes): - # Generic alias are constructs like `list[int]` - return str(v).replace("typing.", "") - - try: - return v.__name__ - except AttributeError: - # happens with typing objects - return str(v).replace("typing.", "") - - -def resolve_annotations( - raw_annotations: Dict[str, Type[Any]], module_name: Optional[str] -) -> Dict[str, Type[Any]]: - """ - Partially taken from typing.get_type_hints. - - Resolve string or ForwardRef annotations into type objects if possible. - """ - base_globals: Optional[Dict[str, Any]] = None - if module_name: - try: - module = sys.modules[module_name] - except KeyError: - # happens occasionally, see https://github.com/samuelcolvin/pydantic/issues/2363 - pass - else: - base_globals = module.__dict__ - - annotations = {} - for name, value in raw_annotations.items(): - if isinstance(value, str): - if (3, 10) > sys.version_info >= (3, 9, 8) or sys.version_info >= ( - 3, - 10, - 1, - ): - value = ForwardRef(value, is_argument=False, is_class=True) - else: - value = ForwardRef(value, is_argument=False) - try: - value = _eval_type(value, base_globals, None) - except NameError: - # this is ok, it can be fixed with update_forward_refs - pass - annotations[name] = value - return annotations - - def is_callable_type(type_: Type[Any]) -> bool: return type_ is Callable or get_origin(type_) is Callable @@ -422,149 +97,3 @@ def all_literal_values(type_: Type[Any]) -> Tuple[Any, ...]: values = literal_values(type_) return tuple(x for value in values for x in all_literal_values(value)) - - -def is_namedtuple(type_: Type[Any]) -> bool: - """ - Check if a given class is a named tuple. - It can be either a `typing.NamedTuple` or `collections.namedtuple` - """ - from .utils import lenient_issubclass - - return lenient_issubclass(type_, tuple) and hasattr(type_, "_fields") - - -def is_typeddict(type_: Type[Any]) -> bool: - """ - Check if a given class is a typed dict (from `typing` or `typing_extensions`) - In 3.10, there will be a public method (https://docs.python.org/3.10/library/typing.html#typing.is_typeddict) - """ - from .utils import lenient_issubclass - - return lenient_issubclass(type_, dict) and hasattr(type_, "__total__") - - -test_type = NewType("test_type", str) - - -def is_new_type(type_: Type[Any]) -> bool: - """ - Check whether type_ was created using typing.NewType - """ - return isinstance(type_, test_type.__class__) and hasattr(type_, "__supertype__") # type: ignore - - -def new_type_supertype(type_: Type[Any]) -> Type[Any]: - while hasattr(type_, "__supertype__"): - type_ = type_.__supertype__ - return type_ - - -def _check_classvar(v: Optional[Type[Any]]) -> bool: - if v is None: - return False - - return v.__class__ == ClassVar.__class__ and getattr(v, "_name", None) == "ClassVar" - - -def is_classvar(ann_type: Type[Any]) -> bool: - if _check_classvar(ann_type) or _check_classvar(get_origin(ann_type)): - return True - - # this is an ugly workaround for class vars that contain forward references and are therefore themselves - # forward references, see #3679 - if ann_type.__class__ == ForwardRef and ann_type.__forward_arg__.startswith( - "ClassVar[" - ): - return True - - return False - - -# Only in Pydantic -# def update_field_forward_refs(field: "ModelField", globalns: Any, localns: Any) -> None: -# """ -# Try to update ForwardRefs on fields based on this ModelField, globalns and localns. -# """ -# if field.type_.__class__ == ForwardRef: -# field.type_ = evaluate_forwardref(field.type_, globalns, localns or None) -# field.prepare() - -# if field.sub_fields: -# for sub_f in field.sub_fields: -# update_field_forward_refs(sub_f, globalns=globalns, localns=localns) - -# if field.discriminator_key is not None: -# field.prepare_discriminated_union_sub_fields() - - -# Only in Pydantic -# def update_model_forward_refs( -# model: Type[Any], -# fields: Iterable["ModelField"], -# json_encoders: Dict[Union[Type[Any], str], AnyCallable], -# localns: "DictStrAny", -# exc_to_suppress: Tuple[Type[BaseException], ...] = (), -# ) -> None: -# """ -# Try to update model fields ForwardRefs based on model and localns. -# """ -# if model.__module__ in sys.modules: -# globalns = sys.modules[model.__module__].__dict__.copy() -# else: -# globalns = {} - -# globalns.setdefault(model.__name__, model) - -# for f in fields: -# try: -# update_field_forward_refs(f, globalns=globalns, localns=localns) -# except exc_to_suppress: -# pass - -# for key in set(json_encoders.keys()): -# if isinstance(key, str): -# fr: ForwardRef = ForwardRef(key) -# elif isinstance(key, ForwardRef): -# fr = key -# else: -# continue - -# try: -# new_key = evaluate_forwardref(fr, globalns, localns or None) -# except exc_to_suppress: # pragma: no cover -# continue - -# json_encoders[new_key] = json_encoders.pop(key) - - -def get_class(type_: Type[Any]) -> Union[None, bool, Type[Any]]: - """ - Tries to get the class of a Type[T] annotation. Returns True if Type is used - without brackets. Otherwise returns None. - """ - try: - origin = get_origin(type_) - if origin is None: # Python 3.6 - origin = type_ - if issubclass(origin, Type): # type: ignore - if not get_args(type_) or not isinstance(get_args(type_)[0], type): - return True - return get_args(type_)[0] - except (AttributeError, TypeError): - pass - return None - - -def get_sub_types(tp: Any) -> List[Any]: - """ - Return all the types that are allowed by type `tp` - `tp` can be a `Union` of allowed types or an `Annotated` type - """ - origin = get_origin(tp) - if origin is Annotated: - return get_sub_types(get_args(tp)[0]) - elif is_union(origin): - return [x for t in get_args(tp) for x in get_sub_types(t)] - else: - return [tp] diff --git a/typer/main.py b/typer/main.py index c385c48c5f..a621bda6ad 100644 --- a/typer/main.py +++ b/typer/main.py @@ -12,8 +12,9 @@ from uuid import UUID import click +from typing_extensions import get_args, get_origin -from ._typing import get_args, get_origin, is_union +from ._typing import is_union from .completion import get_completion_inspect_parameters from .core import ( DEFAULT_MARKUP_MODE, diff --git a/typer/utils.py b/typer/utils.py index 5c0e967a64..93c407447e 100644 --- a/typer/utils.py +++ b/typer/utils.py @@ -3,9 +3,8 @@ from copy import copy from typing import Any, Callable, Dict, List, Tuple, Type, cast -from typing_extensions import Annotated, get_type_hints +from typing_extensions import Annotated, get_args, get_origin, get_type_hints -from ._typing import get_args, get_origin from .models import ArgumentInfo, OptionInfo, ParameterInfo, ParamMeta @@ -96,7 +95,7 @@ def __str__(self) -> str: def _split_annotation_from_typer_annotations( base_annotation: Type[Any], ) -> Tuple[Type[Any], List[ParameterInfo]]: - if get_origin(base_annotation) is not Annotated: # type: ignore + if get_origin(base_annotation) is not Annotated: return base_annotation, [] base_annotation, *maybe_typer_annotations = get_args(base_annotation) return base_annotation, [ From 5889cf82f4ed925f92e6b0750bf1b1ed9ee672f3 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 28 Aug 2024 23:32:31 +0000 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index d0a1d428a7..4c9c83d2bf 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -8,6 +8,7 @@ ### Refactors +* 🔥 Remove unused functionality from `_typing.py` file. PR [#805](https://github.com/fastapi/typer/pull/805) by [@ivantodorovich](https://github.com/ivantodorovich). * ✏️ Fix typo in function name `_make_rich_text`. PR [#959](https://github.com/fastapi/typer/pull/959) by [@svlandeg](https://github.com/svlandeg). ### Internal