fix(run): run a Python script with the project interpreter, not the runserver command line (#897)

Root cause: projectPython built the interpreter prefix from the first Python configuration by dropping only the .py arguments, so a Django `manage.py runserver` config left `runserver` in the prefix. The prefix now ends where the program starts (`-m` or the script), so program arguments such as `runserver` and `test` never reach it; `uv run python` is kept. Covers both plain scripts and pytest files derived from the same prefix.

Co-authored-by: andreabasile <[email protected]>
This commit is contained in:
Andrea Basileandandreabasile authored and GitHub committed 2026-11-02 23:37:47 +08:00
1 parent ece2b2a59e
commit 51988985c5
2 files changed
+27 -2

No files matched your search

+22
View File
@@ -271,4 +271,26 @@ describe("context configuration", () => {
const uv = [cfg({ id: "pytest", kind: "pytest", command: "uv", args: ["run", "pytest"], isTest: true })];
expect(contextConfig("tools/gen.py", uv)).toMatchObject({ command: "uv", args: ["run", "python", "tools/gen.py"] });
});
it("takes the interpreter from a Django runserver config without its arguments", () => {
const runserver = (command: string, args: string[]) =>
cfg({ id: "django:runserver", kind: "python", command, args: [...args, "manage.py", "runserver"] });
expect(contextConfig("tools/seed.py", [runserver(".venv/bin/python", [])])).toMatchObject({
command: ".venv/bin/python",
args: ["tools/seed.py"],
});
expect(contextConfig("tools/seed.py", [runserver("uv", ["run", "python"])])).toMatchObject({
command: "uv",
args: ["run", "python", "tools/seed.py"],
});
expect(contextConfig("worker/test_worker.py", [runserver(".venv/bin/python", [])])).toMatchObject({
args: ["-m", "pytest", "worker/test_worker.py"],
});
});
it("keeps the interpreter of a plain python config", () => {
const plain = [cfg({ id: "python:module:app", kind: "python", command: "python3", args: ["-m", "app"] })];
expect(contextConfig("tools/gen.py", plain)).toMatchObject({ command: "python3", args: ["tools/gen.py"] });
expect(contextConfig("tools/gen.py", [])).toMatchObject({ command: "python3", args: ["tools/gen.py"] });
});
});
+5 -2
View File
@@ -91,8 +91,11 @@ function derived(base: RunConfig, relPath: string, patch: Partial<RunConfig>): R
export function projectPython(configs: RunConfig[]): { command: string; prefix: string[] } | null {
const py = configs.find((c) => c.kind === "python");
if (py) {
const m = py.args.indexOf("-m");
return { command: py.command, prefix: m >= 0 ? py.args.slice(0, m) : py.args.filter((a) => !a.endsWith(".py")) };
// The interpreter part ends where the program starts: `-m`, or the script (`manage.py`). What follows
// (`runserver`, `test`…) belongs to that program, never to the interpreter.
const end = py.args.findIndex((a) => a === "-m" || a.endsWith(".py"));
const python = py.args.findIndex((a) => /^python[\d.]*$/.test(a));
return { command: py.command, prefix: py.args.slice(0, end >= 0 ? end : python + 1) };
}
const pytest = configs.find((c) => c.kind === "pytest");
if (!pytest) return null;