From d901a1f94b2bc86f57092543100af7b0185fc751 Mon Sep 17 00:00:00 2001 From: Mauricio Villegas <5780272+mauvilsa@users.noreply.github.com> Date: Wed, 11 Sep 2024 08:10:21 +0200 Subject: [PATCH 1/5] Add support for python 3.13 (#554) --- .circleci/config.yml | 9 ++++++++- .github/workflows/tests.yml | 2 +- CHANGELOG.rst | 6 +++++- jsonargparse_tests/test_formatters.py | 2 +- jsonargparse_tests/test_stubs_resolver.py | 2 +- pyproject.toml | 6 ++++-- 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2a80bb9c..04f21ef9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -44,6 +44,10 @@ jobs: root: . paths: - ./coverage_*.xml + # test-py313: + # <<: *test-py38 + # docker: + # - image: cimg/python:3.13 test-py312: <<: *test-py38 docker: @@ -101,7 +105,7 @@ jobs: command: | curl -Os https://uploader.codecov.io/latest/linux/codecov chmod +x codecov - for py in 3.7 3.8 3.9 3.10 3.11 3.12; do + for py in 3.7 3.8 3.9 3.10 3.11 3.12 3.13; do for suffix in "" _all _types _pydantic1 _pydantic2; do if [ -f coverage_py${py}${suffix}.xml ]; then ./codecov \ @@ -152,6 +156,8 @@ workflows: only: /^v\d+\.\d+\.\d+.*$/ - test-py38: <<: *buildreq + # - test-py313: + # <<: *buildreq - test-py312: <<: *buildreq - test-py311: @@ -166,6 +172,7 @@ workflows: <<: *buildreq - codecov: requires: + # - test-py313 - test-py312 - test-py311 - test-py310 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index db6793fe..ce6cff56 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - python: [3.7, 3.8, 3.9, "3.10", 3.11, 3.12] + python: [3.7, 3.8, 3.9, "3.10", 3.11, 3.12, 3.13] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b9ab9ff3..1e947a28 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -12,9 +12,13 @@ The semantic versioning only considers the public API as described in paths are considered internals and can change in minor and patch releases. -v4.32.2 (2024-09-??) +v4.33.0 (2024-09-??) -------------------- +Added +^^^^^ +- Support for Python 3.13. + Fixed ^^^^^ - Callable type with subclass return not showing the ``--*.help`` option (`#567 diff --git a/jsonargparse_tests/test_formatters.py b/jsonargparse_tests/test_formatters.py index 5720062c..df729751 100644 --- a/jsonargparse_tests/test_formatters.py +++ b/jsonargparse_tests/test_formatters.py @@ -26,7 +26,7 @@ def test_help_action_config_file(parser): parser.add_argument("-c", "--cfg", help="Config in yaml/json.", action="config") help_str = get_parser_help(parser) assert "ARG: --print_config" in help_str - assert "ARG: -c CFG, --cfg CFG" in help_str + assert "ARG: -c CFG, --cfg CFG" in help_str or "ARG: -c, --cfg CFG" in help_str assert "ENV: APP_CFG" in help_str assert "Config in yaml/json." in help_str assert "APP_PRINT_CONFIG" not in help_str diff --git a/jsonargparse_tests/test_stubs_resolver.py b/jsonargparse_tests/test_stubs_resolver.py index d83c222f..e3269485 100644 --- a/jsonargparse_tests/test_stubs_resolver.py +++ b/jsonargparse_tests/test_stubs_resolver.py @@ -150,7 +150,7 @@ def test_get_params_classmethod(): expected = expected[:4] + ["compresslevel"] + expected[4:] assert expected == get_param_names(params)[: len(expected)] if sys.version_info >= (3, 10): - assert all(p.annotation is not inspect._empty for p in params if p.name != "compresslevel") + assert all(p.annotation is not inspect._empty for p in params if p.name not in {"compresslevel", "stream"}) with mock_typeshed_client_unavailable(): params = get_params(TarFile, "open") assert expected == get_param_names(params)[: len(expected)] diff --git a/pyproject.toml b/pyproject.toml index 1eb018ab..3a25638f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX :: Linux", @@ -56,7 +57,8 @@ jsonschema = [ ] jsonnet = [ "jsonnet>=0.13.0; os_name == 'posix'", - "jsonnet-binary>=0.17.0; os_name != 'posix'", + "jsonnet-binary>=0.17.0; os_name != 'posix' and python_version < '3.13'", + "jsonnet @ https://github.com/google/jsonnet/zipball/master ; os_name != 'posix' and python_version == '3.13'", ] urls = [ "requests>=2.18.4", @@ -190,7 +192,7 @@ Villegas = "Villegas" [tool.tox] legacy_tox_ini = """ [tox] -envlist = py{37,38,39,310,311,312}-{all,no}-extras,omegaconf +envlist = py{37,38,39,310,311,312,313}-{all,no}-extras,omegaconf skip_missing_interpreters = true [testenv] From 88c038745f68bfd56d830ad58ed42e2b1035440d Mon Sep 17 00:00:00 2001 From: Mauricio Villegas <5780272+mauvilsa@users.noreply.github.com> Date: Thu, 12 Sep 2024 08:02:01 +0200 Subject: [PATCH 2/5] Removed shtab experimental warning (#561) --- CHANGELOG.rst | 4 ++++ jsonargparse/_completions.py | 2 -- jsonargparse_tests/test_shtab.py | 6 +----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1e947a28..2348ca03 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -24,6 +24,10 @@ Fixed - Callable type with subclass return not showing the ``--*.help`` option (`#567 `__). +Changed +^^^^^^^ +- Removed shtab experimental warning. + v4.32.1 (2024-08-23) -------------------- diff --git a/jsonargparse/_completions.py b/jsonargparse/_completions.py index 000ce2e5..41222abc 100644 --- a/jsonargparse/_completions.py +++ b/jsonargparse/_completions.py @@ -3,7 +3,6 @@ import locale import os import re -import warnings from collections import defaultdict from contextlib import contextmanager, suppress from contextvars import ContextVar @@ -97,7 +96,6 @@ def __init__( def __call__(self, parser, namespace, shell, option_string=None): import shtab - warnings.warn("Automatic shtab support is experimental and subject to change.", UserWarning) prog = norm_name(parser.prog) assert prog preambles = [] diff --git a/jsonargparse_tests/test_shtab.py b/jsonargparse_tests/test_shtab.py index f8d87345..d26d052d 100644 --- a/jsonargparse_tests/test_shtab.py +++ b/jsonargparse_tests/test_shtab.py @@ -7,7 +7,6 @@ from pathlib import Path from typing import Any, Callable, Optional, Union from unittest.mock import patch -from warnings import catch_warnings import pytest @@ -45,10 +44,7 @@ def parser() -> ArgumentParser: def get_shtab_script(parser, shell): - with catch_warnings(record=True) as w: - shtab_script = get_parse_args_stdout(parser, [f"--print_shtab={shell}"]) - assert "support is experimental" in str(w[0].message) - return shtab_script + return get_parse_args_stdout(parser, [f"--print_shtab={shell}"]) def assert_bash_typehint_completions(subtests, shtab_script, completions): From 27137b55aacd98a8720ab836faf7dc9e173df212 Mon Sep 17 00:00:00 2001 From: Andrew Gardner Date: Thu, 12 Sep 2024 23:19:19 -0500 Subject: [PATCH 3/5] Fix inference of required keys in TypedDicts (#571) --------- Co-authored-by: Mauricio Villegas <5780272+mauvilsa@users.noreply.github.com> --- CHANGELOG.rst | 2 + DOCUMENTATION.rst | 6 ++- jsonargparse/_typehints.py | 55 ++++++++++++++++++++--- jsonargparse/_util.py | 2 +- jsonargparse_tests/test_typehints.py | 66 +++++++++++++++++++++++++--- 5 files changed, 115 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2348ca03..67fbc8a1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -18,6 +18,8 @@ v4.33.0 (2024-09-??) Added ^^^^^ - Support for Python 3.13. +- Support for `NotRequired` and `Required` annotations for `TypedDict` keys (`#571 + `__) Fixed ^^^^^ diff --git a/DOCUMENTATION.rst b/DOCUMENTATION.rst index dfb1bde6..d8e57b7c 100644 --- a/DOCUMENTATION.rst +++ b/DOCUMENTATION.rst @@ -401,8 +401,10 @@ Some notes about this support are: :ref:`parsing-paths` and :ref:`parsing-urls`. - ``Dict``, ``Mapping``, ``MutableMapping``, ``MappingProxyType``, - ``OrderedDict`` and ``TypedDict`` are supported but only with ``str`` or - ``int`` keys. For more details see :ref:`dict-items`. + ``OrderedDict``, and ``TypedDict`` are supported but only with ``str`` or + ``int`` keys. ``Required`` and ``NotRequired`` are also supported for + fine-grained specification of required/optional ``TypedDict`` keys. + For more details see :ref:`dict-items`. - ``Tuple``, ``Set`` and ``MutableSet`` are supported even though they can't be represented in json distinguishable from a list. Each ``Tuple`` element diff --git a/jsonargparse/_typehints.py b/jsonargparse/_typehints.py index 43dff94e..b1a20bcf 100644 --- a/jsonargparse/_typehints.py +++ b/jsonargparse/_typehints.py @@ -3,6 +3,7 @@ import inspect import os import re +import sys from argparse import ArgumentError from collections import OrderedDict, abc, defaultdict from contextlib import contextmanager, suppress @@ -88,6 +89,21 @@ Literal = typing_extensions_import("Literal") +NotRequired = typing_extensions_import("NotRequired") +Required = typing_extensions_import("Required") +TypedDict = typing_extensions_import("TypedDict") +_TypedDictMeta = typing_extensions_import("_TypedDictMeta") + + +def _capture_typing_extension_shadows(name: str, *collections) -> None: + """ + Ensure different origins for types in typing_extensions are captured. + """ + current_module = sys.modules[__name__] + typehint = getattr(current_module, name) + if getattr(typehint, "__module__", None) == "typing_extensions" and hasattr(__import__("typing"), name): + for collection in collections: + collection.add(getattr(__import__("typing"), name)) root_types = { @@ -124,6 +140,8 @@ OrderedDict, Callable, abc.Callable, + NotRequired, + Required, } leaf_types = { @@ -160,9 +178,20 @@ callable_origin_types = {Callable, abc.Callable} literal_types = {Literal} -if getattr(Literal, "__module__", None) == "typing_extensions" and hasattr(__import__("typing"), "Literal"): - root_types.add(__import__("typing").Literal) - literal_types.add(__import__("typing").Literal) +_capture_typing_extension_shadows("Literal", root_types, literal_types) + +not_required_types = {NotRequired} +_capture_typing_extension_shadows("NotRequired", root_types, not_required_types) + +required_types = {Required} +_capture_typing_extension_shadows("Required", root_types, required_types) +not_required_required_types = not_required_types.union(required_types) + +typed_dict_types = {TypedDict} +_capture_typing_extension_shadows("TypedDict", typed_dict_types) + +typed_dict_meta_types = {_TypedDictMeta} +_capture_typing_extension_shadows("_TypedDictMeta", typed_dict_meta_types) subclass_arg_parser: ContextVar = ContextVar("subclass_arg_parser") allow_default_instance: ContextVar = ContextVar("allow_default_instance", default=False) @@ -889,11 +918,18 @@ def adapt_typehints( else: kwargs["prev_val"] = None val[k] = adapt_typehints(v, subtypehints[1], **kwargs) - if get_import_path(typehint.__class__) == "typing._TypedDictMeta": + if type(typehint) in typed_dict_meta_types: if typehint.__total__: - missing_keys = typehint.__annotations__.keys() - val.keys() - if missing_keys: - raise_unexpected_value(f"Missing required keys: {missing_keys}", val) + required_keys = { + k for k, v in typehint.__annotations__.items() if get_typehint_origin(v) not in not_required_types + } + else: + required_keys = { + k for k, v in typehint.__annotations__.items() if get_typehint_origin(v) in required_types + } + missing_keys = required_keys - val.keys() + if missing_keys: + raise_unexpected_value(f"Missing required keys: {missing_keys}", val) extra_keys = val.keys() - typehint.__annotations__.keys() if extra_keys: raise_unexpected_value(f"Unexpected keys: {extra_keys}", val) @@ -904,6 +940,11 @@ def adapt_typehints( elif typehint_origin is OrderedDict: val = dict(val) if serialize else OrderedDict(val) + # TypedDict NotRequired and Required + elif typehint_origin in not_required_required_types: + assert len(subtypehints) == 1, "(Not)Required requires a single type argument" + val = adapt_typehints(val, subtypehints[0], **adapt_kwargs) + # Callable elif typehint_origin in callable_origin_types or typehint in callable_origin_types: if serialize: diff --git a/jsonargparse/_util.py b/jsonargparse/_util.py index 44e53d94..e97ea2a9 100644 --- a/jsonargparse/_util.py +++ b/jsonargparse/_util.py @@ -273,7 +273,7 @@ def get_typehint_origin(typehint): typehint_class = get_import_path(typehint.__class__) if typehint_class == "types.UnionType": return Union - if typehint_class == "typing._TypedDictMeta": + if typehint_class in {"typing._TypedDictMeta", "typing_extensions._TypedDictMeta"}: return dict return getattr(typehint, "__origin__", None) diff --git a/jsonargparse_tests/test_typehints.py b/jsonargparse_tests/test_typehints.py index c391a767..f8f2cde9 100644 --- a/jsonargparse_tests/test_typehints.py +++ b/jsonargparse_tests/test_typehints.py @@ -24,9 +24,6 @@ Type, Union, ) - -if sys.version_info >= (3, 8): - from typing import TypedDict from unittest import mock from warnings import catch_warnings @@ -37,6 +34,9 @@ from jsonargparse._typehints import ( ActionTypeHint, Literal, + NotRequired, + Required, + TypedDict, get_all_subclass_paths, get_subclass_types, is_optional, @@ -501,7 +501,7 @@ def test_mapping_nested_without_args(parser): assert {"b": {"c": 2}} == parser.parse_args(['--map={"b": {"c": 2}}']).map -@pytest.mark.skipif(sys.version_info < (3, 8), reason="TypedDict introduced in python 3.8") +@pytest.mark.skipif(not TypedDict, reason="TypedDict introduced in python 3.8 or backported in typing_extensions") def test_typeddict_without_arg(parser): parser.add_argument("--typeddict", type=TypedDict("MyDict", {})) assert {} == parser.parse_args(["--typeddict={}"])["typeddict"] @@ -513,7 +513,7 @@ def test_typeddict_without_arg(parser): ctx.match("Expected a ") -@pytest.mark.skipif(sys.version_info < (3, 8), reason="TypedDict introduced in python 3.8") +@pytest.mark.skipif(not TypedDict, reason="TypedDict introduced in python 3.8 or backported in typing_extensions") def test_typeddict_with_args(parser): parser.add_argument("--typeddict", type=TypedDict("MyDict", {"a": int})) assert {"a": 1} == parser.parse_args(["--typeddict={'a': 1}"])["typeddict"] @@ -532,7 +532,7 @@ def test_typeddict_with_args(parser): ctx.match("Expected a ") -@pytest.mark.skipif(sys.version_info < (3, 8), reason="TypedDict introduced in python 3.8") +@pytest.mark.skipif(not TypedDict, reason="TypedDict introduced in python 3.8 or backported in typing_extensions") def test_typeddict_with_args_ntotal(parser): parser.add_argument("--typeddict", type=TypedDict("MyDict", {"a": int}, total=False)) assert {"a": 1} == parser.parse_args(["--typeddict={'a': 1}"])["typeddict"] @@ -548,6 +548,60 @@ def test_typeddict_with_args_ntotal(parser): ctx.match("Expected a ") +@pytest.mark.skipif(not NotRequired, reason="NotRequired introduced in python 3.11 or backported in typing_extensions") +def test_not_required_support(parser): + assert ActionTypeHint.is_supported_typehint(NotRequired[Any]) + + +@pytest.mark.skipif(not NotRequired, reason="NotRequired introduced in python 3.11 or backported in typing_extensions") +def test_typeddict_with_not_required_arg(parser): + parser.add_argument("--typeddict", type=TypedDict("MyDict", {"a": int, "b": NotRequired[int]})) + assert {"a": 1} == parser.parse_args(["--typeddict={'a': 1}"])["typeddict"] + assert {"a": 1, "b": 2} == parser.parse_args(["--typeddict={'a': 1, 'b': 2}"])["typeddict"] + with pytest.raises(ArgumentError) as ctx: + parser.parse_args(['--typeddict={"a":1, "b":2, "c": 3}']) + ctx.match("Unexpected keys") + with pytest.raises(ArgumentError) as ctx: + parser.parse_args(['--typeddict={"b":2}']) + ctx.match("Missing required keys") + with pytest.raises(ArgumentError) as ctx: + parser.parse_args(["--typeddict={}"]) + ctx.match("Missing required keys") + with pytest.raises(ArgumentError) as ctx: + parser.parse_args(['--typeddict={"a":"x"}']) + ctx.match("Expected a ") + with pytest.raises(ArgumentError) as ctx: + parser.parse_args(['--typeddict={"a":1, "b":"x"}']) + ctx.match("Expected a ") + + +@pytest.mark.skipif(not Required, reason="Required introduced in python 3.11 or backported in typing_extensions") +def test_required_support(parser): + assert ActionTypeHint.is_supported_typehint(Required[Any]) + + +@pytest.mark.skipif(not Required, reason="Required introduced in python 3.11 or backported in typing_extensions") +def test_typeddict_with_required_arg(parser): + parser.add_argument("--typeddict", type=TypedDict("MyDict", {"a": Required[int], "b": int}, total=False)) + assert {"a": 1} == parser.parse_args(["--typeddict={'a': 1}"])["typeddict"] + assert {"a": 1, "b": 2} == parser.parse_args(["--typeddict={'a': 1, 'b': 2}"])["typeddict"] + with pytest.raises(ArgumentError) as ctx: + parser.parse_args(['--typeddict={"a":1, "b":2, "c": 3}']) + ctx.match("Unexpected keys") + with pytest.raises(ArgumentError) as ctx: + parser.parse_args(['--typeddict={"b":2}']) + ctx.match("Missing required keys") + with pytest.raises(ArgumentError) as ctx: + parser.parse_args(["--typeddict={}"]) + ctx.match("Missing required keys") + with pytest.raises(ArgumentError) as ctx: + parser.parse_args(['--typeddict={"a":"x"}']) + ctx.match("Expected a ") + with pytest.raises(ArgumentError) as ctx: + parser.parse_args(['--typeddict={"a":1, "b":"x"}']) + ctx.match("Expected a ") + + def test_mapping_proxy_type(parser): parser.add_argument("--mapping", type=MappingProxyType) cfg = parser.parse_args(['--mapping={"x":1}']) From 1bab38ae7a5fe281c62fe784fc620bbbf259f2e8 Mon Sep 17 00:00:00 2001 From: Mauricio Villegas <5780272+mauvilsa@users.noreply.github.com> Date: Fri, 13 Sep 2024 06:38:20 +0200 Subject: [PATCH 4/5] Fix jsonnet requirement for python 3.13 and the changelog (#575) --- CHANGELOG.rst | 10 ++++++---- pyproject.toml | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 67fbc8a1..e0773fa1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -17,9 +17,10 @@ v4.33.0 (2024-09-??) Added ^^^^^ -- Support for Python 3.13. -- Support for `NotRequired` and `Required` annotations for `TypedDict` keys (`#571 - `__) +- Support for Python 3.13 (`#554 + `__). +- Support for `NotRequired` and `Required` annotations for `TypedDict` keys + (`#571 `__). Fixed ^^^^^ @@ -28,7 +29,8 @@ Fixed Changed ^^^^^^^ -- Removed shtab experimental warning. +- Removed shtab experimental warning (`#561 + `__). v4.32.1 (2024-08-23) diff --git a/pyproject.toml b/pyproject.toml index 3a25638f..384a250f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,9 +56,9 @@ jsonschema = [ "jsonschema>=3.2.0", ] jsonnet = [ - "jsonnet>=0.13.0; os_name == 'posix'", + "jsonnet>=0.13.0; os_name == 'posix' and python_version < '3.13'", "jsonnet-binary>=0.17.0; os_name != 'posix' and python_version < '3.13'", - "jsonnet @ https://github.com/google/jsonnet/zipball/master ; os_name != 'posix' and python_version == '3.13'", + "jsonnet @ https://github.com/google/jsonnet/zipball/master ; python_version == '3.13'", ] urls = [ "requests>=2.18.4", From d539f425660bffe2162bdb7ffa0c18bb1893d5b2 Mon Sep 17 00:00:00 2001 From: Ethan Marx <61295922+EthanMarx@users.noreply.github.com> Date: Fri, 13 Sep 2024 12:40:01 -0400 Subject: [PATCH 5/5] Handle forward references in `Type` typehints (#576) --------- Co-authored-by: Mauricio Villegas <5780272+mauvilsa@users.noreply.github.com> --- CHANGELOG.rst | 3 +++ jsonargparse/_postponed_annotations.py | 6 +++++- .../test_postponed_annotations.py | 17 ++++++++++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e0773fa1..3aef2827 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -27,6 +27,9 @@ Fixed - Callable type with subclass return not showing the ``--*.help`` option (`#567 `__). +- Forward referenced types not compatible with `Type` typehint (`#576 + `__) + Changed ^^^^^^^ - Removed shtab experimental warning (`#561 diff --git a/jsonargparse/_postponed_annotations.py b/jsonargparse/_postponed_annotations.py index d1a5e112..5dbdffd4 100644 --- a/jsonargparse/_postponed_annotations.py +++ b/jsonargparse/_postponed_annotations.py @@ -229,6 +229,8 @@ def resolve_subtypes_forward_refs(typehint): typehint_origin = Tuple elif typehint_origin in mapping_origin_types: typehint_origin = Dict + elif typehint_origin == type: + typehint_origin = Type typehint = typehint_origin[tuple(subtypes)] except Exception as ex: if logger: @@ -240,6 +242,9 @@ def resolve_subtypes_forward_refs(typehint): def has_subtypes(typehint): typehint_origin = get_typehint_origin(typehint) + if typehint_origin is type and hasattr(typehint, "__args__"): + return True + return ( typehint_origin == Union or typehint_origin in sequence_origin_types @@ -260,7 +265,6 @@ def get_types(obj: Any, logger: Optional[logging.Logger] = None) -> dict: types = get_type_hints(obj, global_vars) except Exception as ex1: types = ex1 # type: ignore[assignment] - if isinstance(types, dict) and all(not type_requires_eval(t) for t in types.values()): return types diff --git a/jsonargparse_tests/test_postponed_annotations.py b/jsonargparse_tests/test_postponed_annotations.py index 0d6b6baf..05d7d907 100644 --- a/jsonargparse_tests/test_postponed_annotations.py +++ b/jsonargparse_tests/test_postponed_annotations.py @@ -10,7 +10,11 @@ from jsonargparse import Namespace from jsonargparse._parameter_resolvers import get_signature_parameters as get_params -from jsonargparse._postponed_annotations import TypeCheckingVisitor, evaluate_postponed_annotations, get_types +from jsonargparse._postponed_annotations import ( + TypeCheckingVisitor, + evaluate_postponed_annotations, + get_types, +) from jsonargparse.typing import Path_drw from jsonargparse_tests.conftest import capture_logs, source_unavailable @@ -267,6 +271,17 @@ def test_get_types_type_checking_tuple(): assert str(types["p1"]) == f"{tpl}[{__name__}.TypeCheckingClass1, {__name__}.TypeCheckingClass2]" +def function_type_checking_type(p1: Type["TypeCheckingClass2"]): + return p1 + + +def test_get_types_type_checking_type(): + types = get_types(function_type_checking_type) + assert list(types.keys()) == ["p1"] + tpl = "typing.Type" if sys.version_info < (3, 10) else "type" + assert str(types["p1"]) == f"{tpl}[{__name__}.TypeCheckingClass2]" + + def function_type_checking_dict(p1: Dict[str, Union[TypeCheckingClass1, "TypeCheckingClass2"]]): return p1