"""Kind of typing_extensions for this package""" __all__ = ["get_args", "get_origin", "get_type_hints"] import sys from types import ModuleType, new_class from typing import Any, Callable, Dict, Generic, Protocol, TypeVar, Union class _FakeType: pass if sys.version_info >= (3, 9): # pragma: no cover from typing import Annotated, get_args, get_origin, get_type_hints else: # pragma: no cover try: from typing_extensions import Annotated except ImportError: pass try: from typing_extensions import get_type_hints as gth except ImportError: from typing import get_type_hints as _gth def gth(obj, globalns=None, localns=None, include_extras=False): return _gth(obj, globalns, localns) def get_type_hints(obj, globalns=None, localns=None, include_extras=False): # TODO This has been fixed in recent 3.7 and 3.8 # fix https://bugs.python.org/issue37838 if not isinstance(obj, (type, ModuleType)) and globalns is None: nsobj = obj while hasattr(nsobj, "__wrapped__"): nsobj = nsobj.__wrapped__ globalns = getattr(nsobj, "__globals__", None) localns = {"unicode": str, **(localns or {})} return gth(obj, globalns, localns, include_extras) try: from typing_extensions import get_args, get_origin except ImportError: def get_origin(tp): if isinstance(tp, _AnnotatedAlias): return None if tp.__args__ is None else Annotated if tp is Generic: return Generic return getattr(tp, "__origin__", None) def get_args(tp): if isinstance(tp, _AnnotatedAlias): return () if tp.__args__ is None else (tp.__args__[0], *tp.__metadata__) res = getattr(tp, "__args__", ()) if get_origin(tp) is Callable and res[0] is not Ellipsis: res = (list(res[:-1]), res[-1]) return res if sys.version_info >= (3, 13): from typing import _collect_type_parameters elif sys.version_info >= (3, 11): from typing import _collect_parameters as _collect_type_parameters # type: ignore else: from typing import _collect_type_vars as _collect_type_parameters def _generic_mro(result, tp): origin = get_origin(tp) if origin is None: origin = tp result[origin] = tp if hasattr(origin, "__orig_bases__"): parameters = _collect_type_parameters(origin.__orig_bases__) substitution = dict(zip(parameters, get_args(tp))) for base in origin.__orig_bases__: if get_origin(base) in result: continue base_parameters = getattr(base, "__parameters__", ()) if base_parameters: base = base[tuple(substitution.get(p, p) for p in base_parameters)] _generic_mro(result, base) # sentinel value to avoid to subscript Generic and Protocol BASE_GENERIC_MRO = {Generic: Generic, Protocol: Protocol} def generic_mro(tp): origin = get_origin(tp) if origin is None and not hasattr(tp, "__orig_bases__"): if not isinstance(tp, type): raise TypeError(f"{tp!r} is not a type or a generic alias") return tp.__mro__ result = BASE_GENERIC_MRO.copy() _generic_mro(result, tp) cls = origin if origin is not None else tp return tuple(result.get(sub_cls, sub_cls) for sub_cls in cls.__mro__) def resolve_type_hints(obj: Any) -> Dict[str, Any]: """Wrap get_type_hints to resolve type vars in case of generic inheritance. `obj` can also be a parametrized generic class.""" origin_or_obj = get_origin(obj) or obj if isinstance(origin_or_obj, type): hints = {} for base in reversed(generic_mro(obj)): 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") _GenericAlias: Any = type(Generic[_T]) try: _AnnotatedAlias: Any = type(Annotated[_T, ...]) except NameError: _AnnotatedAlias = _FakeType def is_new_type(tp: Any) -> bool: return hasattr(tp, "__supertype__") def is_annotated(tp: Any) -> bool: try: from typing import Annotated return get_origin(tp) == Annotated except ImportError: try: from typing_extensions import Annotated # type: ignore return get_origin(tp) == Annotated except ImportError: return False def is_literal(tp: Any) -> bool: from typing import Literal origin = get_origin(tp) if origin is Literal: return True try: from typing_extensions import Literal as Literal2 return get_origin(tp) is Literal2 except ImportError: return False def is_literal_string(tp: Any) -> bool: try: from typing import LiteralString if tp is LiteralString: return True except ImportError: pass try: from typing_extensions import LiteralString return tp is LiteralString except ImportError: return False def is_named_tuple(tp: Any) -> bool: return issubclass(tp, tuple) and hasattr(tp, "_fields") def is_typed_dict(tp: Any) -> bool: # TODO use python 3.10 typing.is_typeddict from typing import TypedDict if isinstance(tp, type(new_class("_TypedDictImplem", (TypedDict,)))): return True try: from typing_extensions import TypedDict as TypedDict2 return isinstance(tp, type(new_class("_TypedDictImplem", (TypedDict2,)))) except ImportError: return False def is_type_var(tp: Any) -> bool: return isinstance(tp, TypeVar) # py38 get_origin of builtin wrapped generics return the unsubscriptable builtin # type. if sys.version_info < (3, 9): import typing TYPING_ALIASES = { getattr(elt, "__origin__", None): elt for elt in typing.__dict__.values() } def typing_origin(origin: Any) -> Any: return TYPING_ALIASES.get(origin, origin) else: typing_origin = lambda tp: tp def is_type(tp: Any) -> bool: """isinstance is not enough because in py39: isinstance(list[int], type) == True""" return isinstance(tp, type) and not get_args(tp) def is_union(tp: Any) -> bool: try: from types import UnionType return tp in (UnionType, Union) except ImportError: return tp is Union