Skip to content

Commit

Permalink
pythongh-112903: Handle non-types in _BaseGenericAlias.__mro_entries__()
Browse files Browse the repository at this point in the history
  • Loading branch information
carljm committed Feb 9, 2024
1 parent 5914a21 commit 88ca12a
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 1 deletion.
34 changes: 34 additions & 0 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 88ca12a

Please sign in to comment.