methods of the :class:`wave.Wave_read` and :class:`wave.Wave_write` classes.
They will be removed in Python 3.15.
(Contributed by Victor Stinner in :gh:`105096`.)
+* Creating a :class:`typing.NamedTuple` class using keyword arguments to denote
+ the fields (``NT = NamedTuple("NT", x=int, y=int)``) is deprecated, and will
+ be disallowed in Python 3.15. Use the class-based syntax or the functional
+ syntax instead. (Contributed by Alex Waygood in :gh:`105566`.)
+* When using the functional syntax to create a :class:`typing.NamedTuple`
+ class, failing to pass a value to the 'fields' parameter
+ (``NT = NamedTuple("NT")``) is deprecated. Passing ``None`` to the 'fields'
+ parameter (``NT = NamedTuple("NT", None)``) is also deprecated. Both will be
+ disallowed in Python 3.15. To create a NamedTuple class with 0 fields, use
+ ``class NT(NamedTuple): pass`` or ``NT = NamedTuple("NT", [])``.
+ (Contributed by Alex Waygood in :gh:`105566`.)
* :mod:`array`'s ``'u'`` format code, deprecated in docs since Python 3.3,
emits :exc:`DeprecationWarning` since 3.13
self.assertEqual(a, (1, [2]))
def test_namedtuple_keyword_usage(self):
- LocalEmployee = NamedTuple("LocalEmployee", name=str, age=int)
+ 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.assertRaises(TypeError):
+
+ 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):
- NT = NamedTuple("NT", cls=type, self=object, typename=str, fields=list)
+ 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.fields, [('bar', tuple)])
def test_empty_namedtuple(self):
- NT = NamedTuple('NT')
+ expected_warning = re.escape(
+ "Failing to pass a value for the 'fields' parameter is deprecated "
+ "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(
+ "Passing `None` as the 'fields' parameter is deprecated "
+ "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. `NT2 = NamedTuple('NT2', [])`."
+ )
+ with self.assertWarnsRegex(DeprecationWarning, fr"^{expected_warning}$"):
+ NT2 = NamedTuple('NT2', None)
+
+ NT3 = NamedTuple('NT2', [])
class CNT(NamedTuple):
pass # empty body
- for struct in [NT, CNT]:
+ for struct in NT1, NT2, NT3, CNT:
with self.subTest(struct=struct):
self.assertEqual(struct._fields, ())
self.assertEqual(struct._field_defaults, {})
def test_namedtuple_errors(self):
with self.assertRaises(TypeError):
NamedTuple.__new__()
- with self.assertRaises(TypeError):
+
+ with self.assertRaisesRegex(
+ TypeError,
+ "missing 1 required positional argument"
+ ):
NamedTuple()
- with self.assertRaises(TypeError):
+
+ with self.assertRaisesRegex(
+ TypeError,
+ "takes from 1 to 2 positional arguments but 3 were given"
+ ):
NamedTuple('Emp', [('name', str)], None)
- with self.assertRaises(ValueError):
+
+ with self.assertRaisesRegex(
+ ValueError,
+ "Field names cannot start with an underscore"
+ ):
NamedTuple('Emp', [('_name', str)])
- with self.assertRaises(TypeError):
+
+ with self.assertRaisesRegex(
+ TypeError,
+ "missing 1 required positional argument: 'typename'"
+ ):
NamedTuple(typename='Emp', name=str, id=int)
def test_copy_and_pickle(self):
return nm_tpl
-def NamedTuple(typename, fields=None, /, **kwargs):
+class _Sentinel:
+ __slots__ = ()
+ def __repr__(self):
+ return '<sentinel>'
+
+
+_sentinel = _Sentinel()
+
+
+def NamedTuple(typename, fields=_sentinel, /, **kwargs):
"""Typed version of namedtuple.
Usage::
Employee = NamedTuple('Employee', [('name', str), ('id', int)])
"""
- if fields is None:
- fields = kwargs.items()
+ 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()
nt = _make_nmtuple(typename, fields, module=_caller())
nt.__orig_bases__ = (NamedTuple,)
return nt