Merge remote-tracking branch 'origin/master' into pycharm/docker
authorDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Tue, 29 Sep 2015 22:35:29 +0000 (00:35 +0200)
committerDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Tue, 29 Sep 2015 22:35:29 +0000 (00:35 +0200)
1  2 
platform/platform-impl/src/com/intellij/remote/RemoteConnectionCredentialsWrapper.java
python/src/META-INF/python-core.xml
python/src/com/jetbrains/python/debugger/PyDebugProcess.java
python/src/com/jetbrains/python/remote/PythonRemoteInterpreterManager.java
python/src/com/jetbrains/python/sdk/PythonSdkType.java

index e9c08ff45b2490ec90fb85314a679676d5093eb9,e110044eda78629ec3015ffeafc0cb96b4cae702..83fbfaa7397c08c354b3777b8b6f36c716412177
@@@ -29,7 -29,6 +29,7 @@@ import org.jetbrains.annotations.NotNul
  public class RemoteConnectionCredentialsWrapper {
    public static final String VAGRANT_PREFIX = "vagrant://";
    public static final String SFTP_DEPLOYMENT_PREFIX = "sftp://";
 +  public static final String DOCKER_PREFIX = "docker://";
  
    /**
     * Connection types
@@@ -37,7 -36,6 +37,7 @@@
    public final Key<VagrantBasedCredentialsHolder> VAGRANT_BASED_CREDENTIALS = Key.create("VAGRANT_BASED_CREDENTIALS");
    public final Key<WebDeploymentCredentialsHolder> WEB_DEPLOYMENT_BASED_CREDENTIALS = Key.create("WEB_DEPLOYMENT_BASED_CREDENTIALS");
    public final Key<RemoteCredentialsHolder> PLAIN_SSH_CREDENTIALS = Key.create("PLAIN_SSH_CREDENTIALS");
 +  public final Key<DockerCredentialsHolder> DOCKER_CREDENTIALS = Key.create("DOCKER_CREDENTIALS");
  
    private UserDataHolderBase myCredentialsTypeHolder = new UserDataHolderBase();
  
      return myCredentialsTypeHolder.getUserData(WEB_DEPLOYMENT_BASED_CREDENTIALS);
    }
  
 +  private DockerCredentialsHolder getDockerCredentials() {
 +    return myCredentialsTypeHolder.getUserData(DOCKER_CREDENTIALS);
 +  }
 +
    private boolean isVagrantConnection() {
      return getVagrantCredentials() != null;
    }
@@@ -86,9 -80,6 +86,9 @@@
      return getWebDeploymentCredentials() != null;
    }
  
 +  private boolean isDockerConnection() {
 +    return getDockerCredentials() != null;
 +  }
  
    public Object getConnectionKey() {
      if (isVagrantConnection()) {
        public void deployment(@NotNull WebDeploymentCredentialsHolder cred) {
          cred.save(rootElement);
        }
 +
 +      @Override
 +      public void docker(@NotNull DockerCredentialsHolder cred) {
 +        cred.save(rootElement);
 +      }
      });
    }
  
        public void deployment(@NotNull WebDeploymentCredentialsHolder cred) {
          copy.setWebDeploymentCredentials(getWebDeploymentCredentials());
        }
 +
 +      @Override
 +      public void docker(@NotNull DockerCredentialsHolder credentials) {
 +        copy.setDockerDeploymentCredentials(getDockerCredentials());
 +      }
      });
    }
  
  
        @Override
        public void vagrant(@NotNull VagrantBasedCredentialsHolder cred) {
-         result.set(VAGRANT_PREFIX + cred.getVagrantFolder());
+         result.set(VAGRANT_PREFIX + cred.getVagrantFolder()
+                    + (StringUtil.isNotEmpty(cred.getMachineName()) ?
+                       "@" + cred.getMachineName() : ""));
        }
  
        @Override
        public void deployment(@NotNull WebDeploymentCredentialsHolder cred) {
          result.set(constructSftpCredentialsFullPath(cred.getSshCredentials()));
        }
 +
 +      @Override
 +      public void docker(@NotNull DockerCredentialsHolder cred) {
 +        // TODO [Docker] review
 +        String name = StringUtil.isNotEmpty(cred.getContainerName()) ? cred.getContainerName() : cred.getImageName();
 +        result.set(DOCKER_PREFIX + name + "/");
 +      }
      });
  
      return result.get();
      else if (isWebDeploymentConnection()) {
        acceptor.deployment(getWebDeploymentCredentials());
      }
 +    else if (isDockerConnection()) {
 +      acceptor.docker(getDockerCredentials());
 +    }
      else {
        throw unknownConnectionType();
      }
        public void deployment(@NotNull WebDeploymentCredentialsHolder cred) {
          result.set("(" + constructSftpCredentialsFullPath(cred.getSshCredentials()) + interpreterPath + ")");
        }
 +
 +      @Override
 +      public void docker(@NotNull DockerCredentialsHolder credentials) {
 +        String containerName = StringUtil.isNotEmpty(credentials.getContainerName())
 +                               ? credentials.getContainerName() + " " : "";
 +        result.set("Docker " + containerName + "(" + credentials.getImageName() + ")");
 +      }
      });
  
      return result.get();
    }
 +
 +
 +  public void setDockerDeploymentCredentials(DockerCredentialsHolder credentials) {
 +    myCredentialsTypeHolder = new UserDataHolderBase();
 +    myCredentialsTypeHolder.putUserData(DOCKER_CREDENTIALS, credentials);
 +  }
  }
index 4a000ebcd3d49b19608d702f818c5167f447ce5e,52b9200c09476fb38eb9d78880544c10c96df7f3..dedf9b380cfa6b42bd2ec8cb18a69bf7619b7295
@@@ -41,6 -41,7 +41,7 @@@
      <lang.documentationProvider language="Python" implementationClass="com.jetbrains.python.documentation.PythonDocumentationProvider"/>
      <lang.documentationProvider language="Python" implementationClass="com.jetbrains.python.console.PydevDocumentationProvider"/>
      <lang.emacs language="Python" implementationClass="com.jetbrains.python.editor.PyEmacsHandler"/>
+     <highlightRangeExtension implementation="com.jetbrains.python.validation.DumbAwareHighlightingAnnotator"/>
      <annotator language="Python" implementationClass="com.jetbrains.python.validation.PyAnnotatingVisitor"/>
      <annotator language="Python" implementationClass="com.jetbrains.python.validation.PyDumbAwareAnnotator"/>
      <quoteHandler fileType="Python" className="com.jetbrains.python.editor.PythonQuoteHandler"/>
@@@ -92,6 -93,8 +93,8 @@@
  
      <applicationService serviceInterface="com.jetbrains.python.packaging.PyPackageService"
                          serviceImplementation="com.jetbrains.python.packaging.PyPackageService"/>
+     <applicationService serviceInterface="com.jetbrains.python.packaging.PyCondaPackageService"
+                         serviceImplementation="com.jetbrains.python.packaging.PyCondaPackageService"/>
      <applicationService serviceInterface="com.jetbrains.python.module.PyModuleService"
                          serviceImplementation="com.jetbrains.python.module.PyModuleServiceImpl"/>
  
      <search.optionContributor implementation="com.jetbrains.python.configuration.PySearchableOptionContributor"/>
      <moduleService serviceInterface="com.jetbrains.python.documentation.PyDocumentationSettings"
                     serviceImplementation="com.jetbrains.python.documentation.PyDocumentationSettings"/>
-     <psi.referenceContributor implementation="com.jetbrains.python.documentation.DocStringReferenceContributor"/>
-     <completion.contributor language="Python" implementationClass="com.jetbrains.python.documentation.DocStringTagCompletionContributor"/>
+     <psi.referenceContributor implementation="com.jetbrains.python.documentation.docstrings.DocStringReferenceContributor"/>
+     <completion.contributor language="Python" implementationClass="com.jetbrains.python.documentation.docstrings.DocStringTagCompletionContributor"/>
+     <completion.contributor language="Python" implementationClass="com.jetbrains.python.documentation.docstrings.DocStringSectionHeaderCompletionContributor"/>
  
      <projectService serviceInterface="com.intellij.psi.search.ProjectScopeBuilder"
                      serviceImplementation="com.jetbrains.python.psi.search.PyProjectScopeBuilder"
      <additionalTextAttributes scheme="Darcula" file="colorSchemes/PythonDarcula.xml"/>
  
      <postStartupActivity implementation="com.jetbrains.python.sdk.PythonSdkUpdater"/>
-     <postStartupActivity implementation="com.jetbrains.python.packaging.PyPIPackagesUpdater"/>
-     <postStartupActivity implementation="com.jetbrains.python.testing.PyTestRunnerUpdater"/>
+     <postStartupActivity implementation="com.jetbrains.python.packaging.PyPackagesUpdater"/>
+     <directoryProjectConfigurator implementation="com.jetbrains.python.testing.PyIntegratedToolsProjectConfigurator" id="integratedTools" order="after sdk"/>
  
      <macro implementation="com.jetbrains.python.sdk.InterpreterDirectoryMacro"/>
  
  
      <!-- typing -->
      <multiHostInjector implementation="com.jetbrains.python.codeInsight.PyTypingAnnotationInjector"/>
+     <lang.parserDefinition language="PythonStub" implementationClass="com.jetbrains.python.pyi.PyiParserDefinition"/>
+     <fileTypeFactory implementation="com.jetbrains.python.pyi.PyiFileTypeFactory"/>
+     <codeInsight.lineMarkerProvider language="Python" implementationClass="com.jetbrains.python.pyi.PyiRelatedItemLineMarkerProvider"/>
  
      <lang.inspectionSuppressor language="Python" implementationClass="com.jetbrains.python.inspections.PyInspectionsSuppressor"/>
      <refactoring.invertBoolean implementation="com.jetbrains.python.refactoring.invertBoolean.PyInvertBooleanDelegate"/>
      <extensionPoint qualifiedName="Pythonid.runConfigurationExtension" interface="com.jetbrains.python.run.PythonRunConfigurationExtension"/>
      <extensionPoint qualifiedName="Pythonid.visitorFilter" beanClass="com.intellij.lang.LanguageExtensionPoint"/>
      <extensionPoint qualifiedName="Pythonid.remoteInterpreterManager" interface="com.jetbrains.python.remote.PythonRemoteInterpreterManager"/>
 +    <extensionPoint qualifiedName="Pythonid.remoteProcessStarterManager" interface="com.jetbrains.python.run.PyRemoteProcessStarterManager"/>
      <extensionPoint qualifiedName="Pythonid.keywordArgumentProvider" interface="com.jetbrains.python.psi.impl.PyKeywordArgumentProvider"/>
      <extensionPoint qualifiedName="Pythonid.canonicalPathProvider" interface="com.jetbrains.python.psi.resolve.PyCanonicalPathProvider"/>
      <extensionPoint qualifiedName="Pythonid.templateContextProvider" interface="com.jetbrains.python.templateLanguages.TemplateContextProvider"/>
      <dumbAnnotator implementation="com.jetbrains.python.validation.GeneratorInArgumentListAnnotator"/>
      <dumbAnnotator implementation="com.jetbrains.python.validation.StarAnnotator"/>
      <dumbAnnotator implementation="com.jetbrains.python.validation.StringLiteralQuotesAnnotator"/>
+     <dumbAnnotator implementation="com.jetbrains.python.validation.DumbAwareHighlightingAnnotator"/>
  
      <customTargetExpressionStubType implementation="com.jetbrains.python.psi.impl.stubs.PropertyStubType"/>
      <dialectsTokenSetContributor implementation="com.jetbrains.python.PythonTokenSetContributor"/>
  
      <!-- typing -->
      <typeProvider implementation="com.jetbrains.python.codeInsight.PyTypingTypeProvider"/>
+     <typeProvider implementation="com.jetbrains.python.pyi.PyiTypeProvider"/>
+     <pyModuleMembersProvider implementation="com.jetbrains.python.pyi.PyiModuleMembersProvider"/>
+     <pyClassMembersProvider implementation="com.jetbrains.python.pyi.PyiClassMembersProvider"/>
+     <visitorFilter language="PythonStub" implementationClass="com.jetbrains.python.pyi.PyiVisitorFilter"/>
  
      <typeProvider implementation="com.jetbrains.python.debugger.PyCallSignatureTypeProvider"/>
      <pyReferenceResolveProvider implementation="com.jetbrains.python.psi.resolve.PythonBuiltinReferenceResolveProvider"/>
index a1a71e3bb9abb9063d959b56c8c3092d3b12b874,fc6fc5092bae36ac2eef348e956fbf9cf8b9201d..adfb44e1625f62bb6dea6216f2fcb79a73f6b9e1
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * Copyright 2000-2014 JetBrains s.r.o.
+  * Copyright 2000-2015 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.
@@@ -35,6 -35,7 +35,6 @@@ import com.intellij.openapi.progress.Ta
  import com.intellij.openapi.project.Project;
  import com.intellij.openapi.ui.Messages;
  import com.intellij.openapi.util.Key;
 -import com.intellij.openapi.util.Pair;
  import com.intellij.openapi.util.Ref;
  import com.intellij.openapi.vfs.VirtualFile;
  import com.intellij.psi.PsiElement;
@@@ -52,7 -53,6 +52,6 @@@ import com.intellij.xdebugger.breakpoin
  import com.intellij.xdebugger.breakpoints.XLineBreakpoint;
  import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
  import com.intellij.xdebugger.frame.XValueChildrenList;
- import com.intellij.xdebugger.impl.XDebugSessionImpl;
  import com.intellij.xdebugger.impl.XSourcePositionImpl;
  import com.intellij.xdebugger.stepping.XSmartStepIntoHandler;
  import com.jetbrains.python.PythonFileType;
@@@ -293,7 -293,9 +292,7 @@@ public class PyDebugProcess extends XDe
  
    protected static int getRemoteTunneledPort(int localPort, @NotNull RemoteProcessHandlerBase handler) throws IOException {
      try {
 -      Pair<String, Integer> remoteSocket = handler.obtainRemoteSocket();
 -      handler.addRemoteForwarding(remoteSocket.getSecond(), localPort);
 -      return remoteSocket.getSecond();
 +      return handler.getRemoteSocket(localPort).getSecond();
      }
      catch (Exception e) {
        throw new IOException(e);
  
    public void startStepIntoMyCode() {
      if (!checkCanPerformCommands()) return;
-     XDebugSessionImpl session = (XDebugSessionImpl)getSession();
-     session.doResume();
+     getSession().sessionResumed();
      passToCurrentThread(ResumeOrStepCommand.Mode.STEP_INTO_MY_CODE);
    }
  
index a83446432f49c267ab056f9e2926cfc5f062d7ba,218fcf3a01aad056ec8036e1772a396a7afe0d86..3a7da17edf3be07e5216815e03f1c636e4c34fd6
@@@ -59,20 -59,12 +59,20 @@@ public abstract class PythonRemoteInter
  
    public final static Key<PathMapper> PATH_MAPPING_SETTINGS_KEY = Key.create("PATH_MAPPING_SETTINGS_KEY");
  
 +  /**
 +   * @deprecated use {@link com.jetbrains.python.run.PyRemoteProcessStarterManager#startRemoteProcess(Project, GeneralCommandLine, PythonRemoteInterpreterManager, PyRemoteSdkAdditionalDataBase, PyRemotePathMapper)}
 +   */
 +  @Deprecated
    public abstract ProcessHandler startRemoteProcess(@Nullable Project project,
                                                      @NotNull PyRemoteSdkCredentials data,
                                                      @NotNull GeneralCommandLine commandLine,
                                                      @NotNull PyRemotePathMapper pathMapper)
      throws RemoteSdkException;
  
 +  /**
 +   * @deprecated use {@link com.jetbrains.python.run.PyRemoteProcessStarterManager#startRemoteProcess(Project, GeneralCommandLine, PythonRemoteInterpreterManager, PyRemoteSdkAdditionalDataBase, PyRemotePathMapper)}
 +   */
 +  @Deprecated
    public abstract ProcessHandler startRemoteProcessWithPid(@Nullable Project project,
                                                             @NotNull PyRemoteSdkCredentials data,
                                                             @NotNull GeneralCommandLine commandLine,
@@@ -83,9 -75,6 +83,9 @@@
                                      NullableConsumer<Sdk> sdkCallback);
  
  
 +  /**
 +   * @deprecated use {@link com.jetbrains.python.run.PyRemoteProcessStarterManager#executeRemoteProcess(Project, String[], String, PythonRemoteInterpreterManager, PyRemoteSdkAdditionalDataBase, PyRemotePathMapper, boolean)}
 +   */
    public abstract ProcessOutput runRemoteProcess(@Nullable Project project,
                                                   RemoteSdkCredentials data,
                                                   @NotNull PyRemotePathMapper pathMapper,
                                                   boolean askForSudo)
      throws RemoteSdkException;
  
 +  /**
 +   * @deprecated use {@link com.jetbrains.python.run.PyRemoteProcessStarterManager#executeRemoteProcess(Project, String[], String, PythonRemoteInterpreterManager, PyRemoteSdkAdditionalDataBase, PyRemotePathMapper, boolean)}
 +   */
 +  @Deprecated
 +  public abstract ProcessOutput runRemoteProcess(@Nullable Project project,
 +                                                 RemoteSdkCredentials data,
 +                                                 @NotNull PyRemotePathMapper pathMapper,
 +                                                 String[] command,
 +                                                 @Nullable String workingDir,
 +                                                 boolean askForSudo, String sdkHomePath)
 +    throws RemoteSdkException;
 +
    @NotNull
    public abstract RemoteSshProcess createRemoteProcess(@Nullable Project project,
                                                         @NotNull PyRemoteSdkCredentials data,
    public abstract RemoteSdkCredentialsProducer<PyRemoteSdkCredentials> getRemoteSdkCredentialsProducer(Function<RemoteCredentials, PyRemoteSdkCredentials> credentialsTransformer,
                                                                                                         RemoteConnectionCredentialsWrapper connectionWrapper);
  
+   public abstract String getInterpreterVersion(@Nullable Project project, PyRemoteSdkAdditionalDataBase data) throws RemoteSdkException;
    public static class PyRemoteInterpreterExecutionException extends ExecutionException {
  
      public PyRemoteInterpreterExecutionException() {
  
    public abstract void runVagrant(@NotNull String vagrantFolder, @Nullable String machineName) throws ExecutionException;
  }
 -
index ab1738fc80fff98131a7127e8a1d75d96ac2ce7f,c08e908d8cb066917b7b85037472e46f3cc46bd9..4bb9cefe4ad69319f1350abe03e382201f468f82
@@@ -16,6 -16,7 +16,7 @@@
  package com.jetbrains.python.sdk;
  
  import com.google.common.collect.ImmutableMap;
+ import com.google.common.collect.Lists;
  import com.intellij.execution.ExecutionException;
  import com.intellij.execution.configurations.GeneralCommandLine;
  import com.intellij.execution.process.ProcessOutput;
@@@ -30,6 -31,7 +31,7 @@@ import com.intellij.notification.Notifi
  import com.intellij.openapi.actionSystem.CommonDataKeys;
  import com.intellij.openapi.application.Application;
  import com.intellij.openapi.application.ApplicationManager;
+ import com.intellij.openapi.application.ModalityState;
  import com.intellij.openapi.application.PathManager;
  import com.intellij.openapi.diagnostic.Logger;
  import com.intellij.openapi.fileChooser.FileChooserDescriptor;
@@@ -45,10 -47,7 +47,7 @@@ import com.intellij.openapi.projectRoot
  import com.intellij.openapi.roots.ModuleRootManager;
  import com.intellij.openapi.roots.OrderRootType;
  import com.intellij.openapi.ui.Messages;
- import com.intellij.openapi.util.Comparing;
- import com.intellij.openapi.util.Key;
- import com.intellij.openapi.util.Ref;
- import com.intellij.openapi.util.SystemInfo;
+ import com.intellij.openapi.util.*;
  import com.intellij.openapi.util.io.FileSystemUtil;
  import com.intellij.openapi.util.io.FileUtil;
  import com.intellij.openapi.util.text.CharFilter;
@@@ -63,12 -62,14 +62,14 @@@ import com.intellij.remote.*
  import com.intellij.util.ArrayUtil;
  import com.intellij.util.Consumer;
  import com.intellij.util.NullableConsumer;
+ import com.intellij.util.ui.UIUtil;
  import com.jetbrains.python.PyBundle;
  import com.jetbrains.python.PyNames;
  import com.jetbrains.python.PythonFileType;
- import com.jetbrains.python.PythonHelpersLocator;
+ import com.jetbrains.python.PythonHelper;
  import com.jetbrains.python.codeInsight.userSkeletons.PyUserSkeletonsUtil;
  import com.jetbrains.python.facet.PythonFacetSettings;
+ import com.jetbrains.python.packaging.PyCondaPackageManagerImpl;
  import com.jetbrains.python.psi.LanguageLevel;
  import com.jetbrains.python.psi.impl.PyBuiltinCache;
  import com.jetbrains.python.psi.search.PyProjectScopeBuilder;
@@@ -242,7 -243,7 +243,7 @@@ public class PythonSdkType extends SdkT
  
    public static boolean isVagrant(@Nullable Sdk sdk) {
      if (sdk != null && sdk.getSdkAdditionalData() instanceof PyRemoteSdkAdditionalDataBase) {
-       PyRemoteSdkAdditionalDataBase data = (PyRemoteSdkAdditionalDataBase) sdk.getSdkAdditionalData();
+       PyRemoteSdkAdditionalDataBase data = (PyRemoteSdkAdditionalDataBase)sdk.getSdkAdditionalData();
  
        return data.getRemoteConnectionType() == CredentialsType.VAGRANT;
      }
      return path != null && getVirtualEnvRoot(path) != null;
    }
  
+   public static boolean isCondaVirtualEnv(Sdk sdk) {
+     final String path = sdk.getHomePath();
+     return path != null && PyCondaPackageManagerImpl.isCondaVEnv(sdk);
+   }
    @Nullable
    public Sdk getVirtualEnvBaseSdk(Sdk sdk) {
      if (isVirtualEnv(sdk)) {
      return PythonSdkAdditionalData.load(currentSdk, additional);
    }
  
-   @Nullable
-   public static String findSkeletonsPath(Sdk sdk) {
-     final String[] urls = sdk.getRootProvider().getUrls(BUILTIN_ROOT_TYPE);
-     for (String url : urls) {
-       if (isSkeletonsPath(url)) {
-         return VfsUtilCore.urlToPath(url);
-       }
-     }
-     return null;
-   }
    public static boolean isSkeletonsPath(String path) {
      return path.contains(SKELETON_DIR_NAME);
    }
      return true;  // run setupSdkPaths only once (from PythonSdkDetailsStep). Skip this from showCustomCreateUI
    }
  
-   public static void setupSdkPaths(Sdk sdk, @Nullable Project project, @Nullable Component ownerComponent) {
-     final SdkModificator sdkModificator = sdk.getSdkModificator();
-     final boolean success = setupSdkPaths(project, ownerComponent, sdk, sdkModificator);
-     if (success) {
-       sdkModificator.commitChanges();
-     }
-     else {
-       Messages.showErrorDialog(
-         project,
-         PyBundle.message("MSG.cant.setup.sdk.$0", FileUtil.toSystemDependentName(sdk.getSdkModificator().getHomePath())),
-         PyBundle.message("MSG.title.bad.sdk")
-       );
-     }
+   public static void setupSdkPaths(@NotNull final Sdk sdk,
+                                    @Nullable final Project project,
+                                    @Nullable final Component ownerComponent,
+                                    @NotNull final SdkModificator sdkModificator) {
+     doSetupSdkPaths(project, ownerComponent, PySdkUpdater.fromSdkModificator(sdk, sdkModificator));
    }
  
-   public static boolean setupSdkPaths(@Nullable final Project project,
-                                       @Nullable final Component ownerComponent,
-                                       @NotNull final Sdk sdk,
-                                       @NotNull final SdkModificator sdkModificator) {
-     if (isRemote(sdk) && project == null && ownerComponent == null) {
+   public static void setupSdkPaths(final Sdk sdk, @Nullable final Project project, @Nullable final Component ownerComponent) {
+     ApplicationManager.getApplication().invokeLater(new Runnable() {
+       @Override
+       public void run() {
+         try {
+           final boolean success = doSetupSdkPaths(project, ownerComponent, PySdkUpdater.fromSdkPath(sdk.getHomePath()));
+           if (!success) {
+             Messages.showErrorDialog(
+               project,
+               PyBundle.message("MSG.cant.setup.sdk.$0", FileUtil.toSystemDependentName(sdk.getSdkModificator().getHomePath())),
+               PyBundle.message("MSG.title.bad.sdk")
+             );
+           }
+         }
+         catch (PySdkUpdater.PySdkNotFoundException e) {
+           // sdk was removed from sdk table so no need to setup paths
+         }
+       }
+     }, ModalityState.NON_MODAL);
+   }
+   private static boolean doSetupSdkPaths(@Nullable final Project project,
+                                          @Nullable final Component ownerComponent,
+                                          @NotNull final PySdkUpdater sdkUpdater) {
+     if (isRemote(sdkUpdater.getSdk()) && project == null && ownerComponent == null) {
        LOG.error("For refreshing skeletons of remote SDK, either project or owner component must be specified");
      }
      final ProgressManager progressManager = ProgressManager.getInstance();
-     final Ref<Boolean> sdkPathsUpdatedRef = new Ref<Boolean>(false);
-     final Task.Modal setupTask = new Task.Modal(project, "Setting up library files for " + sdk.getName(), false) {
-       public void run(@NotNull final ProgressIndicator indicator) {
-         sdkModificator.removeAllRoots();
-         try {
-           updateSdkRootsFromSysPath(sdk, sdkModificator, indicator);
-           updateUserAddedPaths(sdk, sdkModificator, indicator);
-           PythonSdkUpdater.getInstance().markAlreadyUpdated(sdk.getHomePath());
-           sdkPathsUpdatedRef.set(true);
-         }
-         catch (InvalidSdkException ignored) {
+     boolean sdkPathsUpdated = UIUtil.<Boolean>invokeAndWaitIfNeeded(
+       new Computable<Boolean>() {
+         @Override
+         public Boolean compute() {
+           return updateSdkPaths(sdkUpdater);
          }
        }
-     };
-     progressManager.run(setupTask);
-     final Boolean sdkPathsUpdated = sdkPathsUpdatedRef.get();
+     );
      final Application application = ApplicationManager.getApplication();
      if (sdkPathsUpdated && !application.isUnitTestMode()) {
        application.invokeLater(new Runnable() {
              @Override
              public void run(@NotNull ProgressIndicator indicator) {
                try {
-                 final String skeletonsPath = getSkeletonsPath(PathManager.getSystemPath(), sdk.getHomePath());
-                 PythonSdkUpdater.updateSdk(project, ownerComponent, sdk, skeletonsPath);
+                 PythonSdkUpdater.updateSdk(project, ownerComponent, sdkUpdater);
                }
                catch (InvalidSdkException e) {
                  // If the SDK is invalid, the user should worry about the SDK itself, not about skeletons generation errors
-                 if (isVagrant(sdk)) {
+                 if (isVagrant(sdkUpdater.getSdk())) {
                    notifyRemoteSdkSkeletonsFail(e, new Runnable() {
                      @Override
                      public void run() {
-                       setupSdkPaths(project, ownerComponent, sdk, sdkModificator);
+                       setupSdkPaths(sdkUpdater.getSdk(), project, ownerComponent);
                      }
                    });
                  }
-                 else if (!isInvalid(sdk)) {
+                 else if (!isInvalid(sdkUpdater.getSdk())) {
                    LOG.error(e);
                  }
                }
      return sdkPathsUpdated;
    }
  
+   @NotNull
+   public static Boolean updateSdkPaths(@NotNull PySdkUpdater sdkUpdater) {
+     sdkUpdater.modifySdk(new PySdkUpdater.SdkModificationProcessor() {
+       @Override
+       public void process(@NotNull Sdk sdk,
+                           @NotNull SdkModificator sdkModificator) {
+         sdkModificator.removeAllRoots();
+       }
+     });
+     try {
+       updateSdkRootsFromSysPath(sdkUpdater);
+       updateUserAddedPaths(sdkUpdater);
+       PythonSdkUpdater.getInstance()
+         .markAlreadyUpdated(sdkUpdater.getHomePath());
+       return true;
+     }
+     catch (InvalidSdkException ignored) {
+     }
+     return false;
+   }
    public static void notifyRemoteSdkSkeletonsFail(final InvalidSdkException e, @Nullable final Runnable restartAction) {
      NotificationListener notificationListener;
  
  
      Notifications.Bus.notify(
        new Notification(
-         SKELETONS_TOPIC, "Couldn't refresh skeletons for remote interpreter", e.getMessage() + "\n<a href=\"#\">Launch vagrant and refresh skeletons</a>",
+         SKELETONS_TOPIC, "Couldn't refresh skeletons for remote interpreter",
+         e.getMessage() + "\n<a href=\"#\">Launch vagrant and refresh skeletons</a>",
          NotificationType.WARNING,
          notificationListener
        )
  
    private final static Pattern PYTHON_NN_RE = Pattern.compile("python\\d\\.\\d.*");
  
-   public static void updateSdkRootsFromSysPath(Sdk sdk, SdkModificator sdkModificator, ProgressIndicator indicator)
+   public static void updateSdkRootsFromSysPath(PySdkUpdater sdkUpdater)
      throws InvalidSdkException {
      Application application = ApplicationManager.getApplication();
      boolean not_in_unit_test_mode = (application != null && !application.isUnitTestMode());
  
-     String sdkHome = sdkModificator.getHomePath();
+     String sdkHome = sdkUpdater.getHomePath();
      assert sdkHome != null;
      final String sep = File.separator;
-     // we have a number of lib dirs, those listed in python's sys.path
-     if (indicator != null) {
-       indicator.setText("Adding library roots");
-     }
      // Add folders from sys.path
-     if (!PySdkUtil.isRemote(sdk)) { //no sense to add roots of remote sdk
+     if (!PySdkUtil.isRemote(sdkUpdater.getSdk())) { //no sense to add roots of remote sdk
        final List<String> paths = getSysPath(sdkHome);
        if (paths.size() > 0) {
          // add every path as root.
          for (String path : paths) {
            if (!path.contains(sep)) continue; // TODO: interpret possible 'special' paths reasonably
-           if (indicator != null) {
-             indicator.setText2(path);
-           }
-           addSdkRoot(sdkModificator, path);
+           addSdkRoot(sdkUpdater, path);
          }
        }
      }
  
-     PyUserSkeletonsUtil.addUserSkeletonsRoot(sdkModificator);
-     addSkeletonsRoot(sdkModificator, sdkHome);
+     PyUserSkeletonsUtil.addUserSkeletonsRoot(sdkUpdater);
+     addSkeletonsRoot(sdkUpdater, sdkHome);
  
      if (not_in_unit_test_mode) {
        File venv_root = getVirtualEnvRoot(sdkHome);
            }
          }
        }
-       addHardcodedPaths(sdkModificator);
+       addHardcodedPaths(sdkUpdater);
      }
    }
  
-   public static void updateUserAddedPaths(Sdk sdk, SdkModificator sdkModificator, ProgressIndicator indicator)
+   public static void updateUserAddedPaths(PySdkUpdater sdkUpdater)
      throws InvalidSdkException {
-     if (indicator != null) {
-       indicator.setText("Adding user-added roots");
-     }
-     SdkAdditionalData data = sdk.getSdkAdditionalData();
+     SdkAdditionalData data = sdkUpdater.getSdk().getSdkAdditionalData();
      if (data instanceof PythonSdkAdditionalData) {
        for (VirtualFile file : ((PythonSdkAdditionalData)data).getAddedPathFiles()) {
-         addSdkRoot(sdkModificator, file);
+         addSdkRoot(sdkUpdater, file);
        }
      }
    }
  
-   private static void addSkeletonsRoot(@NotNull SdkModificator sdkModificator, String sdkHome) {
+   private static void addSkeletonsRoot(@NotNull PySdkUpdater sdkUpdater, String sdkHome) {
      @NonNls final String skeletonsPath = getSkeletonsPath(PathManager.getSystemPath(), sdkHome);
      new File(skeletonsPath).mkdirs();
      final VirtualFile builtins_root = LocalFileSystem.getInstance().refreshAndFindFileByPath(skeletonsPath);
      assert builtins_root != null : "Cannot find skeletons path " + skeletonsPath + " in VFS";
-     sdkModificator.addRoot(builtins_root, BUILTIN_ROOT_TYPE);
+     sdkUpdater.addRoot(builtins_root, BUILTIN_ROOT_TYPE);
    }
  
-   protected static void addHardcodedPaths(SdkModificator sdkModificator) {
+   protected static void addHardcodedPaths(PySdkUpdater sdkUpdater) {
      // Add python-django installed as package in Linux
      // NOTE: fragile and arbitrary
      if (SystemInfo.isLinux) {
        final VirtualFile file = LocalFileSystem.getInstance().findFileByPath("/usr/lib/python-django");
        if (file != null) {
-         sdkModificator.addRoot(file, OrderRootType.CLASSES);
+         sdkUpdater.addRoot(file, OrderRootType.CLASSES);
        }
      }
    }
  
-   public static void addSdkRoot(SdkModificator sdkModificator, String path) {
+   public static void addSdkRoot(PySdkUpdater sdkUpdater, String path) {
      final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByPath(path);
      if (file != null) {
-       addSdkRoot(sdkModificator, file);
+       addSdkRoot(sdkUpdater, file);
      }
      else {
        LOG.info("Bogus sys.path entry " + path);
      }
    }
  
-   private static void addSdkRoot(@NotNull SdkModificator sdkModificator, @NotNull VirtualFile child) {
+   private static void addSdkRoot(@NotNull PySdkUpdater sdkUpdater, final @NotNull VirtualFile child) {
      // NOTE: Files marked as library sources are not considered part of project source. Since the directory of the project the
      // user is working on is included in PYTHONPATH with many configurations (e.g. virtualenv), we must not mark SDK paths as
      // library sources, only as classes.
-     sdkModificator.addRoot(getSdkRootVirtualFile(child), OrderRootType.CLASSES);
+     sdkUpdater.addRoot(getSdkRootVirtualFile(child), OrderRootType.CLASSES);
    }
  
    @NotNull
      return path;
    }
  
 +  /**
 +   * Returns skeletons location on the local machine. Independent of SDK credentials type (e.g. ssh, Vagrant, Docker or else).
 +   */
    public static String getSkeletonsPath(String basePath, String sdkHome) {
      String sep = File.separator;
      return getSkeletonsRootPath(basePath) + sep + FileUtil.toSystemIndependentName(sdkHome).hashCode() + sep;
  
    @NotNull
    public static List<String> getSysPathsFromScript(@NotNull String binaryPath) throws InvalidSdkException {
-     String scriptFile = PythonHelpersLocator.getHelperPath("syspath.py");
      // to handle the situation when PYTHONPATH contains ., we need to run the syspath script in the
      // directory of the script itself - otherwise the dir in which we run the script (e.g. /usr/bin) will be added to SDK path
-     final ProcessOutput run_result = PySdkUtil.getProcessOutput(new File(scriptFile).getParent(), new String[]{binaryPath, scriptFile},
+     GeneralCommandLine cmd = PythonHelper.SYSPATH.newCommandLine(binaryPath, Lists.<String>newArrayList());
+     final ProcessOutput runResult = PySdkUtil.getProcessOutput(cmd, new File(binaryPath).getParent(),
                                                                  getVirtualEnvExtraEnv(binaryPath), MINUTE);
-     if (!run_result.checkSuccess(LOG)) {
+     if (!runResult.checkSuccess(LOG)) {
        throw new InvalidSdkException(String.format("Failed to determine Python's sys.path value:\nSTDOUT: %s\nSTDERR: %s",
-                                                   run_result.getStdout(),
-                                                   run_result.getStderr()));
+                                                   runResult.getStdout(),
+                                                   runResult.getStderr()));
      }
-     return run_result.getStdoutLines();
+     return runResult.getStdoutLines();
    }
  
    /**
      return null;
    }
  
+   @Nullable
+   @Override
+   public String getVersionString(@NotNull Sdk sdk) {
+     if (isRemote(sdk)) {
+       final PyRemoteSdkAdditionalDataBase data = (PyRemoteSdkAdditionalDataBase)sdk.getSdkAdditionalData();
+       assert data != null;
+       String versionString = data.getVersionString();
+       if (StringUtil.isEmpty(versionString)) {
+         final PythonRemoteInterpreterManager remoteInterpreterManager = PythonRemoteInterpreterManager.getInstance();
+         if (remoteInterpreterManager != null) {
+           try {
+             versionString =
+               remoteInterpreterManager.getInterpreterVersion(null, data);
+           }
+           catch (Exception e) {
+             LOG.warn("Couldn't get interpreter version:" + e.getMessage(), e);
+             versionString = "undefined";
+           }
+         }
+         data.setVersionString(versionString);
+       }
+       return versionString;
+     }
+     else {
+       return getVersionString(sdk.getHomePath());
+     }
+   }
    @Nullable
    public String getVersionString(final String sdkHome) {
      final PythonSdkFlavor flavor = PythonSdkFlavor.getFlavor(sdkHome);
          @Override
          public void deployment(@NotNull WebDeploymentCredentialsHolder cred) {
          }
 +
 +        @Override
 +        public void docker(@NotNull DockerCredentialsHolder credentials) {
 +        }
        });
        return result.get();
      }