From e6d824c725fdef9e96db482c141176e48d55d526 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Mon, 12 Nov 2012 14:36:02 +0100 Subject: [PATCH] don't access remote packages synchronously from inspections --- .../python/packaging/PyPackageManager.java | 6 ++ .../PyPackageRequirementsInspection.java | 31 ++++---- .../packaging/PyPackageManagerImpl.java | 75 +++++++++++++------ .../python/sdk/PySkeletonRefresher.java | 13 +++- .../testing/VFSTestFrameworkListener.java | 7 +- 5 files changed, 90 insertions(+), 42 deletions(-) diff --git a/python/openapi/src/com/jetbrains/python/packaging/PyPackageManager.java b/python/openapi/src/com/jetbrains/python/packaging/PyPackageManager.java index f9afb731e0b9..863b09549d1b 100644 --- a/python/openapi/src/com/jetbrains/python/packaging/PyPackageManager.java +++ b/python/openapi/src/com/jetbrains/python/packaging/PyPackageManager.java @@ -11,6 +11,12 @@ public abstract class PyPackageManager { return PyPackageManagers.getInstance().forSdk(sdk); } + /** + * Returns true if pip is installed for the specific interpreter; returns false if pip is not + * installed or if it is not currently known whether it's installed (e.g. for a remote interpreter). + * + * @return true if pip is known to be installed, false otherwise. + */ public abstract boolean hasPip(); public abstract void install(String requirementString) throws PyExternalProcessException; public abstract void showInstallationError(Project project, String title, String description); diff --git a/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java b/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java index fe0dcc9cd92a..e510041e4bdb 100644 --- a/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java @@ -169,6 +169,7 @@ public class PyPackageRequirementsInspection extends PyInspection { if (sdk != null) { requirements = getTransitiveRequirements(sdk, requirements, new HashSet()); } + if (requirements == null) return; for (PyRequirement req : requirements) { if (packageName.equalsIgnoreCase(req.getName())) { return; @@ -202,22 +203,21 @@ public class PyPackageRequirementsInspection extends PyInspection { } } - @NotNull + @Nullable private static Set getTransitiveRequirements(@NotNull Sdk sdk, @NotNull Collection requirements, @NotNull Set visited) { final Set results = new HashSet(requirements); - try { - final List packages = ((PyPackageManagerImpl) PyPackageManager.getInstance(sdk)).getPackages(); - for (PyRequirement req : requirements) { - final PyPackage pkg = req.match(packages); - if (pkg != null && !visited.contains(pkg)) { - visited.add(pkg); - results.addAll(getTransitiveRequirements(sdk, pkg.getRequirements(), visited)); - } + final List packages = ((PyPackageManagerImpl) PyPackageManager.getInstance(sdk)).getPackagesFast(); + if (packages == null) return null; + for (PyRequirement req : requirements) { + final PyPackage pkg = req.match(packages); + if (pkg != null && !visited.contains(pkg)) { + visited.add(pkg); + final Set transitive = getTransitiveRequirements(sdk, pkg.getRequirements(), visited); + if (transitive == null) return null; + results.addAll(transitive); } } - catch (PyExternalProcessException ignored) { - } return results; } @@ -237,13 +237,8 @@ public class PyPackageRequirementsInspection extends PyInspection { final PyPackageManagerImpl manager = (PyPackageManagerImpl)PyPackageManager.getInstance(sdk); List requirements = PyPackageManagerImpl.getRequirements(module); if (requirements != null) { - final List packages; - try { - packages = manager.getPackages(); - } - catch (PyExternalProcessException ignored) { - return null; - } + final List packages = manager.getPackagesFast(); + if (packages == null) return null; final List unsatisfied = new ArrayList(); for (PyRequirement req : requirements) { if (!ignoredPackages.contains(req.getName()) && req.match(packages) == null) { diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java index d419cb7c502e..cf137a6a56a4 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java @@ -421,6 +421,28 @@ public class PyPackageManagerImpl extends PyPackageManager { return myPackagesCache != null; } + /** + * Returns the list of packages for the SDK without initiating a remote connection. Returns null + * for a remote interpreter if the list of packages was not loaded. + * + * @return the list of packages or null + */ + @Nullable + public synchronized List getPackagesFast() { + if (myPackagesCache != null) { + return myPackagesCache; + } + if (PySdkUtil.isRemote(mySdk)) { + return null; + } + try { + return getPackages(); + } + catch (PyExternalProcessException e) { + throw new UnsupportedOperationException("can't have PyExternalProcessException when running tool for local SDK"); + } + } + @NotNull public synchronized List getPackages() throws PyExternalProcessException { if (myPackagesCache == null) { @@ -428,28 +450,42 @@ public class PyPackageManagerImpl extends PyPackageManager { throw myExceptionCache; } - try { - final String output = runPythonHelper(PACKAGING_TOOL, list("list")); - myPackagesCache = parsePackagingToolOutput(output); - Collections.sort(myPackagesCache, new Comparator() { - @Override - public int compare(PyPackage aPackage, PyPackage aPackage1) { - return aPackage.getName().compareTo(aPackage1.getName()); - } - }); - } - catch (PyExternalProcessException e) { - myExceptionCache = e; - LOG.info("Error loading packages list: " + e.getMessage(), e); - throw e; - } + loadPackages(); } return myPackagesCache; } + public synchronized void loadPackages() throws PyExternalProcessException { + try { + final String output = runPythonHelper(PACKAGING_TOOL, list("list")); + myPackagesCache = parsePackagingToolOutput(output); + Collections.sort(myPackagesCache, new Comparator() { + @Override + public int compare(PyPackage aPackage, PyPackage aPackage1) { + return aPackage.getName().compareTo(aPackage1.getName()); + } + }); + } + catch (PyExternalProcessException e) { + myExceptionCache = e; + LOG.info("Error loading packages list: " + e.getMessage(), e); + throw e; + } + } + @Nullable public PyPackage findPackage(String name) throws PyExternalProcessException { - for (PyPackage pkg : getPackages()) { + return findPackageByName(name, getPackages()); + } + + @Nullable + public PyPackage findPackageFast(String name) { + final List packages = getPackagesFast(); + return packages != null ? findPackageByName(name, packages) : null; + } + + private static PyPackage findPackageByName(String name, List packages) { + for (PyPackage pkg : packages) { if (name.equals(pkg.getName())) { return pkg; } @@ -458,12 +494,7 @@ public class PyPackageManagerImpl extends PyPackageManager { } public boolean hasPip() { - try { - return findPackage(PACKAGE_PIP) != null; - } - catch (PyExternalProcessException e) { - return false; - } + return findPackageFast(PACKAGE_PIP) != null; } @NotNull diff --git a/python/src/com/jetbrains/python/sdk/PySkeletonRefresher.java b/python/src/com/jetbrains/python/sdk/PySkeletonRefresher.java index f82eb835b23b..f58c7855d534 100644 --- a/python/src/com/jetbrains/python/sdk/PySkeletonRefresher.java +++ b/python/src/com/jetbrains/python/sdk/PySkeletonRefresher.java @@ -24,6 +24,9 @@ import com.intellij.util.SmartList; import com.intellij.util.io.ZipUtil; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; +import com.jetbrains.python.packaging.PyExternalProcessException; +import com.jetbrains.python.packaging.PyPackageManager; +import com.jetbrains.python.packaging.PyPackageManagerImpl; import com.jetbrains.python.psi.resolve.PythonSdkPathCache; import com.jetbrains.python.remote.PythonRemoteInterpreterManager; import org.jetbrains.annotations.NonNls; @@ -283,8 +286,16 @@ public class PySkeletonRefresher { indicate(PyBundle.message("sdk.gen.cleaning.$0", readablePath)); cleanUpSkeletons(skeletonsDir); } + if (PySdkUtil.isRemote(mySdk)) { + try { + ((PyPackageManagerImpl) PyPackageManager.getInstance(mySdk)).loadPackages(); + } + catch (PyExternalProcessException e) { + // ignore - already logged + } + } - if (mustUpdateBuiltins && myProject != null) { + if ((mustUpdateBuiltins || PySdkUtil.isRemote(mySdk)) && myProject != null) { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { diff --git a/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java b/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java index 00480374b637..316d1589d19f 100644 --- a/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java +++ b/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java @@ -19,6 +19,7 @@ import com.jetbrains.python.PyNames; import com.jetbrains.python.packaging.PyExternalProcessException; import com.jetbrains.python.packaging.PyPackageManager; import com.jetbrains.python.packaging.PyPackageManagerImpl; +import com.jetbrains.python.sdk.PySdkUtil; import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; @@ -54,6 +55,9 @@ public class VFSTestFrameworkListener implements ApplicationComponent, Persisten if (!containsAt && !containsNose && !containsPy) continue; SDKLOOP: for (Sdk sdk : PythonSdkType.getAllSdks()) { + if (PySdkUtil.isRemote(sdk)) { + continue; + } for (String root : sdk.getRootProvider().getUrls(OrderRootType.CLASSES)) { if (path.contains(root)) { if (containsNose) { @@ -63,7 +67,8 @@ public class VFSTestFrameworkListener implements ApplicationComponent, Persisten else if (containsPy) { updateTestFrameworks(sdk, PyNames.PY_TEST); break SDKLOOP; - } else { + } + else { updateTestFrameworks(sdk, PyNames.AT_TEST); break SDKLOOP; }