PY-57146, DS-4124, DS-3992: Fix local conda activation on Windows for newly created SDKs

What was wrong:
For SDK creation we execute python to get python path for homePath (see ``PyAddCondaTools``).

So, we execute python on conda before homePath set.

On each execution we read vars from ``activate.bat`` to workaround conda SSL in path problem and activate terminal (since cmd in terminal still uses old API).

``PySdkUtil`` can't read vars when homePath not set, hence caches empty vars for SDK and both terminal and conda workaround stop working until IDE restart.

We now stopped caching empty vars if homePath is empty.

GitOrigin-RevId: de3e37bbbc5281775e3fca79840089561ceed189
This commit is contained in:
Ilya.Kazakevich
2022-11-08 16:57:02 +01:00
committed by intellij-monorepo-bot
parent 7c82d7196b
commit ab43751a51
4 changed files with 42 additions and 1 deletions

View File

@@ -212,7 +212,12 @@ public final class PySdkUtil {
if (cached != null) return cached;
final String sdkHome = sdk.getHomePath();
if (sdkHome == null) return Collections.emptyMap();
if (sdkHome == null || sdkHome.trim().isEmpty()) {
// homePath is empty (not null) by default.
// If we cache values when path is empty, we would stuck with empty env and never reread it once path set
LOG.warn("homePath is null or empty, skipping env loading");
return Collections.emptyMap();
}
var additionalData = ObjectUtils.tryCast(sdk.getSdkAdditionalData(), PythonSdkAdditionalData.class);
if (additionalData == null) {

View File

@@ -41,6 +41,8 @@ suspend fun PyCondaCommand.createCondaSdkFromExistingEnv(condaIdentity: PyCondaE
val sdk = ProjectJdkImpl(SdkConfigurationUtil.createUniqueSdkName(condaIdentity.userReadableName, existingSdks),
PythonSdkType.getInstance())
sdk.sdkAdditionalData = additionalData
// homePath is not required by conda, but used by lots of tools all over the code and required by CondaPathFix
// Because homePath is not set yet, CondaPathFix does not work
sdk.homePath = sdk.getPythonBinaryPath(project).getOrThrow()
saveLocalPythonCondaPath(Path.of(fullCondaPathOnTarget))
return sdk

View File

@@ -1,26 +1,35 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.env.conda
import com.intellij.execution.target.TargetedCommandLineBuilder
import com.intellij.execution.target.local.LocalTargetEnvironmentRequest
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl
import com.intellij.openapi.util.SystemInfoRt
import com.intellij.testFramework.ProjectRule
import com.jetbrains.getPythonVersion
import com.jetbrains.python.sdk.PythonSdkType
import com.jetbrains.python.sdk.add.target.conda.createCondaSdkAlongWithNewEnv
import com.jetbrains.python.sdk.add.target.conda.createCondaSdkFromExistingEnv
import com.jetbrains.python.sdk.configureBuilderToRunPythonOnTarget
import com.jetbrains.python.sdk.flavors.conda.*
import com.jetbrains.python.sdk.flavors.conda.CondaPathFix.Companion.shouldBeFixed
import com.jetbrains.python.sdk.getOrCreateAdditionalData
import com.jetbrains.python.sdk.getPythonBinaryPath
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.hamcrest.MatcherAssert
import org.hamcrest.Matchers
import org.jdom.Element
import org.junit.Assert
import org.junit.Assume
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
import java.util.*
/**
* Ensures conda SDK could be created
@@ -38,6 +47,30 @@ internal class PyCondaSdkTest {
internal val chain = RuleChain.outerRule(projectRule).around(condaRule).around(yamlRule)
/**
* When we create fresh local SDK on Windows, it must be patched with env vars, see [CondaPathFix]
*/
@Test
fun testLocalActivationFix(): Unit = runTest {
Assume.assumeTrue("Windows only", SystemInfoRt.isWindows)
val env = PyCondaEnv.getEnvs(
condaRule.condaCommand).getOrThrow().first { (it.envIdentity as? PyCondaEnvIdentity.UnnamedEnv)?.isBase == true }
val condaSdk = condaRule.condaCommand.createCondaSdkFromExistingEnv(env.envIdentity, emptyList(), projectRule.project)
val request = LocalTargetEnvironmentRequest()
val builder = TargetedCommandLineBuilder(request)
Assert.assertTrue("No conda path fix suggested for Windows?", builder.shouldBeFixed)
condaSdk.configureBuilderToRunPythonOnTarget(builder)
builder.apply {
addParameter("-c")
addParameter("import os; print(' '.join(list(os.environ.keys())))")
}
val envVars = builder.build().environmentVariables.map { it.key.uppercase(Locale.getDefault()) to it.value }.toMap()
MatcherAssert.assertThat("Conda not activated?", envVars.keys, Matchers.hasItem("PATH"))
MatcherAssert.assertThat("Conda not activated?", envVars.keys, Matchers.hasItem("CONDA_PREFIX"))
val paths = envVars["PATH"]!!.split(File.pathSeparator).map { Path.of(it) }
MatcherAssert.assertThat("No conda python in PATH", paths, Matchers.hasItem(Path.of(condaSdk.homePath!!).parent))
}
@Test
fun testConvertToConda() = runTest {
System.setProperty("NO_FS_ROOTS_ACCESS_CHECK", "true")

View File

@@ -49,5 +49,6 @@
<orderEntry type="library" scope="TEST" name="kotlinx-coroutines-jdk8" level="project" />
<orderEntry type="library" scope="TEST" name="jetbrains.kotlinx.coroutines.test" level="project" />
<orderEntry type="module" module-name="intellij.python.community.core.impl" scope="TEST" />
<orderEntry type="module" module-name="intellij.python.sdk" scope="TEST" />
</component>
</module>