All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jetbrains.python.inspections.quickfix.GenerateBinaryStubsFix Maven / Gradle / Ivy

Go to download

A packaging of the IntelliJ Community Edition python-community library. This is release number 1 of trunk branch 142.

The newest version!
/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jetbrains.python.inspections.quickfix;

import com.google.common.collect.Lists;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.execution.process.ProcessOutput;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.progress.Task.Backgroundable;
import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator;
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.util.QualifiedName;
import com.intellij.util.Consumer;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PythonHelpersLocator;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.sdk.InvalidSdkException;
import com.jetbrains.python.sdk.PySdkUtil;
import com.jetbrains.python.sdk.PythonSdkType;
import com.jetbrains.python.sdk.flavors.IronPythonSdkFlavor;
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
import com.jetbrains.python.sdk.skeletons.PySkeletonGenerator;
import com.jetbrains.python.sdk.skeletons.PySkeletonRefresher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * @author yole
 */
public class GenerateBinaryStubsFix implements LocalQuickFix {
  private static final Logger LOG = Logger.getInstance("#" + GenerateBinaryStubsFix.class.getName());

  private final String myQualifiedName;
  private final Sdk mySdk;

  /**
   * Generates pack of fixes available for some unresolved import statement.
   * Be sure to call {@link #isApplicable(com.jetbrains.python.psi.PyImportStatementBase)} first to make sure this statement is supported
   *
   * @param importStatementBase statement to fix
   * @return pack of fixes
   */
  @NotNull
  public static Collection generateFixes(@NotNull final PyImportStatementBase importStatementBase) {
    final List names = importStatementBase.getFullyQualifiedObjectNames();
    final List result = new ArrayList(names.size());
    if (importStatementBase instanceof PyFromImportStatement && names.isEmpty()) {
      final QualifiedName qName = ((PyFromImportStatement)importStatementBase).getImportSourceQName();
      if (qName != null)
        result.add(new GenerateBinaryStubsFix(importStatementBase, qName.toString()));
    }
    for (final String qualifiedName : names) {
      result.add(new GenerateBinaryStubsFix(importStatementBase, qualifiedName));
    }
    return result;
  }

  /**
   * @param importStatementBase statement to fix
   * @param qualifiedName       name should be fixed (one of {@link com.jetbrains.python.psi.PyImportStatementBase#getFullyQualifiedObjectNames()})
   */
  private GenerateBinaryStubsFix(@NotNull final PyImportStatementBase importStatementBase, @NotNull final String qualifiedName) {
    myQualifiedName = qualifiedName;
    mySdk = getPythonSdk(importStatementBase);
  }

  @Override
  @NotNull
  public String getName() {
    return PyBundle.message("sdk.gen.stubs.for.binary.modules", myQualifiedName);
  }

  @Override
  @NotNull
  public String getFamilyName() {
    return "Generate binary stubs";
  }

