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
|
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):
|
||||||
|
|
Loading…
Reference in New Issue