Use uv Python keys for runtime IDs

This commit is contained in:
William Woodruff
2026-09-03 16:15:42 -04:00
parent 358b824344
commit 22b0154feb
7 changed files with 88 additions and 194 deletions
+19 -64
View File
@@ -1,82 +1,37 @@
import { execFile } from "node:child_process";
import { join } from "node:path";
import { promisify } from "node:util";
import type { SetupInputs } from "./inputs";
const execFileAsync = promisify(execFile);
const PYTHON_RUNTIME_QUERY = `
import json
import platform
import sys
import sysconfig
print(json.dumps({
"implementation": sys.implementation.name,
"implementationVersion": list(sys.implementation.version),
"pythonVersion": platform.python_version(),
"freethreaded": sysconfig.get_config_var("Py_GIL_DISABLED") == 1,
}))
`;
type ReleaseLevel = "alpha" | "beta" | "candidate" | "final";
interface PythonRuntime {
implementation: string;
implementationVersion: [number, number, number, ReleaseLevel, number];
pythonVersion: string;
freethreaded: boolean;
}
function formatRuntimeId(runtime: PythonRuntime): string {
if (
typeof runtime.implementation !== "string" ||
runtime.implementation === "" ||
typeof runtime.pythonVersion !== "string" ||
runtime.pythonVersion === "" ||
typeof runtime.freethreaded !== "boolean"
) {
throw new Error("Invalid Python runtime metadata");
}
let id = `cpython-${runtime.pythonVersion}`;
if (runtime.implementation !== "cpython") {
const [major, minor, micro, releaseLevel, serial] =
runtime.implementationVersion;
const suffixes = { alpha: "a", beta: "b", candidate: "rc", final: "" };
const suffix = suffixes[releaseLevel];
if (
suffix === undefined ||
![major, minor, micro, serial].every(
(part) => Number.isInteger(part) && part >= 0,
)
) {
throw new Error("Invalid Python implementation version");
}
const implementationVersion = `${major}.${minor}.${micro}${suffix}${suffix ? serial : ""}`;
id = `${runtime.implementation}-${implementationVersion}-python-${runtime.pythonVersion}`;
}
return runtime.freethreaded ? `${id}-freethreaded` : id;
}
export async function getPythonRuntimeId(inputs: SetupInputs): Promise<string> {
if (!inputs.activateEnvironment) {
return "";
}
const pythonPath =
process.platform === "win32"
? join(inputs.venvPath, "Scripts", "python.exe")
: join(inputs.venvPath, "bin", "python");
try {
const { stdout } = await execFileAsync(
pythonPath,
["-I", "-c", PYTHON_RUNTIME_QUERY],
"uv",
[
"python",
"list",
inputs.venvPath,
"--only-installed",
"--output-format",
"json",
],
{ encoding: "utf8" },
);
return formatRuntimeId(JSON.parse(stdout));
const pythons = JSON.parse(stdout);
if (
!Array.isArray(pythons) ||
pythons.length !== 1 ||
typeof pythons[0]?.key !== "string" ||
pythons[0].key === ""
) {
throw new Error("Expected one installed Python with a runtime key");
}
return pythons[0].key;
} catch (error) {
throw new Error(
`Failed to identify the activated environment's Python runtime: ${error instanceof Error ? error.message : String(error)}`,