summaryrefslogtreecommitdiffstats
path: root/Lib/typing.py
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2019-09-17 18:22:00 (GMT)
committerGitHub <noreply@github.com>2019-09-17 18:22:00 (GMT)
commit2bf31ccab3d17f3f35b42dca97f99576dfe2fc7d (patch)
treef92bdc27f3280f735b5d986bc30ef1e97e65430e /Lib/typing.py
parentb57481318e3e3cbacd398b898f9849ec8f2d7eec (diff)
downloadcpython-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.py81
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):