Skip to content

Commit f228bf2

Browse files
bpo-40187: Refactor typing.TypedDict. (GH-19372)
1 parent a2ec069 commit f228bf2

File tree

1 file changed

+33
-34
lines changed

1 file changed

+33
-34
lines changed

Lib/typing.py

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1792,44 +1792,20 @@ def _namedtuple_mro_entries(bases):
17921792
NamedTuple.__mro_entries__ = _namedtuple_mro_entries
17931793

17941794

1795-
def _dict_new(cls, /, *args, **kwargs):
1796-
return dict(*args, **kwargs)
1797-
1798-
1799-
def _typeddict_new(cls, typename, fields=None, /, *, total=True, **kwargs):
1800-
if fields is None:
1801-
fields = kwargs
1802-
elif kwargs:
1803-
raise TypeError("TypedDict takes either a dict or keyword arguments,"
1804-
" but not both")
1805-
1806-
ns = {'__annotations__': dict(fields), '__total__': total}
1807-
try:
1808-
# Setting correct module is necessary to make typed dict classes pickleable.
1809-
ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__')
1810-
except (AttributeError, ValueError):
1811-
pass
1812-
1813-
return _TypedDictMeta(typename, (), ns)
1814-
1815-
1816-
def _check_fails(cls, other):
1817-
# Typed dicts are only for static structural subtyping.
1818-
raise TypeError('TypedDict does not support instance and class checks')
1819-
1820-
18211795
class _TypedDictMeta(type):
18221796
def __new__(cls, name, bases, ns, total=True):
18231797
"""Create new typed dict class object.
18241798
1825-
This method is called directly when TypedDict is subclassed,
1826-
or via _typeddict_new when TypedDict is instantiated. This way
1799+
This method is called when TypedDict is subclassed,
1800+
or when TypedDict is instantiated. This way
18271801
TypedDict supports all three syntax forms described in its docstring.
1828-
Subclasses and instances of TypedDict return actual dictionaries
1829-
via _dict_new.
1802+
Subclasses and instances of TypedDict return actual dictionaries.
18301803
"""
1831-
ns['__new__'] = _typeddict_new if name == 'TypedDict' else _dict_new
1832-
tp_dict = super(_TypedDictMeta, cls).__new__(cls, name, (dict,), ns)
1804+
for base in bases:
1805+
if type(base) is not _TypedDictMeta:
1806+
raise TypeError('cannot inherit from both a TypedDict type '
1807+
'and a non-TypedDict base class')
1808+
tp_dict = type.__new__(_TypedDictMeta, name, (dict,), ns)
18331809

18341810
annotations = {}
18351811
own_annotations = ns.get('__annotations__', {})
@@ -1859,10 +1835,16 @@ def __new__(cls, name, bases, ns, total=True):
18591835
tp_dict.__total__ = total
18601836
return tp_dict
18611837

1862-
__instancecheck__ = __subclasscheck__ = _check_fails
1838+
__call__ = dict # static method
1839+
1840+
def __subclasscheck__(cls, other):
1841+
# Typed dicts are only for static structural subtyping.
1842+
raise TypeError('TypedDict does not support instance and class checks')
18631843

1844+
__instancecheck__ = __subclasscheck__
18641845

1865-
class TypedDict(dict, metaclass=_TypedDictMeta):
1846+
1847+
def TypedDict(typename, fields=None, /, *, total=True, **kwargs):
18661848
"""A simple typed namespace. At runtime it is equivalent to a plain dict.
18671849
18681850
TypedDict creates a dictionary type that expects all of its
@@ -1904,6 +1886,23 @@ class body be required.
19041886
The class syntax is only supported in Python 3.6+, while two other
19051887
syntax forms work for Python 2.7 and 3.2+
19061888
"""
1889+
if fields is None:
1890+
fields = kwargs
1891+
elif kwargs:
1892+
raise TypeError("TypedDict takes either a dict or keyword arguments,"
1893+
" but not both")
1894+
1895+
ns = {'__annotations__': dict(fields), '__total__': total}
1896+
try:
1897+
# Setting correct module is necessary to make typed dict classes pickleable.
1898+
ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__')
1899+
except (AttributeError, ValueError):
1900+
pass
1901+
1902+
return _TypedDictMeta(typename, (), ns)
1903+
1904+
_TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {})
1905+
TypedDict.__mro_entries__ = lambda bases: (_TypedDict,)
19071906

19081907

19091908
def NewType(name, tp):

0 commit comments

Comments
 (0)