mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-91493 Fix "Cannot find executable" after installing Poetry/uv on Windows
Post-install detection looked up the tool on PATH only, but pip and uv drop launchers into per-user dirs not on PATH (%APPDATA%\Python\Scripts, uv's bin). Use the pip helper's reported path and uv's `tool list --show-paths` instead; also run `uv tool update-shell` so the uv bin dir lands on the user's PATH. GitOrigin-RevId: 3c09002a08135d7bb98733185815f3ded6e21c52
This commit is contained in:
committed by
intellij-monorepo-bot
parent
6706df30f8
commit
0137d9f471
+18
-5
@@ -1,6 +1,7 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.python.uv.backend
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.platform.eel.EelApi
|
||||
import com.intellij.python.pytools.InstalledInfo
|
||||
import com.intellij.python.pytools.PyTool
|
||||
@@ -11,14 +12,16 @@ import com.intellij.python.uv.backend.runtime.uvCli
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.getOrNull
|
||||
import com.jetbrains.python.orLogException
|
||||
import com.jetbrains.python.sdk.add.v2.FileSystem
|
||||
import com.jetbrains.python.sdk.add.v2.PathHolder
|
||||
import com.jetbrains.python.sdk.add.v2.detectTool
|
||||
import com.jetbrains.python.sdk.add.v2.toFileSystem
|
||||
import com.jetbrains.python.sdk.impl.PySdkBundle
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.nio.file.Path
|
||||
|
||||
private val LOG = Logger.getInstance(UvToolManagerProvider::class.java)
|
||||
|
||||
/**
|
||||
* Provides a [UvToolManager] when a local `uv` is available for the target environment. Registered
|
||||
* `order="first"` so uv is preferred over the pip fallback.
|
||||
@@ -69,9 +72,19 @@ private class UvToolManager(
|
||||
}
|
||||
|
||||
private suspend fun run(tool: PyTool, reinstall: Boolean): PyResult<Path> {
|
||||
createUvToolRuntime(uv).uvCli().tool().install(tool.packageName.name, reinstall = reinstall).getOr { return it }
|
||||
val executable = fileSystem.detectTool(tool.packageName.name)
|
||||
?: return PyResult.localizedError(PySdkBundle.message("cannot.find.executable", tool.packageName.name, fileSystem.userReadableName))
|
||||
return Result.success(executable.path)
|
||||
val name = tool.packageName.name
|
||||
val uvTool = createUvToolRuntime(uv).uvCli().tool()
|
||||
uvTool.install(name, reinstall = reinstall).getOr { return it }
|
||||
// uv installs launchers into its own bin dir (`uv tool dir --bin`), which is not on PATH by default. Add it
|
||||
// so the tool is also runnable from the user's shell; best-effort, since the IDE uses the resolved path below
|
||||
// regardless (PY-91493).
|
||||
uvTool.updateShell().orLogException(LOG)
|
||||
// Resolve the executable from uv's own records rather than re-detecting it on PATH: the current process does
|
||||
// not see the freshly added PATH entry, so a PATH lookup would spuriously fail with "cannot find executable".
|
||||
val executable = uvTool.list(showPaths = true).getOrNull()
|
||||
?.firstOrNull { PyTool.findByPackageName(it.name) == tool }
|
||||
?.let { it.executables[name] ?: it.executables.values.firstOrNull() }
|
||||
?: return PyResult.localizedError(PySdkBundle.message("cannot.find.executable", name, fileSystem.userReadableName))
|
||||
return Result.success(executable)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.intellij.python.community.execService.ZeroCodeStdoutTransformer
|
||||
import com.intellij.python.pytools.runtime.PyToolRuntime
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.mapResult
|
||||
import java.nio.file.Path
|
||||
|
||||
/**
|
||||
@@ -98,9 +99,12 @@ class UvTool(runtime: PyToolRuntime) : UvCommand("tool", runtime) {
|
||||
suspend fun uninstall(): PyResult<Unit> = TODO()
|
||||
|
||||
/**
|
||||
* Ensure that the tool executable directory is on the PATH
|
||||
* `uv tool update-shell` — ensure the tool executable directory (`uv tool dir --bin`) is on the `PATH`,
|
||||
* updating the user's shell profile (Unix) or user `PATH` (Windows) if needed. Idempotent: a no-op when
|
||||
* the directory is already present. uv prints nothing of interest, so the stdout is discarded.
|
||||
*/
|
||||
suspend fun updateShell(): PyResult<Unit> = TODO()
|
||||
suspend fun updateShell(): PyResult<Unit> =
|
||||
executeAndHandleErrors("update-shell", transformer = ZeroCodeStdoutTransformer).mapResult { Result.success(Unit) }
|
||||
|
||||
/**
|
||||
* `uv tool dir` — the directory uv stores tools in, or (with [bin] = true) the directory their
|
||||
|
||||
+4
-6
@@ -11,7 +11,6 @@ import com.intellij.python.pytools.PyToolManager
|
||||
import com.intellij.python.pytools.PyToolManagerProvider
|
||||
import com.intellij.python.pytools.configuration.ConfigurablePyTool
|
||||
import com.intellij.python.pytools.getToolVersion
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.getOrNull
|
||||
import com.jetbrains.python.packaging.PyPackageVersionNormalizer
|
||||
@@ -20,7 +19,6 @@ import com.jetbrains.python.sdk.add.v2.FileSystem
|
||||
import com.jetbrains.python.sdk.add.v2.PathHolder
|
||||
import com.jetbrains.python.sdk.add.v2.detectTool
|
||||
import com.jetbrains.python.sdk.add.v2.toFileSystem
|
||||
import com.jetbrains.python.sdk.impl.PySdkBundle
|
||||
import com.jetbrains.python.sdk.installExecutableViaPythonScript
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -46,10 +44,10 @@ private class SystemPythonToolManager(
|
||||
private val systemPython: SystemPython,
|
||||
) : PyToolManager {
|
||||
override suspend fun install(tool: PyTool): PyResult<Path> {
|
||||
installExecutableViaPythonScript(systemPython.asExecutablePython.binary, "-n", tool.packageName.name).getOr { return it }
|
||||
val executable = fileSystem.detectTool(tool.packageName.name)
|
||||
?: return PyResult.localizedError(PySdkBundle.message("cannot.find.executable", tool.packageName.name, fileSystem.userReadableName))
|
||||
return Result.success(executable.path)
|
||||
// The pip helper drops the launcher into a per-user scripts directory that is frequently not on PATH
|
||||
// (e.g. %APPDATA%\Python\Scripts on Windows), so trust the path it reports rather than re-detecting the
|
||||
// tool on PATH, which would spuriously fail with "cannot find executable" (PY-91493).
|
||||
return installExecutableViaPythonScript(systemPython.asExecutablePython.binary, "-n", tool.packageName.name)
|
||||
}
|
||||
|
||||
/** The pip helper always installs the latest release, so an upgrade is just a fresh install. */
|
||||
|
||||
Reference in New Issue
Block a user