  @Override
  public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) {
    final PsiFile file = descriptor.getPsiElement().getContainingFile();
    final Backgroundable backgroundable = getFixTask(file);
    ProgressManager.getInstance().runProcessWithProgressAsynchronously(backgroundable, new BackgroundableProcessIndicator(backgroundable));
  }


  /**
   * Returns fix task that is used to generate stubs
   * @param fileToRunTaskIn file where task should run
   * @return task itself
   */
  @NotNull
  public Backgroundable getFixTask(@NotNull final PsiFile fileToRunTaskIn) {
    final Project project = fileToRunTaskIn.getProject();
    final String folder = fileToRunTaskIn.getContainingDirectory().getVirtualFile().getCanonicalPath();
    return new Task.Backgroundable(project, "Generating skeletons for binary module", false) {

      @Override
      public void run(@NotNull ProgressIndicator indicator) {
        indicator.setIndeterminate(true);


        final List assemblyRefs = new ReadAction>() {
          @Override
          protected void run(@NotNull Result> result) throws Throwable {
            result.setResult(collectAssemblyReferences(fileToRunTaskIn));
          }
        }.execute().getResultObject();


        try {
          final PySkeletonRefresher refresher = new PySkeletonRefresher(project, null, mySdk, null, null, folder);

          if (needBinaryList(myQualifiedName)) {
            if (!generateSkeletonsForList(refresher, indicator, folder)) return;
          }
          else {
            //noinspection unchecked
            refresher.generateSkeleton(myQualifiedName, "", assemblyRefs, Consumer.EMPTY_CONSUMER);
          }
          final VirtualFile skeletonDir;
          skeletonDir = LocalFileSystem.getInstance().findFileByPath(refresher.getSkeletonsPath());
          if (skeletonDir != null) {
            skeletonDir.refresh(true, true);
          }
        }
        catch (InvalidSdkException e) {
          LOG.error(e);
        }
      }
    };
  }

  private boolean generateSkeletonsForList(@NotNull final PySkeletonRefresher refresher,
                                           ProgressIndicator indicator,
                                           @Nullable final String currentBinaryFilesPath) throws InvalidSdkException {
    final PySkeletonGenerator generator = new PySkeletonGenerator(refresher.getSkeletonsPath(), mySdk, currentBinaryFilesPath);
    indicator.setIndeterminate(false);
    final String homePath = mySdk.getHomePath();
    if (homePath == null) return false;
    final ProcessOutput runResult = PySdkUtil.getProcessOutput(
      new File(homePath).getParent(),
      new String[]{
        homePath,
        PythonHelpersLocator.getHelperPath("extra_syspath.py"), myQualifiedName},
      PythonSdkType.getVirtualEnvExtraEnv(homePath), 5000
    );
    if (runResult.getExitCode() == 0 && !runResult.isTimeout()) {
      final String extraPath = runResult.getStdout();
      final PySkeletonGenerator.ListBinariesResult binaries = generator.listBinaries(mySdk, extraPath);
      final List names = Lists.newArrayList(binaries.modules.keySet());
      Collections.sort(names);
      final int size = names.size();
      for (int i = 0; i != size; ++i) {
        final String name = names.get(i);
        indicator.setFraction((double)i / size);
        if (needBinaryList(name)) {
          indicator.setText2(name);
          final PySkeletonRefresher.PyBinaryItem item = binaries.modules.get(name);
          final String modulePath = item != null ? item.getPath() : "";
          //noinspection unchecked
          refresher.generateSkeleton(name, modulePath, new ArrayList(), Consumer.EMPTY_CONSUMER);
        }
      }
    }
    return true;
  }

  private static boolean needBinaryList(@NotNull final String qualifiedName) {
    return qualifiedName.startsWith("gi.repository");
  }

  private List collectAssemblyReferences(PsiFile file) {
    if (!(PythonSdkFlavor.getFlavor(mySdk) instanceof IronPythonSdkFlavor)) {
      return Collections.emptyList();
    }
    final List result = new ArrayList();
    file.accept(new PyRecursiveElementVisitor() {
      @Override
      public void visitPyCallExpression(PyCallExpression node) {
        super.visitPyCallExpression(node);
        // TODO: What if user loads it not by literal? We need to ask user for list of DLLs
        if (node.isCalleeText("AddReference", "AddReferenceByPartialName", "AddReferenceByName")) {
          final PyExpression[] args = node.getArguments();
          if (args.length == 1 && args[0] instanceof PyStringLiteralExpression) {
            result.add(((PyStringLiteralExpression)args[0]).getStringValue());
          }
        }
      }
    });
    return result;
  }

  /**
   * Checks if this fix can help you to generate binary stubs
   *
   * @param importStatementBase statement to fix
   * @return true if this fix could work
   */
  public static boolean isApplicable(@NotNull final PyImportStatementBase importStatementBase) {
    if (importStatementBase.getFullyQualifiedObjectNames().isEmpty() &&
        !(importStatementBase instanceof PyFromImportStatement && ((PyFromImportStatement)importStatementBase).isStarImport())) {
      return false;
    }
    final Sdk sdk = getPythonSdk(importStatementBase);
    if (sdk == null) {
      return false;
    }
    final PythonSdkFlavor flavor = PythonSdkFlavor.getFlavor(sdk);
    if (flavor instanceof IronPythonSdkFlavor) {
      return true;
    }
    return isGtk(importStatementBase);
  }

  private static boolean isGtk(@NotNull final PyImportStatementBase importStatementBase) {
    if (importStatementBase instanceof PyFromImportStatement) {
      final QualifiedName qName = ((PyFromImportStatement)importStatementBase).getImportSourceQName();
      if (qName != null && qName.matches("gi", "repository")) {
        return true;
      }
    }
    return false;
  }

  @Nullable
  private static Sdk getPythonSdk(@NotNull final PsiElement element) {
    final Module module = ModuleUtilCore.findModuleForPsiElement(element);
    return (module == null) ? null : PythonSdkType.findPythonSdk(module);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy