From 40ac98d191892ce4e040bfc8fc002b6b7f360a64 Mon Sep 17 00:00:00 2001 From: Joseph Perez Date: Tue, 21 Dec 2021 01:58:26 +0100 Subject: [PATCH 1/2] Cherry pick #283 --- apischema/typing.py | 41 ++++++++++--------- apischema/utils.py | 4 +- .../test_unsupported_union_member.py | 2 +- tests/test_typing.py | 9 +++- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/apischema/typing.py b/apischema/typing.py index c5233577..6e9c763e 100644 --- a/apischema/typing.py +++ b/apischema/typing.py @@ -158,27 +158,30 @@ def resolve_type_hints(obj: Any) -> Dict[str, Any]: `obj` can also be a parametrized generic class.""" origin_or_obj = get_origin(obj) or obj - hints = get_type_hints(origin_or_obj, include_extras=True) if isinstance(origin_or_obj, type): + hints = {} for base in reversed(generic_mro(obj)): - base_origin = get_origin(base) - if base_origin is not None and getattr(base_origin, "__parameters__", ()): # type: ignore - substitution = dict(zip(base_origin.__parameters__, get_args(base))) - base_annotations = getattr(base_origin, "__dict__", {}).get( - "__annotations__", {} - ) - for name, hint in get_type_hints(base_origin).items(): - if name not in base_annotations: - continue - if isinstance(hint, TypeVar): - hints[name] = substitution.get(hint, hint) - elif getattr(hint, "__parameters__", ()): - hints[name] = hint[ - tuple(substitution.get(p, p) for p in hint.__parameters__) - ] - else: - hints[name] = hint - return hints + base_origin = get_origin(base) or base + base_annotations = getattr(base_origin, "__dict__", {}).get( + "__annotations__", {} + ) + substitution = dict( + zip(getattr(base_origin, "__parameters__", ()), get_args(base)) + ) + for name, hint in get_type_hints(base_origin, include_extras=True).items(): + if name not in base_annotations: + continue + if isinstance(hint, TypeVar): + hints[name] = substitution.get(hint, hint) + elif getattr(hint, "__parameters__", ()): + hints[name] = (Union if is_union(hint) else hint)[ + tuple(substitution.get(p, p) for p in hint.__parameters__) + ] + else: + hints[name] = hint + return hints + else: + return get_type_hints(obj, include_extras=True) _T = TypeVar("_T") diff --git a/apischema/utils.py b/apischema/utils.py index b34919ab..33b187c8 100644 --- a/apischema/utils.py +++ b/apischema/utils.py @@ -171,7 +171,9 @@ def substitute_type_vars(tp: AnyType, substitution: Mapping[TV, AnyType]) -> Any except KeyError: return Union[tp.__constraints__] if tp.__constraints__ else Any elif getattr(tp, "__parameters__", ()): - return tp[tuple(substitution.get(p, p) for p in tp.__parameters__)] + return (Union if is_union(tp) else tp)[ + tuple(substitution.get(p, p) for p in tp.__parameters__) + ] else: return tp diff --git a/tests/integration/test_unsupported_union_member.py b/tests/integration/test_unsupported_union_member.py index 7d4c0fb7..7ea96940 100644 --- a/tests/integration/test_unsupported_union_member.py +++ b/tests/integration/test_unsupported_union_member.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Annotated, Union +from typing import Annotated, Union # type: ignore from pytest import raises diff --git a/tests/test_typing.py b/tests/test_typing.py index 02489f11..0497620e 100644 --- a/tests/test_typing.py +++ b/tests/test_typing.py @@ -9,6 +9,7 @@ required_keys, resolve_type_hints, ) +from apischema.typing import Annotated T = TypeVar("T") U = TypeVar("U") @@ -28,7 +29,7 @@ class C(B[str]): class D(C): - pass + d: Annotated[int, ""] test_cases = [ @@ -39,7 +40,11 @@ class D(C): (B[U], [B[U], A[int, U]], {"t": int, "u": U, "v": U}), (B[str], [B[str], A[int, str]], {"t": int, "u": str, "v": str}), (C, [C, B[str], A[int, str]], {"t": int, "u": str, "v": str}), - (D, [D, C, B[str], A[int, str]], {"t": int, "u": str, "v": str}), + ( + D, + [D, C, B[str], A[int, str]], + {"t": int, "u": str, "v": str, "d": Annotated[int, ""]}, + ), ] From 89258a4219c38c24a1218502b7ac2695a5049f61 Mon Sep 17 00:00:00 2001 From: Joseph Perez Date: Tue, 21 Dec 2021 02:01:36 +0100 Subject: [PATCH 2/2] bump patch version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index bd3be359..8feb947c 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name="apischema", - version="0.16.5", + version="0.16.6", url="https://github.com/wyfo/apischema", author="Joseph Perez", author_email="joperez@hotmail.fr",