Skip to content

Commit 327089b

Browse files
authored
Backport get_origin() and get_args() (#698)
The implementations come from CPython commit 427c84f13f77 with one small change – the get_origin's docstring mentions Annotated as it's also supported. get_origin() and get_args() introduced in [1] and modified in [2] to support Annotated. [1] python/cpython#13685 [2] python/cpython#18260 * Define our own get_origin()/get_args() in typing_extensions on Python 3.8 Otherwise typing_extensions.get_origin() would not recognize typing_extensions.Annotated on 3.8.
1 parent a867181 commit 327089b

File tree

2 files changed

+53
-1
lines changed

2 files changed

+53
-1
lines changed

src_py3/test_typing_extensions.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1833,6 +1833,8 @@ def test_typing_extensions_defers_when_possible(self):
18331833
'Final',
18341834
'get_type_hints'
18351835
}
1836+
if sys.version_info[:2] == (3, 8):
1837+
exclude |= {'get_args', 'get_origin'}
18361838
for item in typing_extensions.__all__:
18371839
if item not in exclude and hasattr(typing, item):
18381840
self.assertIs(

src_py3/typing_extensions.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def _check_methods_in_mro(C, *methods):
151151
HAVE_ANNOTATED = PEP_560 or SUBS_TREE
152152

153153
if PEP_560:
154-
__all__.append("get_type_hints")
154+
__all__.extend(["get_args", "get_origin", "get_type_hints"])
155155

156156
if HAVE_ANNOTATED:
157157
__all__.append("Annotated")
@@ -1992,3 +1992,53 @@ class Annotated(metaclass=AnnotatedMeta):
19921992
OptimizedList = Annotated[List[T], runtime.Optimize()]
19931993
OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
19941994
"""
1995+
1996+
# Python 3.8 has get_origin() and get_args() but those implementations aren't
1997+
# Annotated-aware, so we can't use those, only Python 3.9 versions will do.
1998+
if sys.version_info[:2] >= (3, 9):
1999+
get_origin = typing.get_origin
2000+
get_args = typing.get_args
2001+
elif PEP_560:
2002+
from typing import _GenericAlias # noqa
2003+
2004+
def get_origin(tp):
2005+
"""Get the unsubscripted version of a type.
2006+
2007+
This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar
2008+
and Annotated. Return None for unsupported types. Examples::
2009+
2010+
get_origin(Literal[42]) is Literal
2011+
get_origin(int) is None
2012+
get_origin(ClassVar[int]) is ClassVar
2013+
get_origin(Generic) is Generic
2014+
get_origin(Generic[T]) is Generic
2015+
get_origin(Union[T, int]) is Union
2016+
get_origin(List[Tuple[T, T]][int]) == list
2017+
"""
2018+
if isinstance(tp, _AnnotatedAlias):
2019+
return Annotated
2020+
if isinstance(tp, _GenericAlias):
2021+
return tp.__origin__
2022+
if tp is Generic:
2023+
return Generic
2024+
return None
2025+
2026+
def get_args(tp):
2027+
"""Get type arguments with all substitutions performed.
2028+
2029+
For unions, basic simplifications used by Union constructor are performed.
2030+
Examples::
2031+
get_args(Dict[str, int]) == (str, int)
2032+
get_args(int) == ()
2033+
get_args(Union[int, Union[T, int], str][int]) == (int, str)
2034+
get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int])
2035+
get_args(Callable[[], T][int]) == ([], int)
2036+
"""
2037+
if isinstance(tp, _AnnotatedAlias):
2038+
return (tp.__origin__,) + tp.__metadata__
2039+
if isinstance(tp, _GenericAlias):
2040+
res = tp.__args__
2041+
if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis:
2042+
res = (list(res[:-1]), res[-1])
2043+
return res
2044+
return ()

0 commit comments

Comments
 (0)