-
-
Notifications
You must be signed in to change notification settings - Fork 33.5k
Description
Bug report
When Generic appears twice in a class' inheritance tree, Python may fail to find a consistent MRO. An effort seems to be taken to address this in typing._BaseGenericAlias and typing._GenericAlias. However, when one of the bases is a subscripted generic and the other is a child of a subscripted generic (no longer generic itself) this falls short. I believe the root cause is the fact that _GenericAlias, unlike its parent, only checks for isinstance(b, _BaseGenericAlias and not for issubclass(b, Generic) when considering whether it should skip the Generic base for this MRO entry.
Consider the following example:
from typing import Generic, TypeVar
T = TypeVar("T")
class A(Generic[T]):
pass
class B(A[int]):
pass
class Works(B, Generic[T]):
pass
class WorksToo(Generic[T], A[int]):
pass
class Broken(Generic[T], B):
passThe Works class does not present a problem because Generic[T] appears as the last base. The WorksToo class works because A[int] is recognized by _GenericAlias.__mro_entries__ as another _BaseGenericAlias, therefore Generic is only included as MRO entry for the latter. Broken results in an exception, even though B is semantically equivalent to A[int].
Traceback (most recent call last):
File "/home/sander/documents/projects/python-sandbox/main.py", line 23, in <module>
class Broken(Generic[T], B):
TypeError: Cannot create a consistent method resolution
order (MRO) for bases Generic, BI managed to get it to run correctly by making the following change to typing.py:
diff --git a/usr/lib/python3.9/typing.py b/typing.py
index d35a2a5..06be18f 100644
--- a/usr/lib/python3.9/typing.py
+++ b/typing.py
@@ -809,7 +809,7 @@ class _GenericAlias(_BaseGenericAlias, _root=True):
return ()
i = bases.index(self)
for b in bases[i+1:]:
- if isinstance(b, _BaseGenericAlias) and b is not self:
+ if (isinstance(b, _BaseGenericAlias) or issubclass(b, Generic)) and b is not self:
return ()
return (self.__origin__,)I am not sufficiently familiar with typing's internals (or even MRO) to be completely confident of this patch, but I believe it to be sound. If this issue gets confirmed I'd be willing to open a pull request with this change.
Your environment
- CPython versions tested on: 3.9, 3.11
- Operating system and architecture: x86, Linux