bpo-40187: Refactor typing.TypedDict. (GH-19372)
This commit is contained in:
parent
a2ec06938f
commit
f228bf2300
|
@ -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 @@ class _TypedDictMeta(type):
|
|||
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 TypedDict(dict, metaclass=_TypedDictMeta):
|
|||
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):
|
||||
|
|
Loading…
Reference in New Issue