diff --git a/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/configuration/PyPipfileSdkConfiguration.kt b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/configuration/PyPipfileSdkConfiguration.kt index 57c94d6a290f..b1e8cecebabb 100644 --- a/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/configuration/PyPipfileSdkConfiguration.kt +++ b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/configuration/PyPipfileSdkConfiguration.kt @@ -93,14 +93,14 @@ class PyPipfileSdkConfiguration : PyProjectSdkConfigurationExtension { return null } - val path = PythonSdkUtil.getPythonExecutable(pipEnv).also { + val path = VirtualEnvReader.Instance.findPythonInPythonRoot(Path.of(pipEnv)).also { if (it == null) { PySdkConfigurationCollector.logPipEnv(module.project, PipEnvResult.NO_EXECUTABLE) LOGGER.warn("Python executable is not found: $pipEnv") } } ?: return null - val file = LocalFileSystem.getInstance().refreshAndFindFileByPath(path).also { + val file = LocalFileSystem.getInstance().refreshAndFindFileByPath(path.toString()).also { if (it == null) { PySdkConfigurationCollector.logPipEnv(module.project, PipEnvResult.NO_EXECUTABLE_FILE) LOGGER.warn("Python executable file is not found: $path") diff --git a/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/configuration/PyPoetrySdkConfiguration.kt b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/configuration/PyPoetrySdkConfiguration.kt index fb5bb8976a3e..160e40aab263 100644 --- a/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/configuration/PyPoetrySdkConfiguration.kt +++ b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/configuration/PyPoetrySdkConfiguration.kt @@ -91,13 +91,13 @@ class PyPoetrySdkConfiguration : PyProjectSdkConfigurationExtension { return null } - val path = PythonSdkUtil.getPythonExecutable(poetry).also { + val path = VirtualEnvReader.Instance.findPythonInPythonRoot(Path.of(poetry)).also { if (it == null) { LOGGER.warn("Python executable is not found: $poetry") } } ?: return null - val file = LocalFileSystem.getInstance().refreshAndFindFileByPath(path).also { + val file = LocalFileSystem.getInstance().refreshAndFindFileByPath(path.toString()).also { if (it == null) { LOGGER.warn("Python executable file is not found: $path") } diff --git a/python/python-psi-impl/src/com/jetbrains/python/sdk/PythonSdkUtil.java b/python/python-psi-impl/src/com/jetbrains/python/sdk/PythonSdkUtil.java index 0b7da655b101..c1d374a2f815 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/sdk/PythonSdkUtil.java +++ b/python/python-psi-impl/src/com/jetbrains/python/sdk/PythonSdkUtil.java @@ -15,12 +15,15 @@ import com.intellij.openapi.util.NlsSafe; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileSystemUtil; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.*; +import com.intellij.openapi.vfs.StandardFileSystems; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.util.ObjectUtils; import com.intellij.util.SystemProperties; +import com.intellij.util.concurrency.annotations.RequiresBackgroundThread; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyNames; import com.jetbrains.python.PythonRuntimeService; @@ -66,9 +69,6 @@ public final class PythonSdkUtil { */ public static final OrderRootType BUILTIN_ROOT_TYPE = OrderRootType.CLASSES; static final String[] WINDOWS_EXECUTABLE_SUFFIXES = {"cmd", "exe", "bat", "com"}; - private static final String[] DIRS_WITH_BINARY = {"", "bin", "Scripts", "net45"}; - private static final String[] UNIX_BINARY_NAMES = {"jython", "pypy", "python", "python3"}; - private static final String[] WIN_BINARY_NAMES = {"jython.bat", "ipy.exe", "pypy.exe", "python.exe", "python3.exe"}; private static final Predicate REMOTE_SDK_PREDICATE = PythonSdkUtil::isRemote; private static final Key CACHED_SKELETON_HEADER = Key.create("CACHED_SKELETON_HEADER"); @@ -316,66 +316,27 @@ public final class PythonSdkUtil { return getAllSdks().stream().filter(REMOTE_SDK_PREDICATE.negate()).collect(Collectors.toList()); } + // It is only here for external plugins @Nullable + @RequiresBackgroundThread(generateAssertion = false) public static String getPythonExecutable(@NotNull String rootPath) { - final File rootFile = new File(rootPath); - if (rootFile.isFile()) { - return rootFile.getAbsolutePath(); - } - for (String dir : DIRS_WITH_BINARY) { - final File subDir; - if (StringUtil.isEmpty(dir)) { - subDir = rootFile; - } - else { - subDir = new File(rootFile, dir); - } - if (!subDir.isDirectory()) { - continue; - } - for (String binaryName : getBinaryNames()) { - final File executable = new File(subDir, binaryName); - if (executable.isFile()) { - return executable.getAbsolutePath(); - } - } - } - return null; + var python = VirtualEnvReader.getInstance().findPythonInPythonRoot(Path.of(rootPath)); + return (python != null) ? python.toString() : null; } + /** + * @deprecated use {@link #getExecutablePath(Path, String)} + */ + @Deprecated @Nullable + @RequiresBackgroundThread(generateAssertion = false) public static String getExecutablePath(@NotNull final String homeDirectory, @NotNull String name) { - File binPath = new File(homeDirectory); - File binDir = binPath.getParentFile(); - if (binDir == null) return null; - VirtualFileSystem localVfs = StandardFileSystems.local(); - File runner = new File(binDir, name); - if (runner.exists()) return localVfs.extractPresentableUrl(runner.getPath()); - runner = new File(new File(binDir, "Scripts"), name); - if (runner.exists()) return localVfs.extractPresentableUrl(runner.getPath()); - runner = new File(new File(binDir.getParentFile(), "Scripts"), name); - if (runner.exists()) return localVfs.extractPresentableUrl(runner.getPath()); - runner = new File(new File(binDir.getParentFile(), "local"), name); - if (runner.exists()) return localVfs.extractPresentableUrl(runner.getPath()); - runner = new File(new File(new File(binDir.getParentFile(), "local"), "bin"), name); - if (runner.exists()) return localVfs.extractPresentableUrl(runner.getPath()); - - // if interpreter is a symlink - if (FileSystemUtil.isSymLink(homeDirectory)) { - String resolvedPath = FileSystemUtil.resolveSymLink(homeDirectory); - if (resolvedPath != null) { - return getExecutablePath(resolvedPath, name); - } - } - // Search in standard unix path - runner = new File(new File("/usr", "bin"), name); - if (runner.exists()) return localVfs.extractPresentableUrl(runner.getPath()); - runner = new File(new File(new File("/usr", "local"), "bin"), name); - if (runner.exists()) return localVfs.extractPresentableUrl(runner.getPath()); - return null; + Path path = getExecutablePath(Path.of(homeDirectory), name); + return (path != null) ? path.toString() : null; } @Nullable + @RequiresBackgroundThread(generateAssertion = false) public static Path getExecutablePath(@NotNull Path homeDirectory, @NotNull String name) { Path binDir = homeDirectory.getParent(); if (binDir == null) return null; @@ -459,14 +420,6 @@ public final class PythonSdkUtil { return null; } - private static String[] getBinaryNames() { - if (SystemInfo.isUnix) { - return UNIX_BINARY_NAMES; - } - else { - return WIN_BINARY_NAMES; - } - } @Nullable public static Sdk findSdkByKey(@NotNull String key) { diff --git a/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvReader.kt b/python/python-psi-impl/src/com/jetbrains/python/sdk/VirtualEnvReader.kt similarity index 94% rename from python/src/com/jetbrains/python/sdk/flavors/VirtualEnvReader.kt rename to python/python-psi-impl/src/com/jetbrains/python/sdk/VirtualEnvReader.kt index a7a0e0b48f78..fe70907aefc7 100644 --- a/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvReader.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/sdk/VirtualEnvReader.kt @@ -1,12 +1,10 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.jetbrains.python.sdk.flavors +package com.jetbrains.python.sdk -import com.intellij.openapi.util.IntellijInternalApi import com.intellij.openapi.util.SystemInfoRt import com.intellij.openapi.util.io.toCanonicalPath import com.intellij.util.SystemProperties import com.intellij.util.concurrency.annotations.RequiresBackgroundThread -import com.jetbrains.python.sdk.tryResolvePath import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.NonNls import java.io.IOException @@ -19,7 +17,6 @@ import kotlin.io.path.* typealias PythonBinary = Path typealias Directory = Path -@IntellijInternalApi @ApiStatus.Internal class VirtualEnvReader( private val envs: Map<@NonNls String, @NonNls String> = System.getenv(), @@ -105,7 +102,7 @@ class VirtualEnvReader( * [dir] is root directory of python installation or virtualenv */ @RequiresBackgroundThread - private fun findPythonInPythonRoot(dir: Directory): PythonBinary? { + fun findPythonInPythonRoot(dir: Directory): PythonBinary? { if (!dir.isDirectory()) { return null } diff --git a/python/python-restructuredtext/src/com/intellij/restructuredtext/python/SphinxBaseCommand.java b/python/python-restructuredtext/src/com/intellij/restructuredtext/python/SphinxBaseCommand.java index c5ede1f2e065..eb5158f9bdc5 100644 --- a/python/python-restructuredtext/src/com/intellij/restructuredtext/python/SphinxBaseCommand.java +++ b/python/python-restructuredtext/src/com/intellij/restructuredtext/python/SphinxBaseCommand.java @@ -33,6 +33,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -67,8 +68,8 @@ public class SphinxBaseCommand { setTitle(RestBundle.message("sphinx.set.working.directory.dialog.title")); init(); - VirtualFile baseDir = project.getBaseDir(); - String path = baseDir != null? baseDir.getPath() : ""; + VirtualFile baseDir = project.getBaseDir(); + String path = baseDir != null ? baseDir.getPath() : ""; myInputFile.setText(path); myInputFile.setEditable(false); myInputFile.addBrowseFolderListener(RestBundle.message("sphinx.choose.working.directory.browse.folder.title"), null, project, @@ -134,7 +135,8 @@ public class SphinxBaseCommand { GeneralCommandLine cmd = new GeneralCommandLine(); if (sdkHomePath != null) { final String runnerName = "sphinx-quickstart" + (SystemInfo.isWindows ? ".exe" : ""); - String executablePath = PythonSdkUtil.getExecutablePath(sdkHomePath, runnerName); + var executablePathNio = PythonSdkUtil.getExecutablePath(Path.of(sdkHomePath), runnerName); + String executablePath = executablePathNio != null ? executablePathNio.toString() : null; if (executablePath != null) { cmd.setExePath(executablePath); } @@ -143,7 +145,7 @@ public class SphinxBaseCommand { } } - cmd.setWorkDirectory(service.getWorkdir().isEmpty()? module.getProject().getBasePath(): service.getWorkdir()); + cmd.setWorkDirectory(service.getWorkdir().isEmpty() ? module.getProject().getBasePath() : service.getWorkdir()); PythonCommandLineState.createStandardGroups(cmd); ParamsGroup scriptParams = cmd.getParametersList().getParamsGroup(PythonCommandLineState.GROUP_SCRIPT); assert scriptParams != null; @@ -172,5 +174,4 @@ public class SphinxBaseCommand { return cmd; } - } diff --git a/python/src/com/jetbrains/python/packaging/PyCondaPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyCondaPackageManagerImpl.java index 6c2db1c9a793..3999eba98ddf 100644 --- a/python/src/com/jetbrains/python/packaging/PyCondaPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyCondaPackageManagerImpl.java @@ -9,11 +9,13 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.jetbrains.python.PySdkBundle; import com.jetbrains.python.sdk.PythonSdkUtil; +import com.jetbrains.python.sdk.VirtualEnvReader; import com.jetbrains.python.sdk.flavors.PyCondaRunKt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; +import java.nio.file.Path; import java.util.*; public class PyCondaPackageManagerImpl extends PyPackageManagerImpl { @@ -148,9 +150,9 @@ public class PyCondaPackageManagerImpl extends PyPackageManagerImpl { final ArrayList parameters = Lists.newArrayList("create", "-p", destinationDir, "-y", "python=" + version); PyCondaRunKt.runConda(condaExecutable, parameters); - final String binary = PythonSdkUtil.getPythonExecutable(destinationDir); + final Path binary = VirtualEnvReader.getInstance().findPythonInPythonRoot(Path.of(destinationDir)); final String binaryFallback = destinationDir + File.separator + "bin" + File.separator + "python"; - return (binary != null) ? binary : binaryFallback; + return (binary != null) ? binary.toString() : binaryFallback; } @Override diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java index 7652746f1c50..488bd10d5941 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java @@ -26,6 +26,7 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; @@ -193,10 +194,10 @@ public class PyPackageManagerImpl extends PyPackageManagerImplBase { showSdkExecutionException(sdk, e, PySdkBundle.message("python.creating.venv.failed.title")); } - final String binary = PythonSdkUtil.getPythonExecutable(destinationDir); + final Path binary = VirtualEnvReader.getInstance().findPythonInPythonRoot(Paths.get(destinationDir)); final String binaryFallback = destinationDir + mySeparator + "bin" + mySeparator + "python"; - return (binary != null) ? binary : binaryFallback; + return (binary != null) ? binary.toString() : binaryFallback; } /** diff --git a/python/src/com/jetbrains/python/packaging/PyTargetEnvironmentPackageManager.java b/python/src/com/jetbrains/python/packaging/PyTargetEnvironmentPackageManager.java index c12853e4cfa3..058eea96cdba 100644 --- a/python/src/com/jetbrains/python/packaging/PyTargetEnvironmentPackageManager.java +++ b/python/src/com/jetbrains/python/packaging/PyTargetEnvironmentPackageManager.java @@ -37,11 +37,13 @@ import com.jetbrains.python.run.target.HelpersAwareTargetEnvironmentRequest; import com.jetbrains.python.sdk.PyLazySdk; import com.jetbrains.python.sdk.PySdkExtKt; import com.jetbrains.python.sdk.PythonSdkUtil; +import com.jetbrains.python.sdk.VirtualEnvReader; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; @@ -237,11 +239,11 @@ public class PyTargetEnvironmentPackageManager extends PyPackageManagerImplBase // TODO [targets] Pass `parentDir = null` getPythonProcessResult(pythonExecution, false, true, targetEnvironmentRequest); - final String binary = PythonSdkUtil.getPythonExecutable(destinationDir); + final Path binary = VirtualEnvReader.getInstance().findPythonInPythonRoot(Path.of(destinationDir)); final char separator = targetEnvironmentRequest.getTargetPlatform().getPlatform().fileSeparator; final String binaryFallback = destinationDir + separator + "bin" + separator + "python"; - return (binary != null) ? binary : binaryFallback; + return (binary != null) ? binary.toString() : binaryFallback; } /** diff --git a/python/src/com/jetbrains/python/sdk/add/target/PyAddVirtualEnvPanel.kt b/python/src/com/jetbrains/python/sdk/add/target/PyAddVirtualEnvPanel.kt index 631014ccdc3a..2024934b36c3 100644 --- a/python/src/com/jetbrains/python/sdk/add/target/PyAddVirtualEnvPanel.kt +++ b/python/src/com/jetbrains/python/sdk/add/target/PyAddVirtualEnvPanel.kt @@ -5,7 +5,6 @@ import com.intellij.execution.target.TargetEnvironmentConfiguration import com.intellij.execution.target.joinTargetPaths import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.module.Module -import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.project.Project import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil @@ -33,7 +32,7 @@ import com.jetbrains.python.sdk.configuration.createSdkForTarget import com.jetbrains.python.sdk.configuration.createVirtualEnvSynchronously import com.jetbrains.python.sdk.flavors.PyFlavorAndData import com.jetbrains.python.sdk.flavors.PyFlavorData -import com.jetbrains.python.sdk.flavors.VirtualEnvReader.Companion.DEFAULT_VIRTUALENVS_DIR +import com.jetbrains.python.sdk.VirtualEnvReader.Companion.DEFAULT_VIRTUALENVS_DIR import com.jetbrains.python.target.PyTargetAwareAdditionalData import com.jetbrains.python.target.PythonLanguageRuntimeConfiguration import java.awt.BorderLayout diff --git a/python/src/com/jetbrains/python/sdk/add/v2/PythonAddLocalInterpreterPresenter.kt b/python/src/com/jetbrains/python/sdk/add/v2/PythonAddLocalInterpreterPresenter.kt index c1070577c78c..225245936e4e 100644 --- a/python/src/com/jetbrains/python/sdk/add/v2/PythonAddLocalInterpreterPresenter.kt +++ b/python/src/com/jetbrains/python/sdk/add/v2/PythonAddLocalInterpreterPresenter.kt @@ -5,7 +5,7 @@ import com.intellij.openapi.application.EDT import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.util.io.toNioPathOrNull import com.jetbrains.python.sdk.ModuleOrProject -import com.jetbrains.python.sdk.flavors.VirtualEnvReader +import com.jetbrains.python.sdk.VirtualEnvReader import com.jetbrains.python.sdk.rootManager import com.jetbrains.python.sdk.service.PySdkService.Companion.pySdkService import kotlinx.coroutines.Dispatchers diff --git a/python/src/com/jetbrains/python/sdk/add/v2/presentersExt.kt b/python/src/com/jetbrains/python/sdk/add/v2/presentersExt.kt index 3d9c08aa211c..5fadea4a10fd 100644 --- a/python/src/com/jetbrains/python/sdk/add/v2/presentersExt.kt +++ b/python/src/com/jetbrains/python/sdk/add/v2/presentersExt.kt @@ -14,6 +14,7 @@ import com.intellij.platform.ide.progress.runWithModalProgressBlocking import com.jetbrains.python.PyBundle import com.jetbrains.python.sdk.PythonSdkType import com.jetbrains.python.sdk.PythonSdkUtil +import com.jetbrains.python.sdk.VirtualEnvReader import com.jetbrains.python.sdk.add.target.conda.createCondaSdkFromExistingEnv import com.jetbrains.python.sdk.excludeInnerVirtualEnv import com.jetbrains.python.sdk.flavors.conda.PyCondaCommand @@ -41,7 +42,7 @@ internal fun PythonMutableTargetAddInterpreterModel.setupVirtualenv(venvPath: Pa inheritSitePackages = state.inheritSitePackages.get()) if (targetEnvironmentConfiguration != null) error("Remote targets aren't supported") - val venvPython = PythonSdkUtil.getPythonExecutable(venvPathOnTarget) + val venvPython = VirtualEnvReader.Instance.findPythonInPythonRoot(Path.of(venvPathOnTarget))?.toString() val homeFile = try { StandardFileSystems.local().refreshAndFindFileByPath(venvPython!!)!! diff --git a/python/src/com/jetbrains/python/sdk/flavors/UnixPythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/flavors/UnixPythonSdkFlavor.java index c9af8dfc7422..71d4ae682794 100644 --- a/python/src/com/jetbrains/python/sdk/flavors/UnixPythonSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/flavors/UnixPythonSdkFlavor.java @@ -5,6 +5,7 @@ import com.google.common.collect.Sets; import com.intellij.openapi.module.Module; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.UserDataHolder; +import com.jetbrains.python.sdk.VirtualEnvReader; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java b/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java index 472405b0dea0..dbb70c32d808 100644 --- a/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java @@ -11,6 +11,7 @@ import com.jetbrains.python.icons.PythonIcons; import com.jetbrains.python.sdk.BasePySdkExtKt; import com.jetbrains.python.sdk.PySdkExtKt; import com.jetbrains.python.sdk.PythonSdkUtil; +import com.jetbrains.python.sdk.VirtualEnvReader; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/python/src/com/jetbrains/python/sdk/pipenv/pipenv.kt b/python/src/com/jetbrains/python/sdk/pipenv/pipenv.kt index 5b705a6657a9..4b4d1e6fec00 100644 --- a/python/src/com/jetbrains/python/sdk/pipenv/pipenv.kt +++ b/python/src/com/jetbrains/python/sdk/pipenv/pipenv.kt @@ -122,7 +122,7 @@ fun setupPipEnvSdkUnderProgress(project: Project?, override fun compute(indicator: ProgressIndicator): String { indicator.isIndeterminate = true val pipEnv = setupPipEnv(FileUtil.toSystemDependentName(projectPath), python, installPackages) - return PythonSdkUtil.getPythonExecutable(pipEnv) ?: FileUtil.join(pipEnv, "bin", "python") + return VirtualEnvReader.Instance.findPythonInPythonRoot(Path.of(pipEnv))?.toString() ?: FileUtil.join(pipEnv, "bin", "python") } } return createSdkByGenerateTask(task, existingSdks, null, projectPath, suggestedSdkName(projectPath))?.apply { diff --git a/python/src/com/jetbrains/python/sdk/poetry/PoetryCommandExecutor.kt b/python/src/com/jetbrains/python/sdk/poetry/PoetryCommandExecutor.kt index a88dd927c005..8ca357fadb28 100644 --- a/python/src/com/jetbrains/python/sdk/poetry/PoetryCommandExecutor.kt +++ b/python/src/com/jetbrains/python/sdk/poetry/PoetryCommandExecutor.kt @@ -210,4 +210,4 @@ inline fun syncRunPoetry( } } -fun getPythonExecutable(homePath: String): String = PythonSdkUtil.getPythonExecutable(homePath) ?: FileUtil.join(homePath, "bin", "python") \ No newline at end of file +fun getPythonExecutable(homePath: String): String = VirtualEnvReader.Instance.findPythonInPythonRoot(Path.of(homePath))?.toString() ?: FileUtil.join(homePath, "bin", "python") \ No newline at end of file diff --git a/python/src/com/jetbrains/python/statistics/PyStatisticTools.kt b/python/src/com/jetbrains/python/statistics/PyStatisticTools.kt index 53a78e66ec63..c26e464618b0 100644 --- a/python/src/com/jetbrains/python/statistics/PyStatisticTools.kt +++ b/python/src/com/jetbrains/python/statistics/PyStatisticTools.kt @@ -20,7 +20,7 @@ import com.jetbrains.python.sdk.PythonSdkAdditionalData import com.jetbrains.python.sdk.PythonSdkType import com.jetbrains.python.sdk.PythonSdkUtil import com.jetbrains.python.sdk.flavors.PythonSdkFlavor -import com.jetbrains.python.sdk.flavors.VirtualEnvReader +import com.jetbrains.python.sdk.VirtualEnvReader import com.jetbrains.python.sdk.flavors.conda.CondaEnvSdkFlavor import com.jetbrains.python.sdk.pipenv.isPipEnv import com.jetbrains.python.sdk.poetry.isPoetry diff --git a/python/testFramework/src/com/jetbrains/python/tools/Utils.kt b/python/testFramework/src/com/jetbrains/python/tools/Utils.kt index ec84c2724c19..07fed8adaf05 100644 --- a/python/testFramework/src/com/jetbrains/python/tools/Utils.kt +++ b/python/testFramework/src/com/jetbrains/python/tools/Utils.kt @@ -17,10 +17,12 @@ import com.jetbrains.python.PyNames import com.jetbrains.python.packaging.PyPackagingSettings import com.jetbrains.python.sdk.PythonSdkUpdater import com.jetbrains.python.sdk.PythonSdkUtil +import com.jetbrains.python.sdk.VirtualEnvReader import com.jetbrains.python.tools.sdkTools.PySdkTools import com.jetbrains.python.tools.sdkTools.SdkCreationType import java.io.File import java.nio.file.Paths +import kotlin.io.path.Path internal val TestPath = System.getenv("PYCHARM_PERF_ENVS") @@ -30,7 +32,7 @@ fun createSdkForPerformance(module: Module, sdkHome: String = File(TestPath, "envs/py36_64").absolutePath): Sdk { ApplicationManagerEx.setInStressTest(true) // To disable slow debugging - val executable = File(PythonSdkUtil.getPythonExecutable(sdkHome) ?: throw AssertionError("No python on $sdkHome")) + val executable = VirtualEnvReader.Instance.findPythonInPythonRoot(Path(sdkHome))?.toFile() ?: throw AssertionError("No python on $sdkHome") println("Creating Python SDK $sdkHome") return PySdkTools.createTempSdk(VfsUtil.findFileByIoFile(executable, true)!!, sdkCreationType, module, PyPackagingSettings.getInstance(module.project)) diff --git a/python/testSrc/com/jetbrains/env/PyEnvTaskRunner.java b/python/testSrc/com/jetbrains/env/PyEnvTaskRunner.java index 6a713051ee92..4d3a1a01e215 100644 --- a/python/testSrc/com/jetbrains/env/PyEnvTaskRunner.java +++ b/python/testSrc/com/jetbrains/env/PyEnvTaskRunner.java @@ -14,6 +14,7 @@ import com.jetbrains.LoggingRule; import com.jetbrains.python.psi.LanguageLevel; import com.jetbrains.python.sdk.PySdkUtil; import com.jetbrains.python.sdk.PythonSdkUtil; +import com.jetbrains.python.sdk.VirtualEnvReader; import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; import com.jetbrains.python.tools.sdkTools.PySdkTools; import com.jetbrains.python.tools.sdkTools.SdkCreationType; @@ -21,6 +22,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; +import java.nio.file.Path; import java.util.*; public class PyEnvTaskRunner { @@ -74,10 +76,10 @@ public class PyEnvTaskRunner { else { testTask.useNormalTimeout(); } - final String executable = PythonSdkUtil.getPythonExecutable(root); + final Path executable = VirtualEnvReader.getInstance().findPythonInPythonRoot(Path.of(root)); assert executable != null : "No executable in " + root; - final Sdk sdk = getSdk(executable, testTask); + final Sdk sdk = getSdk(executable.toString(), testTask); if (skipOnFlavors != null) { final PythonSdkFlavor flavor = PythonSdkFlavor.getFlavor(sdk); if (ContainerUtil.exists(skipOnFlavors, o -> o.isInstance(flavor))) { @@ -101,7 +103,7 @@ public class PyEnvTaskRunner { } - testTask.runTestOn(executable, sdk); + testTask.runTestOn(executable.toString(), sdk); passedRoots.add(root); } diff --git a/python/testSrc/com/jetbrains/env/python/api/PythonType.kt b/python/testSrc/com/jetbrains/env/python/api/PythonType.kt index 7dd61d0a39e1..029fb9ece22b 100644 --- a/python/testSrc/com/jetbrains/env/python/api/PythonType.kt +++ b/python/testSrc/com/jetbrains/env/python/api/PythonType.kt @@ -9,6 +9,7 @@ import com.jetbrains.env.PyEnvTestCase import com.jetbrains.env.PyEnvTestSettings import com.jetbrains.python.packaging.findCondaExecutableRelativeToEnv import com.jetbrains.python.sdk.PythonSdkUtil +import com.jetbrains.python.sdk.VirtualEnvReader import com.jetbrains.python.sdk.add.target.conda.TargetEnvironmentRequestCommandExecutor import com.jetbrains.python.sdk.flavors.conda.PyCondaEnv import com.jetbrains.python.sdk.flavors.conda.PyCondaEnvIdentity @@ -37,9 +38,9 @@ sealed class PythonType(private val tag: @NonNls String) { .map { it.toPath() } .firstOrNull { typeMatchesEnv(it, *additionalTags) } ?.let { envDir -> - Result.success(pythonPathToEnvironment(Path.of( - PythonSdkUtil.getPythonExecutable(envDir.toString()) - ?: error("Can't find python binary in $envDir")), envDir)) // This is a misconfiguration, hence an error + Result.success(pythonPathToEnvironment( + VirtualEnvReader.Instance.findPythonInPythonRoot(envDir) + ?: error("Can't find python binary in $envDir"), envDir)) // This is a misconfiguration, hence an error } ?: Result.failure(Throwable("No python found. See ${PyEnvTestSettings::class} class for more info")) diff --git a/python/testSrc/com/jetbrains/python/sdk/flavors/VirtualEnvReaderTest.kt b/python/testSrc/com/jetbrains/python/sdk/VirtualEnvReaderTest.kt similarity index 71% rename from python/testSrc/com/jetbrains/python/sdk/flavors/VirtualEnvReaderTest.kt rename to python/testSrc/com/jetbrains/python/sdk/VirtualEnvReaderTest.kt index cde0a4b3238d..af36ed24ffb2 100644 --- a/python/testSrc/com/jetbrains/python/sdk/flavors/VirtualEnvReaderTest.kt +++ b/python/testSrc/com/jetbrains/python/sdk/VirtualEnvReaderTest.kt @@ -1,9 +1,10 @@ -package com.jetbrains.python.sdk.flavors +// Copyright 2000-2024 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.grazie.grammar.assertIsEmpty import com.intellij.openapi.util.io.FileUtilRt import com.intellij.testFramework.utils.io.deleteRecursively -import org.junit.Assert.* +import org.junit.Assert import org.junit.Test import java.nio.file.Files import java.nio.file.Path @@ -95,24 +96,24 @@ class VirtualEnvReaderTest { // just version bootstrap.setupPyenv(listOf("3.1.1"), "python") var interpreters = bootstrap.virtualEnvReader.findPyenvInterpreters() - assertEquals(1, interpreters.size) + Assert.assertEquals(1, interpreters.size) assert(interpreters[0].absolutePathString().startsWith(bootstrap.pyenv.absolutePathString())) assert(interpreters[0].absolutePathString().endsWith("python")) // another version w/o match bootstrap.setupPyenv(listOf("3.2.1"), "xxx") interpreters = bootstrap.virtualEnvReader.findPyenvInterpreters() - assertEquals(1, interpreters.size) + Assert.assertEquals(1, interpreters.size) // both in names bootstrap.setupPyenv(listOf("3.2.2"), "pypy") interpreters = bootstrap.virtualEnvReader.findPyenvInterpreters() - assertEquals(2, interpreters.size) + Assert.assertEquals(2, interpreters.size) assert(interpreters[0] != interpreters[1]) bootstrap.removeVersion(bootstrap.pyenv, "3.2.2") interpreters = bootstrap.virtualEnvReader.findPyenvInterpreters() - assertEquals(1, interpreters.size) + Assert.assertEquals(1, interpreters.size) assert(interpreters[0].absolutePathString().endsWith("python")) } @@ -120,24 +121,24 @@ class VirtualEnvReaderTest { fun testIsPyenvSdk() { val bootstrap = Bootstrap() - assertFalse(bootstrap.virtualEnvReader.isPyenvSdk(null as String?)) - assertFalse(bootstrap.virtualEnvReader.isPyenvSdk("")) - assertFalse(bootstrap.virtualEnvReader.isPyenvSdk("aa\u0000bb")) - assertFalse(bootstrap.virtualEnvReader.isPyenvSdk("a/b/c/d")) - assertFalse(bootstrap.virtualEnvReader.isPyenvSdk(bootstrap.cwd)) - assertFalse(bootstrap.virtualEnvReader.isPyenvSdk(bootstrap.cwd.resolve("smthg"))) + Assert.assertFalse(bootstrap.virtualEnvReader.isPyenvSdk(null as String?)) + Assert.assertFalse(bootstrap.virtualEnvReader.isPyenvSdk("")) + Assert.assertFalse(bootstrap.virtualEnvReader.isPyenvSdk("aa\u0000bb")) + Assert.assertFalse(bootstrap.virtualEnvReader.isPyenvSdk("a/b/c/d")) + Assert.assertFalse(bootstrap.virtualEnvReader.isPyenvSdk(bootstrap.cwd)) + Assert.assertFalse(bootstrap.virtualEnvReader.isPyenvSdk(bootstrap.cwd.resolve("smthg"))) bootstrap.setupPyenv(listOf("3.2.1"), "xxxx") - assertFalse(bootstrap.virtualEnvReader.isPyenvSdk(null as String?)) - assertFalse(bootstrap.virtualEnvReader.isPyenvSdk("")) - assertFalse(bootstrap.virtualEnvReader.isPyenvSdk("aa\u0000bb")) - assertFalse(bootstrap.virtualEnvReader.isPyenvSdk("a/b/c/d")) - assertFalse(bootstrap.virtualEnvReader.isPyenvSdk(bootstrap.cwd)) - assertFalse(bootstrap.virtualEnvReader.isPyenvSdk(bootstrap.cwd.resolve("smthg"))) + Assert.assertFalse(bootstrap.virtualEnvReader.isPyenvSdk(null as String?)) + Assert.assertFalse(bootstrap.virtualEnvReader.isPyenvSdk("")) + Assert.assertFalse(bootstrap.virtualEnvReader.isPyenvSdk("aa\u0000bb")) + Assert.assertFalse(bootstrap.virtualEnvReader.isPyenvSdk("a/b/c/d")) + Assert.assertFalse(bootstrap.virtualEnvReader.isPyenvSdk(bootstrap.cwd)) + Assert.assertFalse(bootstrap.virtualEnvReader.isPyenvSdk(bootstrap.cwd.resolve("smthg"))) // particularly any path inside pyenv root will work - assertTrue(bootstrap.virtualEnvReader.isPyenvSdk(bootstrap.pyenv.resolve("xxx"))) + Assert.assertTrue(bootstrap.virtualEnvReader.isPyenvSdk(bootstrap.pyenv.resolve("xxx"))) // should resolve symlinks val link = bootstrap.cwd.resolve("smthg") @@ -145,9 +146,9 @@ class VirtualEnvReaderTest { // hanging links, should not resolve it Files.createSymbolicLink(link, target) - assertFalse(bootstrap.virtualEnvReader.isPyenvSdk(link)) + Assert.assertFalse(bootstrap.virtualEnvReader.isPyenvSdk(link)) Files.createFile(target) - assertTrue(bootstrap.virtualEnvReader.isPyenvSdk(link)) + Assert.assertTrue(bootstrap.virtualEnvReader.isPyenvSdk(link)) } } \ No newline at end of file diff --git a/python/tools/src/com/jetbrains/python/tools/BuildZippedSkeletons.kt b/python/tools/src/com/jetbrains/python/tools/BuildZippedSkeletons.kt index d3b626488e2c..73802f348eed 100644 --- a/python/tools/src/com/jetbrains/python/tools/BuildZippedSkeletons.kt +++ b/python/tools/src/com/jetbrains/python/tools/BuildZippedSkeletons.kt @@ -5,11 +5,13 @@ import com.intellij.openapi.vfs.VfsUtil import com.intellij.testFramework.TestApplicationManager import com.intellij.util.io.Compressor import com.jetbrains.python.sdk.PythonSdkUtil +import com.jetbrains.python.sdk.VirtualEnvReader import com.jetbrains.python.sdk.skeletons.DefaultPregeneratedSkeletonsProvider import com.jetbrains.python.sdk.skeletons.PySkeletonRefresher import com.jetbrains.python.tools.sdkTools.PySdkTools import com.jetbrains.python.tools.sdkTools.SdkCreationType import java.io.File +import kotlin.io.path.Path import kotlin.math.abs import kotlin.system.exitProcess @@ -29,7 +31,7 @@ fun main() { for (python in File(root).listFiles()!!) { println("Running on $python") - val executable = PythonSdkUtil.getPythonExecutable(python.absolutePath)!! + val executable = VirtualEnvReader.Instance.findPythonInPythonRoot(Path(python.absolutePath))!!.toString() val sdk = PySdkTools.createTempSdk(VfsUtil.findFileByIoFile(File(executable), true)!!, SdkCreationType.SDK_PACKAGES_ONLY, null, null) val skeletonsDir = File(workingDir, "skeletons-${sdk.versionString!!.replace(" ", "_")}_" + abs(sdk.homePath!!.hashCode())) diff --git a/python/tools/src/com/jetbrains/python/tools/PackPythonSdkLibsForStubs.kt b/python/tools/src/com/jetbrains/python/tools/PackPythonSdkLibsForStubs.kt index 056dd77e9ef7..1cbfe6fd91c4 100644 --- a/python/tools/src/com/jetbrains/python/tools/PackPythonSdkLibsForStubs.kt +++ b/python/tools/src/com/jetbrains/python/tools/PackPythonSdkLibsForStubs.kt @@ -3,9 +3,11 @@ package com.jetbrains.python.tools import com.jetbrains.python.PythonHelper import com.jetbrains.python.sdk.PythonSdkUtil +import com.jetbrains.python.sdk.VirtualEnvReader import java.io.BufferedReader import java.io.File import java.io.InputStreamReader +import kotlin.io.path.Path fun main() { @@ -21,7 +23,7 @@ fun main() { } val sdkHome = python.absolutePath - val executable = PythonSdkUtil.getPythonExecutable(sdkHome)?.let { File(it) } + val executable = VirtualEnvReader.Instance.findPythonInPythonRoot(Path(sdkHome))?.toFile() if (executable == null) { println("No python on $sdkHome")