[Python-checkins] bpo-40187: Refactor typing.TypedDict. (GH-19372)
Serhiy Storchaka
webhook-mailer at python.org
Wed Apr 8 04:03:32 EDT 2020
https://github.com/python/cpython/commit/f228bf2300a9d3bf833b1a89336581822e864ae5
commit: f228bf2300a9d3bf833b1a89336581822e864ae5
branch: master
author: Serhiy Storchaka <storchaka at gmail.com>
committer: GitHub <noreply at github.com>
date: 2020-04-08T11:03:27+03:00
summary:
bpo-40187: Refactor typing.TypedDict. (GH-19372)
files:
M Lib/typing.py
diff --git a/Lib/typing.py b/Lib/typing.py
index bcb2233ee50cf..9cacaa840ca35 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -1792,44 +1792,20 @@ def _namedtuple_mro_entries(bases):
NamedTuple.__mro_entries__ = _namedtuple_mro_entries
-def _dict_new(cls, /, *args, **kwargs):
- return dict(*args, **kwargs)
-
-
-def _typeddict_new(cls, typename, fields=None, /, *, total=True, **kwargs):
- if fields is None:
- fields = kwargs
- elif kwargs:
- raise TypeError("TypedDict takes either a dict or keyword arguments,"
- " but not both")
-
- ns = {'__annotations__': dict(fields), '__total__': total}
- try:
- # Setting correct module is necessary to make typed dict classes pickleable.
- ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__')
- except (AttributeError, ValueError):
- pass
-
- return _TypedDictMeta(typename, (), ns)
-
-
-def _check_fails(cls, other):
- # Typed dicts are only for static structural subtyping.
- raise TypeError('TypedDict does not support instance and class checks')
-
-
class _TypedDictMeta(type):
def __new__(cls, name, bases, ns, total=True):
"""Create new typed dict class object.
- This method is called directly when TypedDict is subclassed,
- or via _typeddict_new when TypedDict is instantiated. This way
+ This method is called when TypedDict is subclassed,
+ or when TypedDict is instantiated. This way
TypedDict supports all three syntax forms described in its docstring.
- Subclasses and instances of TypedDict return actual dictionaries
- via _dict_new.
+ Subclasses and instances of TypedDict return actual dictionaries.
"""
- ns['__new__'] = _typeddict_new if name == 'TypedDict' else _dict_new
- tp_dict = super(_TypedDictMeta, cls).__new__(cls, name, (dict,), ns)
+ for base in bases:
+ if type(base) is not _TypedDictMeta:
+ raise TypeError('cannot inherit from both a TypedDict type '
+ 'and a non-TypedDict base class')
+ tp_dict = type.__new__(_TypedDictMeta, name, (dict,), ns)
annotations = {}
own_annotations = ns.get('__annotations__', {})
@@ -1859,10 +1835,16 @@ def __new__(cls, name, bases, ns, total=True):
tp_dict.__total__ = total
return tp_dict
- __instancecheck__ = __subclasscheck__ = _check_fails
+ __call__ = dict # static method
+
+ def __subclasscheck__(cls, other):
+ # Typed dicts are only for static structural subtyping.
+ raise TypeError('TypedDict does not support instance and class checks')
+ __instancecheck__ = __subclasscheck__
-class TypedDict(dict, metaclass=_TypedDictMeta):
+
+def TypedDict(typename, fields=None, /, *, total=True, **kwargs):
"""A simple typed namespace. At runtime it is equivalent to a plain dict.
TypedDict creates a dictionary type that expects all of its
@@ -1904,6 +1886,23 @@ class body be required.
The class syntax is only supported in Python 3.6+, while two other
syntax forms work for Python 2.7 and 3.2+
"""
+ if fields is None:
+ fields = kwargs
+ elif kwargs:
+ raise TypeError("TypedDict takes either a dict or keyword arguments,"
+ " but not both")
+
+ ns = {'__annotations__': dict(fields), '__total__': total}
+ try:
+ # Setting correct module is necessary to make typed dict classes pickleable.
+ ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__')
+ except (AttributeError, ValueError):
+ pass
+
+ return _TypedDictMeta(typename, (), ns)
+
+_TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {})
+TypedDict.__mro_entries__ = lambda bases: (_TypedDict,)
def NewType(name, tp):
More information about the Python-checkins
mailing list