diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2019-09-17 18:22:00 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-09-17 18:22:00 (GMT) |
commit | 2bf31ccab3d17f3f35b42dca97f99576dfe2fc7d (patch) | |
tree | f92bdc27f3280f735b5d986bc30ef1e97e65430e /Lib/typing.py | |
parent | b57481318e3e3cbacd398b898f9849ec8f2d7eec (diff) | |
download | cpython-2bf31ccab3d17f3f35b42dca97f99576dfe2fc7d.zip cpython-2bf31ccab3d17f3f35b42dca97f99576dfe2fc7d.tar.gz cpython-2bf31ccab3d17f3f35b42dca97f99576dfe2fc7d.tar.bz2 |
bpo-38191: Accept arbitrary keyword names in NamedTuple() and TypedDict(). (GH-16222)
This includes such names as "cls", "self", "typename", "_typename",
"fields" and "_fields".
Passing positional arguments by keyword is deprecated.
Diffstat (limited to 'Lib/typing.py')
-rw-r--r-- | Lib/typing.py | 81 |
1 files changed, 71 insertions, 10 deletions
diff --git a/Lib/typing.py b/Lib/typing.py index 3201133..43486a7 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1653,35 +1653,96 @@ class NamedTuple(metaclass=NamedTupleMeta): """ _root = True - def __new__(self, typename, fields=None, **kwargs): + def __new__(*args, **kwargs): + if not args: + raise TypeError('NamedTuple.__new__(): not enough arguments') + cls, *args = args # allow the "cls" keyword be passed + if args: + typename, *args = args # allow the "typename" keyword be passed + elif 'typename' in kwargs: + typename = kwargs.pop('typename') + import warnings + warnings.warn("Passing 'typename' as keyword argument is deprecated", + DeprecationWarning, stacklevel=2) + else: + raise TypeError("NamedTuple.__new__() missing 1 required positional " + "argument: 'typename'") + if args: + try: + fields, = args # allow the "fields" keyword be passed + except ValueError: + raise TypeError(f'NamedTuple.__new__() takes from 2 to 3 ' + f'positional arguments but {len(args) + 2} ' + f'were given') from None + elif 'fields' in kwargs and len(kwargs) == 1: + fields = kwargs.pop('fields') + import warnings + warnings.warn("Passing 'fields' as keyword argument is deprecated", + DeprecationWarning, stacklevel=2) + else: + fields = None + if fields is None: fields = kwargs.items() elif kwargs: raise TypeError("Either list of fields or keywords" " can be provided to NamedTuple, not both") return _make_nmtuple(typename, fields) + __new__.__text_signature__ = '($cls, typename, fields=None, /, **kwargs)' -def _dict_new(cls, *args, **kwargs): +def _dict_new(*args, **kwargs): + if not args: + raise TypeError('TypedDict.__new__(): not enough arguments') + cls, *args = args # allow the "cls" keyword be passed return dict(*args, **kwargs) - - -def _typeddict_new(cls, _typename, _fields=None, **kwargs): - total = kwargs.pop('total', True) - if _fields is None: - _fields = kwargs +_dict_new.__text_signature__ = '($cls, _typename, _fields=None, /, **kwargs)' + + +def _typeddict_new(*args, total=True, **kwargs): + if not args: + raise TypeError('TypedDict.__new__(): not enough arguments') + cls, *args = args # allow the "cls" keyword be passed + if args: + typename, *args = args # allow the "_typename" keyword be passed + elif '_typename' in kwargs: + typename = kwargs.pop('_typename') + import warnings + warnings.warn("Passing '_typename' as keyword argument is deprecated", + DeprecationWarning, stacklevel=2) + else: + raise TypeError("TypedDict.__new__() missing 1 required positional " + "argument: '_typename'") + if args: + try: + fields, = args # allow the "_fields" keyword be passed + except ValueError: + raise TypeError(f'TypedDict.__new__() takes from 2 to 3 ' + f'positional arguments but {len(args) + 2} ' + f'were given') from None + elif '_fields' in kwargs and len(kwargs) == 1: + fields = kwargs.pop('_fields') + import warnings + warnings.warn("Passing '_fields' as keyword argument is deprecated", + DeprecationWarning, stacklevel=2) + else: + fields = None + + 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} + 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) + return _TypedDictMeta(typename, (), ns) +_typeddict_new.__text_signature__ = '($cls, _typename, _fields=None, /, *, total=True, **kwargs)' def _check_fails(cls, other): |