mirror of https://github.com/python/cpython.git
gh-133817: remove keyword arguments syntax for `NamedTuple` (#133822)
This commit is contained in:
parent
92337f666e
commit
c5e1775825
|
@ -121,10 +121,14 @@ Deprecated
|
||||||
Removed
|
Removed
|
||||||
=======
|
=======
|
||||||
|
|
||||||
module_name
|
typing
|
||||||
-----------
|
------
|
||||||
|
|
||||||
* TODO
|
* The undocumented keyword argument syntax for creating
|
||||||
|
:class:`~typing.NamedTuple` classes (for example,
|
||||||
|
``Point = NamedTuple("Point", x=int, y=int)``).
|
||||||
|
Use the class-based syntax or the functional syntax instead.
|
||||||
|
(Contributed by Bénédikt Tran in :gh:`133817`.)
|
||||||
|
|
||||||
|
|
||||||
Porting to Python 3.15
|
Porting to Python 3.15
|
||||||
|
|
|
@ -8080,78 +8080,13 @@ class Group(NamedTuple):
|
||||||
self.assertIs(type(a), Group)
|
self.assertIs(type(a), Group)
|
||||||
self.assertEqual(a, (1, [2]))
|
self.assertEqual(a, (1, [2]))
|
||||||
|
|
||||||
def test_namedtuple_keyword_usage(self):
|
|
||||||
with self.assertWarnsRegex(
|
|
||||||
DeprecationWarning,
|
|
||||||
"Creating NamedTuple classes using keyword arguments is deprecated"
|
|
||||||
):
|
|
||||||
LocalEmployee = NamedTuple("LocalEmployee", name=str, age=int)
|
|
||||||
|
|
||||||
nick = LocalEmployee('Nick', 25)
|
|
||||||
self.assertIsInstance(nick, tuple)
|
|
||||||
self.assertEqual(nick.name, 'Nick')
|
|
||||||
self.assertEqual(LocalEmployee.__name__, 'LocalEmployee')
|
|
||||||
self.assertEqual(LocalEmployee._fields, ('name', 'age'))
|
|
||||||
self.assertEqual(LocalEmployee.__annotations__, dict(name=str, age=int))
|
|
||||||
|
|
||||||
with self.assertRaisesRegex(
|
|
||||||
TypeError,
|
|
||||||
"Either list of fields or keywords can be provided to NamedTuple, not both"
|
|
||||||
):
|
|
||||||
NamedTuple('Name', [('x', int)], y=str)
|
|
||||||
|
|
||||||
with self.assertRaisesRegex(
|
|
||||||
TypeError,
|
|
||||||
"Either list of fields or keywords can be provided to NamedTuple, not both"
|
|
||||||
):
|
|
||||||
NamedTuple('Name', [], y=str)
|
|
||||||
|
|
||||||
with self.assertRaisesRegex(
|
|
||||||
TypeError,
|
|
||||||
(
|
|
||||||
r"Cannot pass `None` as the 'fields' parameter "
|
|
||||||
r"and also specify fields using keyword arguments"
|
|
||||||
)
|
|
||||||
):
|
|
||||||
NamedTuple('Name', None, x=int)
|
|
||||||
|
|
||||||
def test_namedtuple_special_keyword_names(self):
|
|
||||||
with self.assertWarnsRegex(
|
|
||||||
DeprecationWarning,
|
|
||||||
"Creating NamedTuple classes using keyword arguments is deprecated"
|
|
||||||
):
|
|
||||||
NT = NamedTuple("NT", cls=type, self=object, typename=str, fields=list)
|
|
||||||
|
|
||||||
self.assertEqual(NT.__name__, 'NT')
|
|
||||||
self.assertEqual(NT._fields, ('cls', 'self', 'typename', 'fields'))
|
|
||||||
a = NT(cls=str, self=42, typename='foo', fields=[('bar', tuple)])
|
|
||||||
self.assertEqual(a.cls, str)
|
|
||||||
self.assertEqual(a.self, 42)
|
|
||||||
self.assertEqual(a.typename, 'foo')
|
|
||||||
self.assertEqual(a.fields, [('bar', tuple)])
|
|
||||||
|
|
||||||
def test_empty_namedtuple(self):
|
def test_empty_namedtuple(self):
|
||||||
expected_warning = re.escape(
|
with self.assertRaisesRegex(TypeError, "missing.*required.*argument"):
|
||||||
"Failing to pass a value for the 'fields' parameter is deprecated "
|
BAD = NamedTuple('BAD')
|
||||||
"and will be disallowed in Python 3.15. "
|
|
||||||
"To create a NamedTuple class with 0 fields "
|
|
||||||
"using the functional syntax, "
|
|
||||||
"pass an empty list, e.g. `NT1 = NamedTuple('NT1', [])`."
|
|
||||||
)
|
|
||||||
with self.assertWarnsRegex(DeprecationWarning, fr"^{expected_warning}$"):
|
|
||||||
NT1 = NamedTuple('NT1')
|
|
||||||
|
|
||||||
expected_warning = re.escape(
|
NT1 = NamedTuple('NT1', {})
|
||||||
"Passing `None` as the 'fields' parameter is deprecated "
|
NT2 = NamedTuple('NT2', ())
|
||||||
"and will be disallowed in Python 3.15. "
|
NT3 = NamedTuple('NT3', [])
|
||||||
"To create a NamedTuple class with 0 fields "
|
|
||||||
"using the functional syntax, "
|
|
||||||
"pass an empty list, e.g. `NT2 = NamedTuple('NT2', [])`."
|
|
||||||
)
|
|
||||||
with self.assertWarnsRegex(DeprecationWarning, fr"^{expected_warning}$"):
|
|
||||||
NT2 = NamedTuple('NT2', None)
|
|
||||||
|
|
||||||
NT3 = NamedTuple('NT2', [])
|
|
||||||
|
|
||||||
class CNT(NamedTuple):
|
class CNT(NamedTuple):
|
||||||
pass # empty body
|
pass # empty body
|
||||||
|
@ -8166,16 +8101,18 @@ class CNT(NamedTuple):
|
||||||
def test_namedtuple_errors(self):
|
def test_namedtuple_errors(self):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
NamedTuple.__new__()
|
NamedTuple.__new__()
|
||||||
|
with self.assertRaisesRegex(TypeError, "object is not iterable"):
|
||||||
|
NamedTuple('Name', None)
|
||||||
|
|
||||||
with self.assertRaisesRegex(
|
with self.assertRaisesRegex(
|
||||||
TypeError,
|
TypeError,
|
||||||
"missing 1 required positional argument"
|
"missing 2 required positional arguments"
|
||||||
):
|
):
|
||||||
NamedTuple()
|
NamedTuple()
|
||||||
|
|
||||||
with self.assertRaisesRegex(
|
with self.assertRaisesRegex(
|
||||||
TypeError,
|
TypeError,
|
||||||
"takes from 1 to 2 positional arguments but 3 were given"
|
"takes 2 positional arguments but 3 were given"
|
||||||
):
|
):
|
||||||
NamedTuple('Emp', [('name', str)], None)
|
NamedTuple('Emp', [('name', str)], None)
|
||||||
|
|
||||||
|
@ -8187,10 +8124,22 @@ def test_namedtuple_errors(self):
|
||||||
|
|
||||||
with self.assertRaisesRegex(
|
with self.assertRaisesRegex(
|
||||||
TypeError,
|
TypeError,
|
||||||
"missing 1 required positional argument: 'typename'"
|
"got some positional-only arguments passed as keyword arguments"
|
||||||
):
|
):
|
||||||
NamedTuple(typename='Emp', name=str, id=int)
|
NamedTuple(typename='Emp', name=str, id=int)
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
TypeError,
|
||||||
|
"got an unexpected keyword argument"
|
||||||
|
):
|
||||||
|
NamedTuple('Name', [('x', int)], y=str)
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
TypeError,
|
||||||
|
"got an unexpected keyword argument"
|
||||||
|
):
|
||||||
|
NamedTuple('Name', [], y=str)
|
||||||
|
|
||||||
def test_copy_and_pickle(self):
|
def test_copy_and_pickle(self):
|
||||||
global Emp # pickle wants to reference the class by name
|
global Emp # pickle wants to reference the class by name
|
||||||
Emp = NamedTuple('Emp', [('name', str), ('cool', int)])
|
Emp = NamedTuple('Emp', [('name', str), ('cool', int)])
|
||||||
|
|
|
@ -2968,7 +2968,7 @@ def annotate(format):
|
||||||
return nm_tpl
|
return nm_tpl
|
||||||
|
|
||||||
|
|
||||||
def NamedTuple(typename, fields=_sentinel, /, **kwargs):
|
def NamedTuple(typename, fields, /):
|
||||||
"""Typed version of namedtuple.
|
"""Typed version of namedtuple.
|
||||||
|
|
||||||
Usage::
|
Usage::
|
||||||
|
@ -2988,48 +2988,9 @@ class Employee(NamedTuple):
|
||||||
|
|
||||||
Employee = NamedTuple('Employee', [('name', str), ('id', int)])
|
Employee = NamedTuple('Employee', [('name', str), ('id', int)])
|
||||||
"""
|
"""
|
||||||
if fields is _sentinel:
|
|
||||||
if kwargs:
|
|
||||||
deprecated_thing = "Creating NamedTuple classes using keyword arguments"
|
|
||||||
deprecation_msg = (
|
|
||||||
"{name} is deprecated and will be disallowed in Python {remove}. "
|
|
||||||
"Use the class-based or functional syntax instead."
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
deprecated_thing = "Failing to pass a value for the 'fields' parameter"
|
|
||||||
example = f"`{typename} = NamedTuple({typename!r}, [])`"
|
|
||||||
deprecation_msg = (
|
|
||||||
"{name} is deprecated and will be disallowed in Python {remove}. "
|
|
||||||
"To create a NamedTuple class with 0 fields "
|
|
||||||
"using the functional syntax, "
|
|
||||||
"pass an empty list, e.g. "
|
|
||||||
) + example + "."
|
|
||||||
elif fields is None:
|
|
||||||
if kwargs:
|
|
||||||
raise TypeError(
|
|
||||||
"Cannot pass `None` as the 'fields' parameter "
|
|
||||||
"and also specify fields using keyword arguments"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
deprecated_thing = "Passing `None` as the 'fields' parameter"
|
|
||||||
example = f"`{typename} = NamedTuple({typename!r}, [])`"
|
|
||||||
deprecation_msg = (
|
|
||||||
"{name} is deprecated and will be disallowed in Python {remove}. "
|
|
||||||
"To create a NamedTuple class with 0 fields "
|
|
||||||
"using the functional syntax, "
|
|
||||||
"pass an empty list, e.g. "
|
|
||||||
) + example + "."
|
|
||||||
elif kwargs:
|
|
||||||
raise TypeError("Either list of fields or keywords"
|
|
||||||
" can be provided to NamedTuple, not both")
|
|
||||||
if fields is _sentinel or fields is None:
|
|
||||||
import warnings
|
|
||||||
warnings._deprecated(deprecated_thing, message=deprecation_msg, remove=(3, 15))
|
|
||||||
fields = kwargs.items()
|
|
||||||
types = {n: _type_check(t, f"field {n} annotation must be a type")
|
types = {n: _type_check(t, f"field {n} annotation must be a type")
|
||||||
for n, t in fields}
|
for n, t in fields}
|
||||||
field_names = [n for n, _ in fields]
|
field_names = [n for n, _ in fields]
|
||||||
|
|
||||||
nt = _make_nmtuple(typename, field_names, _make_eager_annotate(types), module=_caller())
|
nt = _make_nmtuple(typename, field_names, _make_eager_annotate(types), module=_caller())
|
||||||
nt.__orig_bases__ = (NamedTuple,)
|
nt.__orig_bases__ = (NamedTuple,)
|
||||||
return nt
|
return nt
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Remove support for creating :class:`~typing.NamedTuple` classes via the
|
||||||
|
undocumented keyword argument syntax. Patch by Bénédikt Tran.
|
Loading…
Reference in New Issue