initial version of quickfix to trigger generation of IronPython binary stubs

This commit is contained in:
Dmitry Jemerov
2010-04-06 21:42:31 +04:00
parent baecd72fe8
commit 7cf9883735
7 changed files with 178 additions and 36 deletions
@@ -0,0 +1,111 @@
package com.jetbrains.python.actions;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyImportReferenceImpl;
import com.jetbrains.python.sdk.IronPythonSdkFlavor;
import com.jetbrains.python.sdk.PythonSdkFlavor;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author yole
*/
public class GenerateBinaryStubsFix implements LocalQuickFix {
private String myQualifiedName;
private Sdk mySdk;
public GenerateBinaryStubsFix(PsiReference reference) {
myQualifiedName = getReferenceText(reference);
mySdk = getPythonSdk(reference);
}
private static String getReferenceText(PsiReference reference) {
PsiElement element = reference.getElement();
while (element.getParent() instanceof PyReferenceExpression) {
element = element.getParent();
}
return element.getText();
}
@NotNull
public String getName() {
return "Generate stubs for binary module " + myQualifiedName;
}
@NotNull
public String getFamilyName() {
return "GenerateBinaryStubs";
}
public void applyFix(@NotNull Project project, @NotNull final ProblemDescriptor descriptor) {
ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
public void run() {
List<String> assemblyRefs = collectAssemblyReferences(descriptor.getPsiElement().getContainingFile());
final String skeletonPath = PythonSdkType.findSkeletonsPath(mySdk);
PythonSdkType.generateSkeleton(mySdk.getHomePath(), skeletonPath, myQualifiedName, assemblyRefs);
final VirtualFile skeletonDir = LocalFileSystem.getInstance().findFileByPath(skeletonPath);
if (skeletonDir != null) {
skeletonDir.refresh(true, true);
}
}
}, "Generating stubs for binary module", false, project);
}
private List<String> collectAssemblyReferences(PsiFile file) {
if (!(PythonSdkFlavor.getFlavor(mySdk.getHomePath()) instanceof IronPythonSdkFlavor)) {
return Collections.emptyList();
}
final List<String> result = new ArrayList<String>();
file.accept(new PyRecursiveElementVisitor() {
@Override
public void visitPyCallExpression(PyCallExpression node) {
super.visitPyCallExpression(node);
if (node.isCalleeText("AddReference") || node.isCalleeText("AddReferenceByPartialName")) {
final PyExpression[] args = node.getArguments();
if (args.length == 1 && args [0] instanceof PyStringLiteralExpression) {
result.add(((PyStringLiteralExpression) args [0]).getStringValue());
}
}
}
});
return result;
}
public static boolean isApplicable(PsiReference ref) {
if (!(ref instanceof PyImportReferenceImpl)) {
return false;
}
final Sdk sdk = getPythonSdk(ref);
if (sdk == null) {
return false;
}
final PythonSdkFlavor flavor = PythonSdkFlavor.getFlavor(sdk.getHomePath());
if (flavor instanceof IronPythonSdkFlavor) {
return getReferenceText(ref).contains("System");
}
return false;
}
@Nullable
private static Sdk getPythonSdk(PsiReference ref) {
final Module module = ModuleUtil.findModuleForPsiElement(ref.getElement());
return module == null ? null : PythonSdkType.findPythonSdk(module);
}
}
@@ -16,10 +16,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Consumer;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.actions.AddFieldQuickFix;
import com.jetbrains.python.actions.AddImportAction;
import com.jetbrains.python.actions.AddMethodQuickFix;
import com.jetbrains.python.actions.ImportFromExistingFix;
import com.jetbrains.python.actions.*;
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
@@ -373,6 +370,9 @@ public class PyUnresolvedReferencesInspection extends LocalInspectionTool {
hl_type = ProblemHighlightType.LIKE_UNKNOWN_SYMBOL;
}
if (GenerateBinaryStubsFix.isApplicable(reference)) {
actions.add(new GenerateBinaryStubsFix(reference));
}
addPluginQuickFixes(reference, actions);
PsiElement point = node.getLastChild(); // usually the identifier at the end of qual ref
@@ -38,6 +38,13 @@ public interface PyCallExpression extends PyExpression {
@Nullable
PyMarkedFunction resolveCallee();
/**
* Checks if the unqualified name of the callee matches the specified text.
*
* @param name the text to check
* @return true if matches, false otherwise
*/
boolean isCalleeText(@NotNull String name);
/**
* Couples function with a flag describing the way it is called.
@@ -49,6 +49,14 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress
return PyCallExpressionHelper.resolveCallee(this);
}
public boolean isCalleeText(@NotNull String name) {
final PyExpression callee = getCallee();
if (!(callee instanceof PyReferenceExpression)) {
return false;
}
return name.equals(((PyReferenceExpression)callee).getReferencedName());
}
@Override
public String toString() {
return "PyCallExpression: " + PyUtil.getReadableRepr(getCallee(), true); //or: getCalledFunctionReference().getReferencedName();
@@ -102,6 +102,14 @@ public class PyDecoratorImpl extends PyPresentableElementImpl<PyDecoratorStub> i
return callee;
}
public boolean isCalleeText(@NotNull String name) {
final PyExpression callee = getCallee();
if (!(callee instanceof PyReferenceExpression)) {
return false;
}
return name.equals(((PyReferenceExpression)callee).getReferencedName());
}
@Override
public String toString() {
return "PyDecorator: @" + PyUtil.getReadableRepr(getCallee(), true); //getCalledFunctionReference().getReferencedName();
@@ -48,10 +48,7 @@ import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.TreeSet;
import java.util.*;
import static com.jetbrains.python.psi.PyUtil.sure;
@@ -98,8 +95,6 @@ public class PythonSdkType extends SdkType {
return PyBuiltinCache.BUILTIN_FILE;
}
@NonNls static final private String PYTHON_STR = "python";
@NonNls
@Nullable
public String suggestHomePath() {
@@ -352,9 +347,8 @@ public class PythonSdkType extends SdkType {
}
}
// fix skeletons as needed
String url = findSkeletonsUrl(currentSdk);
if (url != null) {
final String path = VfsUtil.urlToPath(url);
final String path = findSkeletonsPath(currentSdk);
if (path != null) {
File stubs_dir = new File(path);
if (!stubs_dir.exists()) {
final ProgressManager progman = ProgressManager.getInstance();
@@ -364,7 +358,7 @@ public class PythonSdkType extends SdkType {
public void run(@NotNull final ProgressIndicator indicator) {
try {
generateBuiltinStubs(currentSdk.getHomePath(), path);
generateBinaryStubs(currentSdk.getHomePath(), path, indicator);
generateBinarySkeletons(currentSdk.getHomePath(), path, indicator);
}
catch (Exception e) {
LOG.error(e);
@@ -410,11 +404,11 @@ public class PythonSdkType extends SdkType {
}
@Nullable
public static String findSkeletonsUrl(Sdk sdk) {
public static String findSkeletonsPath(Sdk sdk) {
final String[] urls = sdk.getRootProvider().getUrls(BUILTIN_ROOT_TYPE);
for (String url : urls) {
if (url.contains(SKELETON_DIR_NAME)) {
return url;
return VfsUtil.urlToPath(url);
}
}
return null;
@@ -528,7 +522,7 @@ public class PythonSdkType extends SdkType {
// regenerate stubs, existing or not
final File stubs_dir = new File(stubs_path);
if (!stubs_dir.exists()) stubs_dir.mkdirs();
generateBinaryStubs(bin_path, stubs_path, indicator);
generateBinarySkeletons(bin_path, stubs_path, indicator);
}
}
@@ -603,8 +597,7 @@ public class PythonSdkType extends SdkType {
* @param stubsRoot where to put results (expected to exist).
* @param indicator ProgressIndicator to update, or null.
*/
public static void generateBinaryStubs(final String binaryPath, final String stubsRoot, ProgressIndicator indicator)
{
public static void generateBinarySkeletons(final String binaryPath, final String stubsRoot, ProgressIndicator indicator) {
if (indicator != null) {
indicator.setText("Generating skeletons of binary libs");
}
@@ -635,19 +628,7 @@ public class PythonSdkType extends SdkType {
indicator.setText2(modname);
}
LOG.info("Skeleton for " + modname);
final ProcessOutput gen_result = SdkUtil.getProcessOutput(
parent_dir,
new String[]{binaryPath, PythonHelpersLocator.getHelperPath(GENERATOR3), "-d", stubsRoot, modname},
getVirtualEnvAdditionalEnv(binaryPath),
RUN_TIMEOUT
);
if (gen_result.getExitCode() != 0) {
StringBuffer sb = new StringBuffer("Skeleton for ");
sb.append(modname).append(" failed. stderr: --");
for (String err_line : gen_result.getStderrLines()) sb.append(err_line).append("\n");
sb.append("--");
LOG.warn(sb.toString());
}
generateSkeleton(binaryPath, stubsRoot, modname, Collections.<String>emptyList());
}
}
}
@@ -661,6 +642,34 @@ public class PythonSdkType extends SdkType {
}
}
public static void generateSkeleton(String binaryPath, String stubsRoot, String modname, List<String> assemblyRefs) {
final String parent_dir = new File(binaryPath).getParent();
List<String> commandLine = new ArrayList<String>();
commandLine.add(binaryPath);
commandLine.add(PythonHelpersLocator.getHelperPath(GENERATOR3));
commandLine.add("-d");
commandLine.add(stubsRoot);
if (!assemblyRefs.isEmpty()) {
commandLine.add("-c");
commandLine.add(StringUtil.join(assemblyRefs, ";"));
}
commandLine.add(modname);
final ProcessOutput gen_result = SdkUtil.getProcessOutput(
parent_dir,
commandLine.toArray(new String[commandLine.size()]),
getVirtualEnvAdditionalEnv(binaryPath),
RUN_TIMEOUT*10
);
if (gen_result.getExitCode() != 0) {
StringBuffer sb = new StringBuffer("Skeleton for ");
sb.append(modname).append(" failed. stderr: --");
for (String err_line : gen_result.getStderrLines()) sb.append(err_line).append("\n");
sb.append("--");
LOG.warn(sb.toString());
}
}
public static List<Sdk> getAllSdks() {
return ProjectJdkTable.getInstance().getSdksOfType(getInstance());
}
@@ -6,7 +6,7 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import org.jetbrains.annotations.NotNull;
import java.io.*;
@@ -30,10 +30,9 @@ public class SkeletonVersionUpdater implements ProjectComponent {
writeVersion(versionFile, SKELETONS_VERSION);
final List<Sdk> sdkList = PythonSdkType.getAllSdks();
for (Sdk sdk : sdkList) {
final String url = PythonSdkType.findSkeletonsUrl(sdk);
final String path = VfsUtil.urlToPath(url);
final String path = PythonSdkType.findSkeletonsPath(sdk);
PythonSdkType.generateBuiltinStubs(sdk.getHomePath(), path);
PythonSdkType.generateBinaryStubs(sdk.getHomePath(), path, ProgressManager.getInstance().getProgressIndicator());
PythonSdkType.generateBinarySkeletons(sdk.getHomePath(), path, ProgressManager.getInstance().getProgressIndicator());
}
}
}