diff options
author | Chris Moradi <37349208+chrismoradi@users.noreply.github.com> | 2022-01-15 15:22:16 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-15 15:22:16 -0800 |
commit | c6ce6b1b287fc47264ca63c6819dcd01f294e754 (patch) | |
tree | 72cbbbcc8f785da40547b62ed3e6ee7b56889d5f | |
parent | 0c397ed24103f2bdb72684a473ccb4bdd59f6495 (diff) | |
download | typing-c6ce6b1b287fc47264ca63c6819dcd01f294e754.tar.gz |
Add is_typeddict from Python 3.10 (#1016)
-rw-r--r-- | typing_extensions/CHANGELOG | 5 | ||||
-rw-r--r-- | typing_extensions/README.rst | 1 | ||||
-rw-r--r-- | typing_extensions/src/test_typing_extensions.py | 25 | ||||
-rw-r--r-- | typing_extensions/src/typing_extensions.py | 24 |
4 files changed, 54 insertions, 1 deletions
diff --git a/typing_extensions/CHANGELOG b/typing_extensions/CHANGELOG index 24fe698..bc1c2c8 100644 --- a/typing_extensions/CHANGELOG +++ b/typing_extensions/CHANGELOG @@ -1,3 +1,8 @@ +# Release 4.x.x + +- Add `is_typeddict`. Patch by Chris Moradi (@chrismoradi) and James + Hilton-Balfe (@Gobot1234). + # Release 4.0.1 (November 30, 2021) - Fix broken sdist in release 4.0.0. Patch by Adam Turner (@AA-Turner). diff --git a/typing_extensions/README.rst b/typing_extensions/README.rst index 2dacd65..270cff4 100644 --- a/typing_extensions/README.rst +++ b/typing_extensions/README.rst @@ -49,6 +49,7 @@ This module currently contains the following: - ``ParamSpecKwargs`` (see PEP 612) - ``TypeAlias`` (see PEP 613) - ``TypeGuard`` (see PEP 647) + - ``is_typeddict`` - In ``typing`` since Python 3.9 diff --git a/typing_extensions/src/test_typing_extensions.py b/typing_extensions/src/test_typing_extensions.py index 731f973..186aa7a 100644 --- a/typing_extensions/src/test_typing_extensions.py +++ b/typing_extensions/src/test_typing_extensions.py @@ -19,7 +19,7 @@ import typing_extensions from typing_extensions import NoReturn, ClassVar, Final, IntVar, Literal, Type, NewType, TypedDict, Self from typing_extensions import TypeAlias, ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs, TypeGuard from typing_extensions import Awaitable, AsyncIterator, AsyncContextManager, Required, NotRequired -from typing_extensions import Protocol, runtime, runtime_checkable, Annotated, overload +from typing_extensions import Protocol, runtime, runtime_checkable, Annotated, overload, is_typeddict try: from typing_extensions import get_type_hints except ImportError: @@ -36,6 +36,7 @@ except ImportError: # Flags used to mark tests that only apply after a specific # version of the typing module. TYPING_3_6_1 = sys.version_info[:3] >= (3, 6, 1) +TYPING_3_8_0 = sys.version_info[:3] >= (3, 8, 0) TYPING_3_10_0 = sys.version_info[:3] >= (3, 10, 0) TYPING_3_11_0 = sys.version_info[:3] >= (3, 11, 0) @@ -1681,6 +1682,28 @@ class TypedDictTests(BaseTestCase): 'voice': str, } + def test_is_typeddict(self): + assert is_typeddict(Point2D) is True + assert is_typeddict(Point2Dor3D) is True + assert is_typeddict(Union[str, int]) is False + # classes, not instances + assert is_typeddict(Point2D()) is False + + @skipUnless(TYPING_3_8_0, "Python 3.8+ required") + def test_is_typeddict_against_typeddict_from_typing(self): + Point = typing.TypedDict('Point', {'x': int, 'y': int}) + + class PointDict2D(typing.TypedDict): + x: int + y: int + + class PointDict3D(PointDict2D, total=False): + z: int + + assert is_typeddict(Point) is True + assert is_typeddict(PointDict2D) is True + assert is_typeddict(PointDict3D) is True + class AnnotatedTests(BaseTestCase): diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index 455e319..b5a4654 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -72,6 +72,7 @@ __all__ = [ 'Annotated', 'final', 'IntVar', + 'is_typeddict', 'Literal', 'NewType', 'overload', @@ -979,6 +980,7 @@ if sys.version_info >= (3, 9, 2): # The standard library TypedDict in Python 3.9.0/1 does not honour the "total" # keyword with old-style TypedDict(). See https://bugs.python.org/issue42059 TypedDict = typing.TypedDict + _TypedDictMeta = typing._TypedDictMeta else: def _check_fails(cls, other): try: @@ -1123,6 +1125,28 @@ else: """ +if hasattr(typing, "is_typeddict"): + is_typeddict = typing.is_typeddict +else: + if hasattr(typing, "_TypedDictMeta"): + _TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta) + else: + _TYPEDDICT_TYPES = (_TypedDictMeta,) + + def is_typeddict(tp): + """Check if an annotation is a TypedDict class + + For example:: + class Film(TypedDict): + title: str + year: int + + is_typeddict(Film) # => True + is_typeddict(Union[list, str]) # => False + """ + return isinstance(tp, tuple(_TYPEDDICT_TYPES)) + + # Python 3.9+ has PEP 593 (Annotated and modified get_type_hints) if hasattr(typing, 'Annotated'): Annotated = typing.Annotated |