From 88ca12a99b61ddfe79943ac56934248f23da8904 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Thu, 8 Feb 2024 16:58:16 -0800 Subject: [PATCH] gh-112903: Handle non-types in _BaseGenericAlias.__mro_entries__() --- Lib/test/test_typing.py | 34 ++++++++++++++++++++++++++++++++++ Lib/typing.py | 9 ++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index b684af4f33ed71..9611a618da4eb0 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4920,6 +4920,40 @@ class B(Generic[S]): ... class C(List[int], B): ... self.assertEqual(C.__mro__, (C, list, B, Generic, object)) + def test_multiple_inheritance_with_non_type_implementing___mro_entries__(self): + class O: + def __mro_entries__(self, bases): + return (object,) + + class A(List[int], O()): ... + + self.assertEqual(A.__mro__, (A, list, Generic, object)) + + def test_multiple_inheritance_with_non_type_without___mro_entries__(self): + class O: pass + + # We should get an error here, but from the type machinery, not from typing.py + with self.assertRaisesRegex(TypeError, r"^metaclass conflict:"): + class A(List[int], O()): ... + + def test_multiple_inheritance_with_non_type_with_bad___mro_entries__(self): + class O: + def __mro_entries__(self, bases): + return None + + # We should get an error here, but from the type machinery, not from typing.py + with self.assertRaisesRegex(TypeError, r"^__mro_entries__ must return a tuple"): + class A(List[int], O()): ... + + def test_multiple_inheritance_with_genericalias(self): + T = TypeVar("T") + + class BaseSeq(typing.Sequence[T]): ... + class MySeq(List[T], BaseSeq[T]): ... + + self.assertEqual(MySeq.__mro__[:4], (MySeq, list, BaseSeq, collections.abc.Sequence)) + self.assertEqual(MySeq.__mro__[-2:], (Generic, object)) + def test_init_subclass_super_called(self): class FinalException(Exception): pass diff --git a/Lib/typing.py b/Lib/typing.py index d278b4effc7eba..71680221541991 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1137,7 +1137,14 @@ def __mro_entries__(self, bases): res.append(self.__origin__) i = bases.index(self) for b in bases[i+1:]: - if isinstance(b, _BaseGenericAlias) or issubclass(b, Generic): + if isinstance(b, _BaseGenericAlias): + break + if not isinstance(b, type): + meth = getattr(b, "__mro_entries__", None) + nb = meth(bases) if meth else None + if isinstance(nb, tuple) and any(issubclass(b2, Generic) for b2 in nb): + break + elif issubclass(b, Generic): break else: res.append(Generic)