bpo-40187: Refactor typing.TypedDict. (GH-19372)

This commit is contained in:
Serhiy Storchaka 2020-04-08 11:03:27 +03:00 committed by GitHub
parent a2ec06938f
commit f228bf2300
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 33 additions and 34 deletions

View File

@ -1792,44 +1792,20 @@ def _namedtuple_mro_entries(bases):
NamedTuple.__mro_entries__ = _namedtuple_mro_entries 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): class _TypedDictMeta(type):
def __new__(cls, name, bases, ns, total=True): def __new__(cls, name, bases, ns, total=True):
"""Create new typed dict class object. """Create new typed dict class object.
This method is called directly when TypedDict is subclassed, This method is called when TypedDict is subclassed,
or via _typeddict_new when TypedDict is instantiated. This way or when TypedDict is instantiated. This way
TypedDict supports all three syntax forms described in its docstring. TypedDict supports all three syntax forms described in its docstring.
Subclasses and instances of TypedDict return actual dictionaries Subclasses and instances of TypedDict return actual dictionaries.
via _dict_new.
""" """
ns['__new__'] = _typeddict_new if name == 'TypedDict' else _dict_new for base in bases:
tp_dict = super(_TypedDictMeta, cls).__new__(cls, name, (dict,), ns) 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 = {} annotations = {}
own_annotations = ns.get('__annotations__', {}) own_annotations = ns.get('__annotations__', {})
@ -1859,10 +1835,16 @@ class _TypedDictMeta(type):
tp_dict.__total__ = total tp_dict.__total__ = total
return tp_dict 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. """A simple typed namespace. At runtime it is equivalent to a plain dict.
TypedDict creates a dictionary type that expects all of its TypedDict creates a dictionary type that expects all of its
@ -1904,6 +1886,23 @@ class TypedDict(dict, metaclass=_TypedDictMeta):
The class syntax is only supported in Python 3.6+, while two other The class syntax is only supported in Python 3.6+, while two other
syntax forms work for Python 2.7 and 3.2+ 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): def NewType(name, tp):