Quick fix to set abc.ABCMeta as metaclass (PY-30789)

This commit is contained in:
Semyon Proshev
2018-07-17 20:17:19 +03:00
parent fff6955833
commit c579994aec
13 changed files with 121 additions and 6 deletions

View File

@@ -211,6 +211,7 @@ public class PyNames {
public static final String ABSTRACTPROPERTY = "abstractproperty";
public static final String ABC_META_CLASS = "ABCMeta";
public static final String ABC = "abc.ABC";
public static final String ABC_META = "abc.ABCMeta";
public static final String TUPLE = "tuple";
public static final String SET = "set";

View File

@@ -16,6 +16,7 @@ import com.jetbrains.python.codeInsight.typing.PyProtocolsKt;
import com.jetbrains.python.inspections.quickfix.PyImplementMethodsQuickFix;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.types.PyClassLikeType;
import com.jetbrains.python.psi.types.TypeEvalContext;
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
@@ -52,7 +53,11 @@ public class PyAbstractClassInspection extends PyInspection {
final List<PyFunction> toImplement = PyOverrideImplementUtil.getAllSuperAbstractMethods(pyClass, myTypeEvalContext);
final ASTNode nameNode = pyClass.getNameNode();
if (!toImplement.isEmpty() && nameNode != null) {
final SmartList<LocalQuickFix> quickFixes = new SmartList<>(new PyImplementMethodsQuickFix(pyClass, toImplement));
final SmartList<LocalQuickFix> quickFixes = new SmartList<>(
new PyImplementMethodsQuickFix(pyClass, toImplement),
new SetABCMetaAsMetaclassQuickFix()
);
if (LanguageLevel.forElement(pyClass).isPy3K()) {
quickFixes.add(new AddABCToSuperclassesQuickFix());
}
@@ -101,5 +106,27 @@ public class PyAbstractClassInspection extends PyInspection {
PyClassRefactoringUtil.addSuperclasses(project, cls, abcClass);
}
}
private static class SetABCMetaAsMetaclassQuickFix implements LocalQuickFix {
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getFamilyName() {
return "Set '" + PyNames.ABC_META + "' as metaclass";
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PyClass cls = PyUtil.as(descriptor.getPsiElement().getParent(), PyClass.class);
if (cls == null) return;
final PyClass abcMetaClass = PyPsiFacade.getInstance(project).createClassByQName(PyNames.ABC_META, cls);
if (abcMetaClass == null) return;
final TypeEvalContext context = TypeEvalContext.userInitiated(cls.getProject(), cls.getContainingFile());
PyClassRefactoringUtil.addMetaClassIfNotExist(cls, abcMetaClass, context);
}
}
}
}

View File

@@ -1509,11 +1509,9 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
return null;
}
try {
final String abcMeta = "abc." + PyNames.ABC_META_CLASS;
return classTypes
.stream()
.filter(t -> !abcMeta.equals(t.getClassQName()))
.filter(t -> !PyNames.ABC_META.equals(t.getClassQName()))
.max(
(t1, t2) -> {
if (Objects.equals(t1, t2)) {

View File

@@ -116,7 +116,7 @@ class MethodsManager extends MembersManager<PyFunction> {
final Project project = aClass.getProject();
final PsiFile file = aClass.getContainingFile();
final PyClass abcMetaClass = PyPsiFacade.getInstance(project).createClassByQName("abc." + PyNames.ABC_META_CLASS, aClass);
final PyClass abcMetaClass = PyPsiFacade.getInstance(project).createClassByQName(PyNames.ABC_META, aClass);
final TypeEvalContext context = TypeEvalContext.userInitiated(project, file);
if (abcMetaClass != null && PyClassRefactoringUtil.addMetaClassIfNotExist(aClass, abcMetaClass, context)) {

View File

@@ -0,0 +1,4 @@
from PyAbstractClassInspection.quickFix.SetABCMetaAsMetaclassPy3.main_import import A1
class A<caret>2(A1):
pass

View File

@@ -0,0 +1,6 @@
from abc import ABCMeta
from PyAbstractClassInspection.quickFix.SetABCMetaAsMetaclassPy3.main_import import A1
class A2(A1, metaclass=ABCMeta):
pass

View File

@@ -0,0 +1,6 @@
import abc
class A1(metaclass=abc.ABCMeta):
@abc.abstractmethod
def m1(self):
pass

View File

@@ -0,0 +1,12 @@
from abc import ABCMeta, abstractmethod
class A1:
__metaclass__ = ABCMeta
@abstractmethod
def m1(self):
pass
class A<caret>2(A1):
pass

View File

@@ -0,0 +1,12 @@
from abc import ABCMeta, abstractmethod
class A1:
__metaclass__ = ABCMeta
@abstractmethod
def m1(self):
pass
class A2(A1):
__metaclass__ = ABCMeta

View File

@@ -0,0 +1,9 @@
from abc import ABCMeta, abstractmethod
class A1(metaclass=ABCMeta):
@abstractmethod
def m1(self):
pass
class A<caret>2(A1):
pass

View File

@@ -0,0 +1,9 @@
from abc import ABCMeta, abstractmethod
class A1(metaclass=ABCMeta):
@abstractmethod
def m1(self):
pass
class A2(A1, metaclass=ABCMeta):
pass

View File

@@ -161,6 +161,29 @@ public class Py3QuickFixTest extends PyTestCase {
true);
}
// PY-30789
public void testSetABCMetaAsMetaclassPy3() {
final String[] testFiles = {
"PyAbstractClassInspection/quickFix/SetABCMetaAsMetaclassPy3/main.py",
"PyAbstractClassInspection/quickFix/SetABCMetaAsMetaclassPy3/main_import.py"
};
doInspectionTest(testFiles,
PyAbstractClassInspection.class,
"Set '" + PyNames.ABC_META + "' as metaclass",
true,
true);
}
// PY-30789
public void testSetImportedABCMetaAsMetaclassPy3() {
doInspectionTest("PyAbstractClassInspection/quickFix/SetImportedABCMetaAsMetaclassPy3/main.py",
PyAbstractClassInspection.class,
"Set '" + PyNames.ABC_META + "' as metaclass",
true,
true);
}
@Override
@NonNls
protected String getTestDataPath() {

View File

@@ -4,7 +4,6 @@ package com.jetbrains.python;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.testFramework.PsiTestUtil;
import com.intellij.testFramework.TestDataFile;
import com.intellij.testFramework.TestDataPath;
import com.intellij.util.containers.ContainerUtil;
@@ -685,6 +684,15 @@ public class PyQuickFixTest extends PyTestCase {
doInspectionTest(PyMethodOverridingInspection.class, "<html>Change signature of m(self, <b>**kwargs</b>)</html>", true, true);
}
// PY-30789
public void testSetImportedABCMetaAsMetaclassPy2() {
doInspectionTest("PyAbstractClassInspection/quickFix/SetImportedABCMetaAsMetaclassPy2/main.py",
PyAbstractClassInspection.class,
"Set '" + PyNames.ABC_META + "' as metaclass",
true,
true);
}
@Override
@NonNls
protected String getTestDataPath() {