fix(setup): align Python setup probe with eval
This commit is contained in:
@@ -2,6 +2,11 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `omp setup python` to validate the same configured or discovered interpreter used by the Python eval runtime.
|
||||
|
||||
|
||||
## [17.2.4] - 2026-08-01
|
||||
|
||||
### Added
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
* Handles `omp setup` for onboarding and `omp setup <component>` for optional dependencies.
|
||||
*/
|
||||
import * as path from "node:path";
|
||||
import { $which, APP_NAME, getProjectDir, getPythonEnvDir } from "@oh-my-pi/pi-utils";
|
||||
import { $ } from "bun";
|
||||
import { APP_NAME, getProjectDir, getPythonEnvDir } from "@oh-my-pi/pi-utils";
|
||||
import chalk from "chalk";
|
||||
import { Settings, settings } from "../config/settings";
|
||||
import { checkPythonKernelAvailability } from "../eval/py/kernel";
|
||||
import { theme } from "../modes/theme/theme";
|
||||
import { downloadSttModel, isSttModelCached } from "../stt/downloader";
|
||||
import { isSttModelKey, STT_MODEL_OPTIONS } from "../stt/models";
|
||||
@@ -82,25 +82,14 @@ function managedPythonPath(): string {
|
||||
/**
|
||||
* Check Python environment and kernel dependencies.
|
||||
*/
|
||||
async function checkPythonSetup(): Promise<PythonCheckResult> {
|
||||
const result: PythonCheckResult = {
|
||||
available: false,
|
||||
async function checkPythonSetup(cwd: string, interpreter?: string): Promise<PythonCheckResult> {
|
||||
const availability = await checkPythonKernelAvailability(cwd, interpreter);
|
||||
return {
|
||||
available: availability.ok,
|
||||
pythonPath: availability.pythonPath,
|
||||
usingManagedEnv: availability.pythonPath === managedPythonPath(),
|
||||
managedEnvPath: MANAGED_PYTHON_ENV,
|
||||
};
|
||||
|
||||
const systemPythonPath = $which("python") ?? $which("python3");
|
||||
const managedPath = managedPythonPath();
|
||||
const hasManagedEnv = await Bun.file(managedPath).exists();
|
||||
|
||||
const pythonPath = systemPythonPath ?? (hasManagedEnv ? managedPath : undefined);
|
||||
if (!pythonPath) {
|
||||
return result;
|
||||
}
|
||||
const probe = await $`${pythonPath} -c "import sys;sys.exit(0)"`.quiet().nothrow();
|
||||
result.pythonPath = pythonPath;
|
||||
result.available = probe.exitCode === 0;
|
||||
result.usingManagedEnv = pythonPath === managedPath;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,7 +115,10 @@ export async function runSetupCommand(cmd: SetupCommandArgs): Promise<void> {
|
||||
}
|
||||
|
||||
async function handlePythonSetup(flags: { json?: boolean; check?: boolean }): Promise<void> {
|
||||
const check = await checkPythonSetup();
|
||||
const cwd = getProjectDir();
|
||||
const projectSettings = await Settings.init({ cwd });
|
||||
const interpreter = projectSettings.get("python.interpreter")?.trim() || undefined;
|
||||
const check = await checkPythonSetup(cwd, interpreter);
|
||||
|
||||
if (flags.json) {
|
||||
console.log(JSON.stringify(check, null, 2));
|
||||
@@ -136,7 +128,7 @@ async function handlePythonSetup(flags: { json?: boolean; check?: boolean }): Pr
|
||||
|
||||
if (!check.pythonPath) {
|
||||
console.error(chalk.red(`${theme.status.error} Python not found`));
|
||||
console.error(chalk.dim("Install Python 3.8+ and ensure it's in your PATH"));
|
||||
console.error(chalk.dim("Install Python 3.8+ or set python.interpreter to its executable path"));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { afterEach, describe, expect, it } from "bun:test";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
import { TempDir } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const cliEntry = path.join(import.meta.dir, "..", "src", "cli.ts");
|
||||
|
||||
interface CliProcessResult {
|
||||
exitCode: number;
|
||||
output: string;
|
||||
error: string;
|
||||
}
|
||||
|
||||
async function runSetupPython(cwd: string): Promise<CliProcessResult> {
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
...process.env,
|
||||
NO_COLOR: "1",
|
||||
PI_CODING_AGENT_DIR: path.join(cwd, "agent"),
|
||||
};
|
||||
delete env.VIRTUAL_ENV;
|
||||
delete env.CONDA_DEFAULT_ENV;
|
||||
delete env.CONDA_PREFIX;
|
||||
const proc = Bun.spawn([process.execPath, cliEntry, "setup", "python", "--json"], {
|
||||
cwd,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
env,
|
||||
});
|
||||
const output = new Response(proc.stdout).text();
|
||||
const error = new Response(proc.stderr).text();
|
||||
const [exitCode, stdout, stderr] = await Promise.all([proc.exited, output, error]);
|
||||
return { exitCode, output: stdout, error: stderr };
|
||||
}
|
||||
|
||||
describe("omp setup python", () => {
|
||||
let projectDir: TempDir | undefined;
|
||||
|
||||
afterEach(async () => {
|
||||
await projectDir?.remove();
|
||||
projectDir = undefined;
|
||||
});
|
||||
|
||||
it.skipIf(process.platform === "win32")(
|
||||
"probes the project-configured interpreter instead of the PATH interpreter",
|
||||
async () => {
|
||||
projectDir = TempDir.createSync("@omp-setup-python-");
|
||||
const cwd = projectDir.path();
|
||||
const interpreter = path.join(cwd, "configured-python");
|
||||
await Bun.write(interpreter, "#!/bin/sh\nexit 0\n");
|
||||
await fs.chmod(interpreter, 0o755);
|
||||
await Bun.write(path.join(cwd, ".omp", "config.yml"), `python:\n interpreter: ${interpreter}\n`);
|
||||
|
||||
const result = await runSetupPython(cwd);
|
||||
|
||||
expect(result.error).toBe("");
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(JSON.parse(result.output)).toMatchObject({
|
||||
available: true,
|
||||
pythonPath: interpreter,
|
||||
usingManagedEnv: false,
|
||||
});
|
||||
},
|
||||
);
|
||||
it.skipIf(process.platform === "win32")("prefers the project venv over the PATH interpreter", async () => {
|
||||
projectDir = TempDir.createSync("@omp-setup-python-");
|
||||
const cwd = projectDir.path();
|
||||
const interpreter = path.join(cwd, ".venv", "bin", "python");
|
||||
await Bun.write(interpreter, "#!/bin/sh\nexit 0\n");
|
||||
await fs.chmod(interpreter, 0o755);
|
||||
|
||||
const result = await runSetupPython(cwd);
|
||||
|
||||
expect(result.error).toBe("");
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(JSON.parse(result.output)).toMatchObject({
|
||||
available: true,
|
||||
pythonPath: interpreter,
|
||||
usingManagedEnv: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user