From 956af7d0aad6985fede14b873e9c9fd10531a821 Mon Sep 17 00:00:00 2001 From: Vladimir Lagunov Date: Mon, 6 Oct 2025 12:18:07 +0200 Subject: [PATCH] Python Env Tests: support python.exe from Windows Apps in `PYTHON_FOR_TESTS` GitOrigin-RevId: dc3c04ab43749a1c02285ccdc4deab01f49b13c1 --- .../python/venvReader/VirtualEnvReader.kt | 16 +++++- .../testFramework/testEnv/PythonType.kt | 49 ++++++++++++++++--- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/python/openapi/src/com/jetbrains/python/venvReader/VirtualEnvReader.kt b/python/openapi/src/com/jetbrains/python/venvReader/VirtualEnvReader.kt index 37baab199da5..0cb99642ea34 100644 --- a/python/openapi/src/com/jetbrains/python/venvReader/VirtualEnvReader.kt +++ b/python/openapi/src/com/jetbrains/python/venvReader/VirtualEnvReader.kt @@ -10,7 +10,9 @@ import com.jetbrains.python.PythonHomePath import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.NonNls import java.io.IOException +import java.nio.file.FileSystemException import java.nio.file.Path +import java.nio.file.attribute.BasicFileAttributes import kotlin.io.path.* typealias Directory = Path @@ -151,7 +153,19 @@ class VirtualEnvReader( */ @RequiresBackgroundThread private fun findInterpreter(dir: Path): PythonBinary? = - dir.listDirectoryEntries().firstOrNull { it.isRegularFile() && it.name.lowercase() in pythonNames } + dir.listDirectoryEntries().firstOrNull { child -> + if (child.name.lowercase() !in pythonNames) { + return@firstOrNull false + } + try { + child.readAttributes().isRegularFile + } + catch (err: FileSystemException) { + // Handling a possible reparse point from WindowsApps. + if (SystemInfoRt.isWindows && err.javaClass == FileSystemException::class.java) true + else throw err + } + } @RequiresBackgroundThread private fun resolveDirFromEnvOrElseGetDirInHomePath(env: String, dirName: String): Path = diff --git a/python/setup-test-environment/src/com/intellij/python/community/testFramework/testEnv/PythonType.kt b/python/setup-test-environment/src/com/intellij/python/community/testFramework/testEnv/PythonType.kt index 23d2d1cfb37e..791ab51970c3 100644 --- a/python/setup-test-environment/src/com/intellij/python/community/testFramework/testEnv/PythonType.kt +++ b/python/setup-test-environment/src/com/intellij/python/community/testFramework/testEnv/PythonType.kt @@ -3,19 +3,24 @@ package com.intellij.python.community.testFramework.testEnv import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.projectRoots.Sdk +import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess import com.intellij.util.concurrency.annotations.RequiresBackgroundThread import com.jetbrains.python.PythonBinary import com.jetbrains.python.sdk.flavors.PythonSdkFlavor import com.jetbrains.python.venvReader.VirtualEnvReader +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.* +import kotlinx.coroutines.withContext import org.jetbrains.annotations.NonNls +import java.nio.file.FileSystemException import java.nio.file.Path +import java.nio.file.attribute.BasicFileAttributes import java.util.concurrent.ConcurrentHashMap import kotlin.Result.Companion.failure -import kotlin.io.path.exists -import kotlin.io.path.isDirectory +import kotlin.io.path.listDirectoryEntries import kotlin.io.path.pathString +import kotlin.io.path.readAttributes /** * Gradle script installs two types of python: conda and vanilla. Env could be obtained by [createSdkClosableEnv] which also provides closable @@ -67,16 +72,44 @@ abstract class PythonType(private val tag: @NonNls String) { """.trimIndent()) } val pythonPath = Path.of(pythonStr) - val pythonBinary = when { - pythonPath.isDirectory() -> VirtualEnvReader.Instance.findPythonInPythonRoot(pythonPath) - pythonPath.exists() -> pythonPath - else -> null - } ?: error("$PYTHON_FOR_TESTS env var points to something that is not a python: $pythonPath") - pythonBinary.parent + + withContext(Dispatchers.IO) { + var pythonBinary: Path? = null + val err = IllegalStateException("$PYTHON_FOR_TESTS env var points to something that is not a python: $pythonPath") + + try { + val attrs = pythonPath.readAttributes() + + pythonBinary = if (attrs.isDirectory) { + VirtualEnvReader.Instance.findPythonInPythonRoot(pythonPath) + } + else { + pythonPath + } + } + catch (err2: FileSystemException) { + err.initCause(err2) + + // Handling a possible reparse point from WindowsApps. + if (SystemInfo.isWindows && err2.javaClass == FileSystemException::class.java) { + try { + if (pythonPath in pythonPath.parent!!.listDirectoryEntries()) { + pythonBinary = pythonPath + } + } + catch (err3: FileSystemException) { + err.addSuppressed(err3) + } + } + } + + pythonBinary?.parent ?: throw err + } } else { null } + if (customPythonDir != null) { pythons.add(customPythonDir) }