fix additional data creation for remote interpreters; (#PY-76055) Fixed

GitOrigin-RevId: 064cbbaeb5f4241b718c9e51df0396b49971a365
This commit is contained in:
Aleksandr Sorotskii
2024-09-23 13:30:27 +02:00
committed by intellij-monorepo-bot
parent 23c1ddb6b2
commit d67337d2c9
13 changed files with 68 additions and 49 deletions

View File

@@ -3,7 +3,6 @@ package com.jetbrains.python.sdk
import org.jetbrains.annotations.ApiStatus
import java.nio.file.InvalidPathException
import java.nio.file.Path
import java.nio.file.Paths
// TODO: Move to PythonSdkUtil when rewritten in Kotlin
@@ -11,10 +10,13 @@ import java.nio.file.Paths
* Converts [str] to [Path] of str is real nio path. Returns null otherwise
*/
@ApiStatus.Internal
fun tryResolvePath(str: String): Path? {
if (PythonSdkUtil.isCustomPythonSdkHomePath(str)) return null
fun tryResolvePath(str: String?): Path? {
if (str == null || PythonSdkUtil.isCustomPythonSdkHomePath(str)) {
return null
}
try {
return Paths.get(str)
return Path.of(str)
}
catch (_: InvalidPathException) {
}

View File

@@ -39,6 +39,7 @@ import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import static com.jetbrains.python.sdk.PythonSdkUtilKtKt.tryResolvePath;
import static com.jetbrains.python.sdk.flavors.PySdkFlavorUtilKt.getFileExecutionError;
@@ -283,14 +284,14 @@ public abstract class PythonSdkFlavor<D extends PyFlavorData> {
@Nullable
public static PythonSdkFlavor<?> getFlavor(@Nullable String sdkPath) {
if (sdkPath == null || PythonSdkUtil.isCustomPythonSdkHomePath(sdkPath)) return null;
return tryDetectFlavorByLocalPath(Path.of(sdkPath));
return tryDetectFlavorByLocalPath(sdkPath);
}
/**
* Detects {@link PythonSdkFlavor} for local python path
*/
@RequiresBackgroundThread(generateAssertion = false) //No warning yet as there are usages: to be fixed
public static @Nullable PythonSdkFlavor<?> tryDetectFlavorByLocalPath(@NotNull Path sdkPath) {
public static @Nullable PythonSdkFlavor<?> tryDetectFlavorByLocalPath(@NotNull String sdkPath) {
// Iterate over all flavors starting with platform-independent (like venv): see `getApplicableFlavors` doc.
// Order is important as venv must have priority over unix/windows
for (PythonSdkFlavor<?> flavor : getApplicableFlavors(true)) {
@@ -311,15 +312,14 @@ public abstract class PythonSdkFlavor<D extends PyFlavorData> {
return null;
}
Path path = Path.of(sdkPath);
for (PythonSdkFlavor<?> flavor : getPlatformIndependentFlavors()) {
if (flavor.isValidSdkPath(path)) {
if (flavor.isValidSdkPath(sdkPath)) {
return flavor;
}
}
for (PythonSdkFlavor<?> flavor : getPlatformFlavorsFromExtensions(true)) {
if (flavor.isValidSdkPath(path)) {
if (flavor.isValidSdkPath(sdkPath)) {
return flavor;
}
}
@@ -329,7 +329,12 @@ public abstract class PythonSdkFlavor<D extends PyFlavorData> {
/**
* It only validates path for local target, hence use {@link #sdkSeemsValid(Sdk, PyFlavorData, TargetEnvironmentConfiguration)} instead
*/
public boolean isValidSdkPath(@NotNull Path path) {
public boolean isValidSdkPath(@NotNull String pathStr) {
Path path = tryResolvePath(pathStr);
if (path == null) {
return false;
}
return Files.exists(path) && Files.isExecutable(path);
}

View File

@@ -53,8 +53,9 @@ public class PyRemoteSdkAdditionalData extends PythonSdkAdditionalData implement
private static @Nullable PythonSdkFlavor<?> computeFlavor(@Nullable String sdkPath) {
if (sdkPath != null) {
// FIXME: converge with sdk flavor & use os.isWindows
for (var flavor : getApplicableFlavors(sdkPath.contains("\\"))) {
if (flavor.isValidSdkPath(Path.of(sdkPath))) {
if (flavor.isValidSdkPath(sdkPath)) {
return flavor;
}
}

View File

@@ -475,9 +475,19 @@ private fun Sdk.containsModuleName(module: Module?): Boolean {
*/
fun Sdk.getOrCreateAdditionalData(): PythonSdkAdditionalData {
val existingData = sdkAdditionalData as? PythonSdkAdditionalData
if (existingData != null) return existingData
val homePath = Path.of(homePath ?: error("homePath is null for $this"))
val flavor = PythonSdkFlavor.tryDetectFlavorByLocalPath(homePath) ?: error("No flavor detected for $homePath sdk")
if (existingData != null) {
return existingData
}
if (homePath == null) {
error("homePath is null for $this")
}
val flavor = PythonSdkFlavor.tryDetectFlavorByLocalPath(homePath!!)
if (flavor == null) {
error("No flavor detected for $homePath sdk")
}
val newData = PythonSdkAdditionalData(if (flavor.supportsEmptyData()) flavor else null)
val modificator = sdkModificator
modificator.sdkAdditionalData = newData

View File

@@ -14,7 +14,6 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.File;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
@@ -44,8 +43,8 @@ public final class JythonSdkFlavor extends PythonSdkFlavor<PyFlavorData.Empty> {
}
@Override
public boolean isValidSdkPath(@NotNull Path path) {
return StringUtil.toLowerCase(FileUtilRt.getNameWithoutExtension(path.getFileName().toString())).startsWith("jython");
public boolean isValidSdkPath(@NotNull String pathStr) {
return StringUtil.toLowerCase(FileUtilRt.getNameWithoutExtension(pathStr)).startsWith("jython");
}
@Override

View File

@@ -4,13 +4,12 @@ package com.jetbrains.python.sdk.flavors
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.VirtualFile
import java.io.File
import java.nio.file.Path
class MayaSdkFlavor private constructor() : CPythonSdkFlavor<PyFlavorData.Empty>() {
override fun getFlavorDataClass(): Class<PyFlavorData.Empty> = PyFlavorData.Empty::class.java
override fun isValidSdkPath(path: Path): Boolean {
val name = FileUtil.getNameWithoutExtension(path.fileName.toString()).lowercase()
override fun isValidSdkPath(pathStr: String): Boolean {
val name = FileUtil.getNameWithoutExtension(pathStr).lowercase()
return name.startsWith("mayapy")
}

View File

@@ -11,8 +11,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.File;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
@@ -43,8 +41,8 @@ public final class PyPySdkFlavor extends PythonSdkFlavor<PyFlavorData.Empty> {
}
@Override
public boolean isValidSdkPath(@NotNull Path path) {
return StringUtil.toLowerCase(FileUtilRt.getNameWithoutExtension(path.getFileName().toString())).startsWith("pypy");
public boolean isValidSdkPath(@NotNull String pathStr) {
return StringUtil.toLowerCase(FileUtilRt.getNameWithoutExtension(pathStr)).startsWith("pypy");
}
@Override

View File

@@ -16,7 +16,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
@@ -76,12 +75,12 @@ public final class VirtualEnvSdkFlavor extends CPythonSdkFlavor<PyFlavorData.Emp
}
@Override
public boolean isValidSdkPath(@NotNull Path path) {
if (!super.isValidSdkPath(path)) {
public boolean isValidSdkPath(@NotNull String pathStr) {
if (!super.isValidSdkPath(pathStr)) {
return false;
}
return PythonSdkUtil.getVirtualEnvRoot(path.toString()) != null;
return PythonSdkUtil.getVirtualEnvRoot(pathStr) != null;
}
@Override

View File

@@ -25,6 +25,7 @@ import java.io.File;
import java.nio.file.Path;
import java.util.*;
import static com.jetbrains.python.sdk.PythonSdkUtilKtKt.tryResolvePath;
import static com.jetbrains.python.sdk.WinAppxToolsKt.getAppxFiles;
import static com.jetbrains.python.sdk.WinAppxToolsKt.getAppxProduct;
@@ -87,33 +88,33 @@ public class WinPythonSdkFlavor extends CPythonSdkFlavor<PyFlavorData.Empty> {
public boolean sdkSeemsValid(@NotNull Sdk sdk,
PyFlavorData.@NotNull Empty flavorData,
@Nullable TargetEnvironmentConfiguration targetConfig) {
if (super.sdkSeemsValid(sdk, flavorData, targetConfig)) {
return true;
}
if (targetConfig != null) {
if (super.sdkSeemsValid(sdk, flavorData, targetConfig) || targetConfig != null) {
// non-local, cant check for appx
return true;
}
var path = sdk.getHomePath();
return path != null && isLocalPathValidPython(Path.of(path));
var path = tryResolvePath(sdk.getHomePath());
return path != null && isLocalPathValidPython(path);
}
@Override
public boolean isValidSdkPath(final @NotNull Path path) {
if (super.isValidSdkPath(path)) {
public boolean isValidSdkPath(final @NotNull String pathStr) {
if (super.isValidSdkPath(pathStr)) {
return true;
}
return isLocalPathValidPython(path);
var path = tryResolvePath(pathStr);
return path != null && isLocalPathValidPython(path);
}
private boolean isLocalPathValidPython(@NotNull Path path) {
if (myAppxCache.getValue().contains(path.toString())) {
String pathStr = path.toString();
if (myAppxCache.getValue().contains(pathStr)) {
return true;
}
String product = getAppxProduct(path);
return product != null && product.contains(APPX_PRODUCT) && isValidSdkPath(path);
return product != null && product.contains(APPX_PRODUCT) && isValidSdkPath(pathStr);
}
@Override

View File

@@ -78,12 +78,12 @@ public final class CondaEnvSdkFlavor extends CPythonSdkFlavor<PyCondaFlavorData>
}
@Override
public boolean isValidSdkPath(@NotNull Path path) {
if (!super.isValidSdkPath(path)) {
public boolean isValidSdkPath(@NotNull String pathStr) {
if (!super.isValidSdkPath(pathStr)) {
return false;
}
return PythonSdkUtil.isConda(path.toString());
return PythonSdkUtil.isConda(pathStr);
}
public static @Nullable File getCondaEnvRoot(final @NotNull String binaryPath) {

View File

@@ -3,11 +3,10 @@ package com.jetbrains.python.sdk.pipenv
import com.jetbrains.python.sdk.flavors.CPythonSdkFlavor
import com.jetbrains.python.sdk.flavors.PyFlavorData
import java.nio.file.Path
object PyPipEnvSdkFlavor : CPythonSdkFlavor<PyFlavorData.Empty>() {
override fun getIcon() = PIPENV_ICON
override fun getFlavorDataClass(): Class<PyFlavorData.Empty> = PyFlavorData.Empty::class.java
override fun isValidSdkPath(path: Path) = false
override fun isValidSdkPath(pathStr: String) = false
}

View File

@@ -3,7 +3,6 @@ package com.jetbrains.python.sdk.poetry
import com.jetbrains.python.sdk.flavors.CPythonSdkFlavor
import com.jetbrains.python.sdk.flavors.PyFlavorData
import java.nio.file.Path
/**
@@ -14,5 +13,5 @@ object PyPoetrySdkFlavor : CPythonSdkFlavor<PyFlavorData.Empty>() {
override fun getIcon() = POETRY_ICON
override fun getFlavorDataClass(): Class<PyFlavorData.Empty> = PyFlavorData.Empty::class.java
override fun isValidSdkPath(path: Path) = false
override fun isValidSdkPath(pathStr: String) = false
}

View File

@@ -1,14 +1,13 @@
// 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.python.sdk
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.testFramework.ProjectRule
import com.jetbrains.python.remote.PyRemoteSdkAdditionalData
import org.jdom.Element
import org.junit.Assert
import org.junit.Rule
import org.junit.Test
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
class PySdkAdditionalDataTest {
@JvmField
@@ -27,4 +26,12 @@ class PySdkAdditionalDataTest {
val loadedSut = PythonSdkAdditionalData.loadFromElement(element)
Assert.assertEquals("UUID hasn't been loaded", sut.uuid, loadedSut.uuid)
}
@Test
fun remoteAdditionalDataTest() {
var data = PyRemoteSdkAdditionalData("!!!!!")
data = PyRemoteSdkAdditionalData("sftp://host:22/home/bin/python3")
data = PyRemoteSdkAdditionalData("docker://django:latest/python")
}
}