aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Moradi <37349208+chrismoradi@users.noreply.github.com>2022-01-15 15:22:16 -0800
committerGitHub <noreply@github.com>2022-01-15 15:22:16 -0800
commitc6ce6b1b287fc47264ca63c6819dcd01f294e754 (patch)
tree72cbbbcc8f785da40547b62ed3e6ee7b56889d5f
parent0c397ed24103f2bdb72684a473ccb4bdd59f6495 (diff)
downloadtyping-c6ce6b1b287fc47264ca63c6819dcd01f294e754.tar.gz
Add is_typeddict from Python 3.10 (#1016)
-rw-r--r--typing_extensions/CHANGELOG5
-rw-r--r--typing_extensions/README.rst1
-rw-r--r--typing_extensions/src/test_typing_extensions.py25
-rw-r--r--typing_extensions/src/typing_extensions.py24
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