diff --git a/python/openapi/src/com/jetbrains/python/venvReader/VirtualEnvReader.kt b/python/openapi/src/com/jetbrains/python/venvReader/VirtualEnvReader.kt index a0efcf226da3..92e1c8efb0d3 100644 --- a/python/openapi/src/com/jetbrains/python/venvReader/VirtualEnvReader.kt +++ b/python/openapi/src/com/jetbrains/python/venvReader/VirtualEnvReader.kt @@ -18,6 +18,8 @@ import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.TestOnly import java.io.IOException +import java.nio.file.NoSuchFileException +import java.nio.file.NotDirectoryException import java.nio.file.Path import kotlin.io.path.* @@ -57,7 +59,7 @@ class VirtualEnvReader private constructor( */ @RequiresBackgroundThread fun findVEnvInterpreters(): List = - findLocalInterpreters(getVEnvRootDir()) + findVenvsInDir(getVEnvRootDir()) @RequiresBackgroundThread fun getPyenvRootDir(eel: EelApi? = getLocalEelIfApp()): Directory { @@ -66,16 +68,26 @@ class VirtualEnvReader private constructor( @RequiresBackgroundThread fun findPyenvInterpreters(): List = - findLocalInterpreters(getPyenvVersionsDir()) + findVenvsInDir(getPyenvVersionsDir()) + /** + * List contents of [root] looking for envs there, returns all pythons it were able to find + */ @RequiresBackgroundThread - fun findLocalInterpreters(root: Directory): List { - if (!root.isDirectory()) { - return listOf() - } + fun findVenvsInDir(root: Directory): List { val candidates: ArrayList = arrayListOf() - for (dir in root.listDirectoryEntries()) { + val children = try { + root.listDirectoryEntries() + } + catch (_: NoSuchFileException) { + return emptyList() + } + catch (_: NotDirectoryException) { + return emptyList() + } + + for (dir in children) { findPythonInPythonRoot(dir)?.let { candidates.add(it) } } @@ -120,19 +132,12 @@ class VirtualEnvReader private constructor( */ @RequiresBackgroundThread fun findPythonInPythonRoot(dir: PythonHomePath): PythonBinary? { - if (!dir.isDirectory()) { - return null - } val bin = dir.resolve("bin") - if (bin.isDirectory()) { - findInterpreter(bin)?.let { return it } - } + findInterpreter(bin)?.let { return it } val scripts = dir.resolve("Scripts") - if (scripts.isDirectory()) { - findInterpreter(scripts)?.let { return it } - } + findInterpreter(scripts)?.let { return it } return findInterpreter(dir) } @@ -170,10 +175,18 @@ class VirtualEnvReader private constructor( @RequiresBackgroundThread private fun findInterpreter(dir: Path): PythonBinary? { val pythonNames = when (forcedOs ?: dir.getEelDescriptor().osFamily) { - EelOsFamily.Posix -> setOf("pypy", "python") - EelOsFamily.Windows -> setOf("pypy.exe", "python.exe") + EelOsFamily.Posix -> POSIX_BINS + EelOsFamily.Windows -> WIN_BINS + } + return try { + dir.listDirectoryEntries().firstOrNull { it.name.lowercase() in pythonNames && it.isRegularFile() } + } + catch (_: NotDirectoryException) { + return null + } + catch (_: NoSuchFileException) { + return null } - return dir.listDirectoryEntries().firstOrNull { it.isRegularFile() && it.name.lowercase() in pythonNames } } @RequiresBackgroundThread @@ -203,6 +216,8 @@ class VirtualEnvReader private constructor( @Suppress("VENV_IS_OK") // The only place it should be used in prod const val DEFAULT_VIRTUALENV_DIRNAME: String = ".venv" + private val POSIX_BINS = setOf("pypy", "python") + private val WIN_BINS = setOf("pypy.exe", "python.exe") private fun getLocalEelIfApp(): EelApi? = if (ApplicationManager.getApplication() != null) localEel else null } } \ No newline at end of file diff --git a/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java b/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java index 0b0a89a89f5c..0d51c8b4e8b1 100644 --- a/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java @@ -57,11 +57,11 @@ public final class VirtualEnvSdkFlavor extends CPythonSdkFlavor