don't access remote packages synchronously from inspections

This commit is contained in:
Dmitry Jemerov
2012-11-12 14:45:20 +01:00
parent cf1bf4f3c0
commit e6d824c725
5 changed files with 90 additions and 42 deletions
@@ -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);
@@ -169,6 +169,7 @@ public class PyPackageRequirementsInspection extends PyInspection {
if (sdk != null) {
requirements = getTransitiveRequirements(sdk, requirements, new HashSet<PyPackage>());
}
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<PyRequirement> getTransitiveRequirements(@NotNull Sdk sdk, @NotNull Collection<PyRequirement> requirements,
@NotNull Set<PyPackage> visited) {
final Set<PyRequirement> results = new HashSet<PyRequirement>(requirements);
try {
final List<PyPackage> 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<PyPackage> 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<PyRequirement> 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<PyRequirement> requirements = PyPackageManagerImpl.getRequirements(module);
if (requirements != null) {
final List<PyPackage> packages;
try {
packages = manager.getPackages();
}
catch (PyExternalProcessException ignored) {
return null;
}
final List<PyPackage> packages = manager.getPackagesFast();
if (packages == null) return null;
final List<PyRequirement> unsatisfied = new ArrayList<PyRequirement>();
for (PyRequirement req : requirements) {
if (!ignoredPackages.contains(req.getName()) && req.match(packages) == null) {
@@ -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<PyPackage> 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<PyPackage> 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<PyPackage>() {
@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<PyPackage>() {
@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<PyPackage> packages = getPackagesFast();
return packages != null ? findPackageByName(name, packages) : null;
}
private static PyPackage findPackageByName(String name, List<PyPackage> 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
@@ -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() {
@@ -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;
}