mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
[python] Special-case typing.Generic while calculating a class MRO
typing.Generic is a magical class that can be specified in any position
in the list of base classes, not affecting the MRO consistency. It's done by
the custom __mro_entries__ implementation in typing._BaseGenericAlias (Python < 3.12),
which skips this Generic entry if there are other generic classes following
it on the list of superclasses. Namely, it's possible to do the following:
```
class Base(Generic[T]):
pass
class MyClass(Generic[T], Base[T]):
pass
```
which would cause a TypeError for regular classes. Since it broke our implementation
of the C3 algorithm in PyClassImpl.getMROAncestorTypes, we now special-case it by
always moving typing.Generic to the very end of the base class list while constructing
MRO.
See https://github.com/python/cpython/blob/3.11/Lib/typing.py#L1298 for a pure-Python
version of typing._BaseGenericAlias.__mro_entries__ and a relevant discussion in
https://github.com/python/cpython/issues/106102.
GitOrigin-RevId: e7d765193d532ab8457133e8fb5ad06840d89225
This commit is contained in:
committed by
intellij-monorepo-bot
parent
5622587ae3
commit
190a55438e
@@ -25,15 +25,13 @@ import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PyStubElementTypes;
|
||||
import com.jetbrains.python.PythonDialectsTokenSetProvider;
|
||||
import com.jetbrains.python.*;
|
||||
import com.jetbrains.python.ast.PyAstFunction.Modifier;
|
||||
import com.jetbrains.python.ast.impl.PyUtilCore;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
|
||||
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider;
|
||||
import com.jetbrains.python.documentation.docstrings.DocStringUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.stubs.PyClassElementType;
|
||||
@@ -383,7 +381,7 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
|
||||
cache.put(type, Ref.create());
|
||||
List<PyClassLikeType> result = null;
|
||||
try {
|
||||
final List<PyClassLikeType> bases = removeNotNullDuplicates(type.getSuperClassTypes(context));
|
||||
final List<PyClassLikeType> bases = moveTypingGenericToTheEnd(removeNotNullDuplicates(type.getSuperClassTypes(context)));
|
||||
final List<List<PyClassLikeType>> lines = new ArrayList<>();
|
||||
for (PyClassLikeType base : bases) {
|
||||
if (base != null) {
|
||||
@@ -424,6 +422,20 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
|
||||
return result;
|
||||
}
|
||||
|
||||
private static @NotNull List<PyClassLikeType> moveTypingGenericToTheEnd(@NotNull List<PyClassLikeType> bases) {
|
||||
int genericPos = ContainerUtil.indexOf(bases,
|
||||
base -> base instanceof PyCustomType customType &&
|
||||
PyTypingTypeProvider.GENERIC.equals(customType.getClassQName()));
|
||||
if (genericPos >= 0) {
|
||||
List<PyClassLikeType> reorderedBases = new ArrayList<>(bases.size());
|
||||
reorderedBases.addAll(bases.subList(0, genericPos));
|
||||
reorderedBases.addAll(bases.subList(genericPos + 1, bases.size()));
|
||||
reorderedBases.add(bases.get(genericPos));
|
||||
return reorderedBases;
|
||||
}
|
||||
return bases;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static <T> List<T> removeNotNullDuplicates(@NotNull List<T> list) {
|
||||
final Set<T> distinct = new HashSet<>();
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
class Base(Generic[T]):
|
||||
pass
|
||||
|
||||
class MyClass(Generic[T], Base[T]):
|
||||
pass
|
||||
@@ -152,6 +152,11 @@ public class PyClassMROTest extends PyTestCase {
|
||||
});
|
||||
}
|
||||
|
||||
public void testTypingGenericAsFirstBaseClass() {
|
||||
PyClass pyClass = getClass("MyClass");
|
||||
assertMRO(pyClass, "Base", "Generic", "object");
|
||||
}
|
||||
|
||||
// PY-21837
|
||||
public void testClassImportedFromUnstubbedFileAndSuperImportedWithAs() {
|
||||
myFixture.copyDirectoryToProject("codeInsight/classMRO/" + getTestName(false), "");
|
||||
|
||||
Reference in New Issue
Block a user