From f7da96db034fea606618bb2b384b14f68db55475 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Tue, 19 Dec 2017 17:19:06 +0300 Subject: [PATCH] PY-27656 PY-27604 Exclude ancestors of metaclasses from class hierarchy in case this relationship was created implicitly by inheriting an instance of metaclass. It also fixes warnings about the first parameter of SQLAlchemy model methods being named "self" instead of "cls" since we no longer consider these classes descendants of "type". --- .../jetbrains/python/psi/impl/PyClassImpl.java | 17 ++++++++++++++++- .../DirectlyInstantiatedMetaclassAncestor.py | 13 +++++++++++++ .../python/codeInsight/PyClassMROTest.java | 5 +++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 python/testData/codeInsight/classMRO/DirectlyInstantiatedMetaclassAncestor.py diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 09fe1bd98699..24b3ed199cdd 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -427,7 +427,22 @@ public class PyClassImpl extends PyBaseElementImpl implements PyCla final List> lines = new ArrayList<>(); for (PyClassLikeType base : bases) { if (base != null) { - final List baseClassMRO = mroLinearize(base, true, context, cache); + // Don't include ancestors of a metaclass instance + final List baseClassMRO; + if (base.isDefinition()) { + baseClassMRO = mroLinearize(base, true, context, cache); + } + else { + List metaclassInstanceMro = new ArrayList<>(); + metaclassInstanceMro.add(base); + if (base instanceof PyClassType) { + final PyClassImpl pyClass = as(((PyClassType)base).getPyClass(), PyClassImpl.class); + if (pyClass != null) { + ContainerUtil.addIfNotNull(metaclassInstanceMro, pyClass.getImplicitSuper(context)); + } + } + baseClassMRO = metaclassInstanceMro; + } if (!baseClassMRO.isEmpty()) { // mroMerge() updates passed MRO lists internally lines.add(new LinkedList<>(baseClassMRO)); diff --git a/python/testData/codeInsight/classMRO/DirectlyInstantiatedMetaclassAncestor.py b/python/testData/codeInsight/classMRO/DirectlyInstantiatedMetaclassAncestor.py new file mode 100644 index 000000000000..1382eff7461b --- /dev/null +++ b/python/testData/codeInsight/classMRO/DirectlyInstantiatedMetaclassAncestor.py @@ -0,0 +1,13 @@ +class MetaBase(type): + pass + + +class Meta(MetaBase): + pass + + +Base = Meta('Base', (), {}) + + +class MyClass(Base): + pass \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/codeInsight/PyClassMROTest.java b/python/testSrc/com/jetbrains/python/codeInsight/PyClassMROTest.java index 1fcc619a197d..53e485b6bb9f 100644 --- a/python/testSrc/com/jetbrains/python/codeInsight/PyClassMROTest.java +++ b/python/testSrc/com/jetbrains/python/codeInsight/PyClassMROTest.java @@ -125,6 +125,11 @@ public class PyClassMROTest extends PyTestCase { assertMRO(getClass("MyClass"), "Base", "object"); } + // PY-27656 + public void testDirectlyInstantiatedMetaclassAncestor() { + runWithLanguageLevel(LanguageLevel.PYTHON30, () -> assertMRO(getClass("MyClass"), "Meta", "object")); + } + // PY-20026 public void testUnresolvedMetaClassAncestors() { runWithLanguageLevel(LanguageLevel.PYTHON30, () -> assertMRO(getClass("CompositeFieldMeta"), "type", "object"));