diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index a0beed4a8c77fb..624085dc6f0baf 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2401,6 +2401,9 @@ types. disallowed in Python 3.15. To create a NamedTuple class with 0 fields, use ``class NT(NamedTuple): pass`` or ``NT = NamedTuple("NT", [])``. + .. versionchanged:: 3.14 + Added support for arbitrary multiple inheritance. + .. class:: NewType(name, tp) Helper class to create low-overhead :ref:`distinct types `. diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 5b03bd9e5a8caf..322836c1c8c595 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1803,6 +1803,11 @@ typing * Remove :class:`!typing.ByteString`. It had previously raised a :exc:`DeprecationWarning` since Python 3.12. +* Add support of multiple inheritance with :class:`~typing.NamedTuple`. + Previously, multiple inheritance was only supported if there was exactly + one other base and the base was :class:`typing.Generic`. + (Contributed by Serhiy Storchaka in :gh:`116241`.) + * :class:`typing.TypeAliasType` now supports star unpacking. urllib diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 4fd3d53b72c01b..9ac8bf3a304bd1 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -8003,7 +8003,40 @@ class X(NamedTuple): def test_multiple_inheritance(self): class A: - pass + @property + def x(self): + return 4 + @property + def y(self): + return 5 + def __len__(self): + return 10 + + class X(NamedTuple, A): + x: int + self.assertEqual(X.__bases__, (tuple, A)) + self.assertEqual(X.__orig_bases__, (NamedTuple, A)) + self.assertEqual(X.__mro__, (X, tuple, A, object)) + + a = X(3) + self.assertEqual(a.x, 3) + self.assertEqual(a.y, 5) + self.assertEqual(len(a), 1) + + class Y(A, NamedTuple): + x: int + self.assertEqual(Y.__bases__, (A, tuple)) + self.assertEqual(Y.__orig_bases__, (A, NamedTuple)) + self.assertEqual(Y.__mro__, (Y, A, tuple, object)) + + a = Y(3) + self.assertEqual(a.x, 3) + self.assertEqual(a.y, 5) + self.assertEqual(len(a), 10) + + def test_multiple_inheritance_errors(self): + class A(NamedTuple): + x: int with self.assertRaises(TypeError): class X(NamedTuple, A): x: int diff --git a/Lib/typing.py b/Lib/typing.py index f70dcd0b5b7b5c..b15d00f626e0cb 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -2897,10 +2897,6 @@ def __new__(cls, typename, bases, ns): if "__classcell__" in ns: raise TypeError( "uses of super() and __class__ are unsupported in methods of NamedTuple subclasses") - for base in bases: - if base is not _NamedTuple and base is not Generic: - raise TypeError( - 'can only inherit from a NamedTuple type and Generic') bases = tuple(tuple if base is _NamedTuple else base for base in bases) if "__annotations__" in ns: types = ns["__annotations__"] diff --git a/Misc/NEWS.d/next/Library/2022-04-28-18-45-58.gh-issue-116241.hu9kRk.rst b/Misc/NEWS.d/next/Library/2022-04-28-18-45-58.gh-issue-116241.hu9kRk.rst new file mode 100644 index 00000000000000..0cbfb8ad12f6e3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-04-28-18-45-58.gh-issue-116241.hu9kRk.rst @@ -0,0 +1 @@ +Add support for arbitrary multiple inheritance with :class:`typing.NamedTuple`.