From 5d96378c2efd9fa1cd27e89a896dac69246d07e7 Mon Sep 17 00:00:00 2001 From: Ethan Jacob Marx Date: Fri, 13 Sep 2024 06:57:11 -0700 Subject: [PATCH 1/7] add support for `Type` to has_subtypes function --- jsonargparse/_postponed_annotations.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/jsonargparse/_postponed_annotations.py b/jsonargparse/_postponed_annotations.py index d1a5e112..5b86f0a7 100644 --- a/jsonargparse/_postponed_annotations.py +++ b/jsonargparse/_postponed_annotations.py @@ -240,6 +240,10 @@ def resolve_subtypes_forward_refs(typehint): def has_subtypes(typehint): typehint_origin = get_typehint_origin(typehint) + if typehint_origin is type: + if hasattr(typehint, "__args__"): + return True + return ( typehint_origin == Union or typehint_origin in sequence_origin_types @@ -260,7 +264,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 From 9feb3597ecbca0cc4a6d083b05efda3309c4315a Mon Sep 17 00:00:00 2001 From: Ethan Jacob Marx Date: Fri, 13 Sep 2024 06:57:59 -0700 Subject: [PATCH 2/7] add test for forward reference with `Type` --- .../test_postponed_annotations.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/jsonargparse_tests/test_postponed_annotations.py b/jsonargparse_tests/test_postponed_annotations.py index 0d6b6baf..64d10488 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"] + tpls = "typing.Type" if sys.version_info < (3, 10) else "type" + assert str(types["p1"]) == f"{tpls}[{__name__}.TypeCheckingClass2]" + + def function_type_checking_dict(p1: Dict[str, Union[TypeCheckingClass1, "TypeCheckingClass2"]]): return p1 From a5f278af54f6c1f4e10d7a5084338a03a4d6fd74 Mon Sep 17 00:00:00 2001 From: Ethan Jacob Marx Date: Fri, 13 Sep 2024 07:07:28 -0700 Subject: [PATCH 3/7] add test for forward reference with `Type` --- jsonargparse_tests/test_postponed_annotations.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jsonargparse_tests/test_postponed_annotations.py b/jsonargparse_tests/test_postponed_annotations.py index 64d10488..0d29292c 100644 --- a/jsonargparse_tests/test_postponed_annotations.py +++ b/jsonargparse_tests/test_postponed_annotations.py @@ -278,8 +278,7 @@ def function_type_checking_type(p1: Type["TypeCheckingClass2"]): def test_get_types_type_checking_type(): types = get_types(function_type_checking_type) assert list(types.keys()) == ["p1"] - tpls = "typing.Type" if sys.version_info < (3, 10) else "type" - assert str(types["p1"]) == f"{tpls}[{__name__}.TypeCheckingClass2]" + assert str(types["p1"]) == f"type[{__name__}.TypeCheckingClass2]" def function_type_checking_dict(p1: Dict[str, Union[TypeCheckingClass1, "TypeCheckingClass2"]]): From 23b2464ce3c2ec982f82d41c93be4a74ca4e9105 Mon Sep 17 00:00:00 2001 From: Ethan Jacob Marx Date: Fri, 13 Sep 2024 07:32:56 -0700 Subject: [PATCH 4/7] handle string representation of Type in tests depdending on version --- jsonargparse_tests/test_postponed_annotations.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jsonargparse_tests/test_postponed_annotations.py b/jsonargparse_tests/test_postponed_annotations.py index 0d29292c..ddff147d 100644 --- a/jsonargparse_tests/test_postponed_annotations.py +++ b/jsonargparse_tests/test_postponed_annotations.py @@ -278,7 +278,8 @@ def function_type_checking_type(p1: Type["TypeCheckingClass2"]): def test_get_types_type_checking_type(): types = get_types(function_type_checking_type) assert list(types.keys()) == ["p1"] - assert str(types["p1"]) == f"type[{__name__}.TypeCheckingClass2]" + tpl = "typing.Type" if sys.version_info < (3, 9) else "type" + assert str(types["p1"]) == f"{tpl}[{__name__}.TypeCheckingClass2]" def function_type_checking_dict(p1: Dict[str, Union[TypeCheckingClass1, "TypeCheckingClass2"]]): From ae6b1d8fbe758133ccbc1aafa71b245a0aac90b4 Mon Sep 17 00:00:00 2001 From: Ethan Jacob Marx Date: Fri, 13 Sep 2024 08:26:52 -0700 Subject: [PATCH 5/7] make forward reference resolver for Type python version dependent --- jsonargparse/_postponed_annotations.py | 2 ++ jsonargparse_tests/test_postponed_annotations.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/jsonargparse/_postponed_annotations.py b/jsonargparse/_postponed_annotations.py index 5b86f0a7..a76d245a 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: diff --git a/jsonargparse_tests/test_postponed_annotations.py b/jsonargparse_tests/test_postponed_annotations.py index ddff147d..05d7d907 100644 --- a/jsonargparse_tests/test_postponed_annotations.py +++ b/jsonargparse_tests/test_postponed_annotations.py @@ -278,7 +278,7 @@ def function_type_checking_type(p1: Type["TypeCheckingClass2"]): 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, 9) else "type" + tpl = "typing.Type" if sys.version_info < (3, 10) else "type" assert str(types["p1"]) == f"{tpl}[{__name__}.TypeCheckingClass2]" From 988c41baad8a474f1d6e032f59feec2f50b27642 Mon Sep 17 00:00:00 2001 From: Ethan Jacob Marx Date: Fri, 13 Sep 2024 08:35:21 -0700 Subject: [PATCH 6/7] update change log --- CHANGELOG.rst | 3 +++ 1 file changed, 3 insertions(+) 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 From abc8ae5475416ae590bfaf5ae1460a8e5c2dc962 Mon Sep 17 00:00:00 2001 From: Ethan Marx <61295922+EthanMarx@users.noreply.github.com> Date: Fri, 13 Sep 2024 12:18:16 -0400 Subject: [PATCH 7/7] address sonarcloud suggestion Co-authored-by: Mauricio Villegas <5780272+mauvilsa@users.noreply.github.com> --- jsonargparse/_postponed_annotations.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/jsonargparse/_postponed_annotations.py b/jsonargparse/_postponed_annotations.py index a76d245a..5dbdffd4 100644 --- a/jsonargparse/_postponed_annotations.py +++ b/jsonargparse/_postponed_annotations.py @@ -242,9 +242,8 @@ def resolve_subtypes_forward_refs(typehint): def has_subtypes(typehint): typehint_origin = get_typehint_origin(typehint) - if typehint_origin is type: - if hasattr(typehint, "__args__"): - return True + if typehint_origin is type and hasattr(typehint, "__args__"): + return True return ( typehint_origin == Union