mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Move adding metaclass method (PY-30789)
This commit is contained in:
@@ -41,6 +41,8 @@ import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.impl.PyImportedModule;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -204,10 +206,10 @@ public final class PyClassRefactoringUtil {
|
||||
|
||||
|
||||
/**
|
||||
* Restores references saved by {@link #rememberNamedReferences(com.intellij.psi.PsiElement, String...)}.
|
||||
* Restores references saved by {@link #rememberNamedReferences(PsiElement, String...)}.
|
||||
*
|
||||
* @param element newly created element to restore references
|
||||
* @see #rememberNamedReferences(com.intellij.psi.PsiElement, String...)
|
||||
* @see #rememberNamedReferences(PsiElement, String...)
|
||||
*/
|
||||
public static void restoreNamedReferences(@NotNull final PsiElement element) {
|
||||
restoreNamedReferences(element, null);
|
||||
@@ -337,9 +339,9 @@ public final class PyClassRefactoringUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for references inside some element (like {@link com.jetbrains.python.psi.PyAssignmentStatement}, {@link com.jetbrains.python.psi.PyFunction} etc
|
||||
* Searches for references inside some element (like {@link PyAssignmentStatement}, {@link PyFunction} etc
|
||||
* and stored them.
|
||||
* After that you can add element to some new parent. Newly created element then should be processed via {@link #restoreNamedReferences(com.intellij.psi.PsiElement)}
|
||||
* After that you can add element to some new parent. Newly created element then should be processed via {@link #restoreNamedReferences(PsiElement)}
|
||||
* and all references would be restored.
|
||||
*
|
||||
* @param element element to store references for
|
||||
@@ -526,10 +528,10 @@ public final class PyClassRefactoringUtil {
|
||||
* @param paramExpressions param expressions. Like "object" or "MySuperClass". Will not add any param exp. if null.
|
||||
* @param keywordArguments keyword args like "metaclass=ABCMeta". key-value pairs. Will not add any keyword arg. if null.
|
||||
*/
|
||||
public static void addSuperClassExpressions(@NotNull final Project project,
|
||||
@NotNull final PyClass clazz,
|
||||
@Nullable final Collection<String> paramExpressions,
|
||||
@Nullable final Collection<Pair<String, String>> keywordArguments) {
|
||||
private static void addSuperClassExpressions(@NotNull final Project project,
|
||||
@NotNull final PyClass clazz,
|
||||
@Nullable final Collection<String> paramExpressions,
|
||||
@Nullable final Collection<Pair<String, String>> keywordArguments) {
|
||||
final PyElementGenerator generator = PyElementGenerator.getInstance(project);
|
||||
final LanguageLevel languageLevel = LanguageLevel.forElement(clazz);
|
||||
|
||||
@@ -597,11 +599,32 @@ public final class PyClassRefactoringUtil {
|
||||
return PyUtil.addElementToStatementList(assignmentStatement, aClass.getStatementList(), true);
|
||||
}
|
||||
|
||||
public static boolean addMetaClassIfNotExist(@NotNull PyClass cls, @NotNull PyClass metaClass, @NotNull TypeEvalContext context) {
|
||||
final String metaClassName = metaClass.getName();
|
||||
if (metaClassName == null) return false;
|
||||
|
||||
final PyType metaClassType = cls.getMetaClassType(false, context);
|
||||
if (metaClassType != null) return false;
|
||||
|
||||
insertImport(cls, metaClass);
|
||||
|
||||
final LanguageLevel languageLevel = LanguageLevel.forElement(cls);
|
||||
if (languageLevel.isPython2()) {
|
||||
addClassAttributeIfNotExist(cls, PyNames.DUNDER_METACLASS, metaClassName);
|
||||
}
|
||||
else {
|
||||
final List<Pair<String, String>> keywordArguments = Collections.singletonList(Pair.create(PyNames.METACLASS, metaClassName));
|
||||
addSuperClassExpressions(cls.getProject(), cls, null, keywordArguments);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static class DynamicNamedElement extends LightElement implements PsiNamedElement {
|
||||
private final PsiFile myFile;
|
||||
private final String myName;
|
||||
|
||||
public DynamicNamedElement(@NotNull PsiFile file, @NotNull String name) {
|
||||
private DynamicNamedElement(@NotNull PsiFile file, @NotNull String name) {
|
||||
super(file.getManager(), file.getLanguage());
|
||||
myName = name;
|
||||
myFile = file;
|
||||
|
||||
+11
-46
@@ -18,7 +18,7 @@ package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiReference;
|
||||
@@ -29,7 +29,6 @@ import com.jetbrains.python.codeInsight.imports.AddImportHelper;
|
||||
import com.jetbrains.python.codeInsight.imports.AddImportHelper.ImportPriority;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyFunctionBuilder;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -114,58 +113,26 @@ class MethodsManager extends MembersManager<PyFunction> {
|
||||
|
||||
// Add ABCMeta to new classes if needed
|
||||
for (final PyClass aClass : classesToAddMetaAbc) {
|
||||
if (addMetaAbcIfNeeded(aClass)) {
|
||||
filesToCheckImport.add(aClass.getContainingFile());
|
||||
final Project project = aClass.getProject();
|
||||
final PsiFile file = aClass.getContainingFile();
|
||||
|
||||
final PyClass abcMetaClass = PyPsiFacade.getInstance(project).createClassByQName("abc." + PyNames.ABC_META_CLASS, aClass);
|
||||
final TypeEvalContext context = TypeEvalContext.userInitiated(project, file);
|
||||
|
||||
if (abcMetaClass != null && PyClassRefactoringUtil.addMetaClassIfNotExist(aClass, abcMetaClass, context)) {
|
||||
filesToCheckImport.add(file);
|
||||
}
|
||||
}
|
||||
|
||||
// Add imports for ABC if needed
|
||||
for (final PsiFile file : filesToCheckImport) {
|
||||
addImportFromAbc(file, PyNames.ABSTRACTMETHOD);
|
||||
addImportFromAbc(file, PyNames.ABC_META_CLASS);
|
||||
AddImportHelper.addOrUpdateFromImportStatement(file, ABC_META_PACKAGE, PyNames.ABSTRACTMETHOD, null, ImportPriority.BUILTIN, null);
|
||||
PyClassRefactoringUtil.optimizeImports(file); //To remove redundant imports
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds metaclass = ABCMeta for class if has no.
|
||||
*
|
||||
* @param aClass class where it should be added
|
||||
* @return true if added. False if class already has metaclass so we did not touch it.
|
||||
*/
|
||||
// TODO: Copy/Paste with PyClass.getMeta..
|
||||
private static boolean addMetaAbcIfNeeded(@NotNull final PyClass aClass) {
|
||||
final PsiFile file = aClass.getContainingFile();
|
||||
final PyType type = aClass.getMetaClassType(TypeEvalContext.userInitiated(aClass.getProject(), file));
|
||||
if (type != null) {
|
||||
return false; //User already has metaclass. He probably knows about metaclasses, so we should not add ABCMeta
|
||||
}
|
||||
final LanguageLevel languageLevel = LanguageLevel.forElement(aClass);
|
||||
if (languageLevel.isPy3K()) { //TODO: Copy/paste, use strategy because we already has the same check in #couldBeAbstract
|
||||
// Add (metaclass= for Py3K
|
||||
PyClassRefactoringUtil
|
||||
.addSuperClassExpressions(aClass.getProject(), aClass, null, Collections.singletonList(Pair.create(PyNames.METACLASS,
|
||||
PyNames.ABC_META_CLASS)));
|
||||
}
|
||||
else {
|
||||
// Add __metaclass__ for Py2
|
||||
PyClassRefactoringUtil.addClassAttributeIfNotExist(aClass, PyNames.DUNDER_METACLASS, PyNames.ABC_META_CLASS);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds import from ABC module
|
||||
*
|
||||
* @param file where to add import
|
||||
* @param nameToImport what to import
|
||||
*/
|
||||
private static void addImportFromAbc(@NotNull final PsiFile file, @NotNull final String nameToImport) {
|
||||
AddImportHelper.addOrUpdateFromImportStatement(file, ABC_META_PACKAGE, nameToImport, null, ImportPriority.BUILTIN, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves methods (as opposite to {@link #makeMethodsAbstract(java.util.Collection, com.jetbrains.python.psi.PyClass...)})
|
||||
* Moves methods (as opposite to {@link #makeMethodsAbstract(Collection, PyClass...)})
|
||||
*
|
||||
* @param from source
|
||||
* @param methodsToMove what to move
|
||||
@@ -211,8 +178,6 @@ class MethodsManager extends MembersManager<PyFunction> {
|
||||
assert flags != null : "Function should be called on method!";
|
||||
|
||||
final boolean py3K = LanguageLevel.forElement(function).isPy3K();
|
||||
|
||||
//TODO: use strategy because we already has the same check in #addMetaAbcIfNeeded
|
||||
return flags.isInstanceMethod() || py3K; //Any method could be made abstract in py3
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
from abc import abstractmethod, ABCMeta
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
|
||||
class A:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# coding=utf-8
|
||||
from abc import abstractmethod, ABCMeta
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
|
||||
class A:
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
from abc import abstractmethod, ABCMeta
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
|
||||
class NewParent(object):
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
from abc import abstractmethod, ABCMeta
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
|
||||
class A:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# coding=utf-8
|
||||
from abc import abstractmethod, ABCMeta
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
|
||||
class A:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from abc import abstractmethod, ABCMeta
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
|
||||
class Parent(object):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from abc import object, abstractmethod, ABCMeta
|
||||
from abc import object, ABCMeta, abstractmethod
|
||||
|
||||
|
||||
class Parent(object, metaclass=ABCMeta):
|
||||
|
||||
Reference in New Issue
Block a user