fix(windows): derive PYTHONPATH from the detected KiCAD python (#84)

findPythonExecutable already auto-detects KiCAD's bundled python across versions
(10.0/9.0/…) and both Program Files and per-user %LOCALAPPDATA% installs, but the
spawn still defaulted PYTHONPATH to a hardcoded "C:/Program Files/KiCad/9.0/..."
path. So a KiCad 10 or per-user user whose python.exe was found correctly still
got the wrong PYTHONPATH unless they set it by hand — the exact manual fixup
people keep posting in #84.

Add deriveKiCadSitePackages(pythonExe): for a detected KiCAD python at
<root>/<version>/bin/python.exe it returns the matching site-packages
(<...>/bin/Lib/site-packages, or the older lib/python3/dist-packages layout),
verified to exist. Use it as the PYTHONPATH default. Precedence is unchanged at
the top: explicit PYTHONPATH env override still wins; the legacy 9.0 string
remains only as a last-resort fallback, so this can't regress below current
behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
mixelpixx
2026-05-30 09:47:25 -04:00
parent 5493a37024
commit df02627134

View File

@@ -71,6 +71,29 @@ function getWindowsKiCadPythonCandidates(): string[] {
return [...new Set(candidates)];
}
/**
* Derive the KiCAD bundled-Python site-packages path for a detected python.exe,
* so PYTHONPATH follows the *same* install we picked (any version, Program Files
* or per-user %LOCALAPPDATA%) instead of a hardcoded KiCad 9.0 path.
*
* KiCAD on Windows installs python at `<root>/<version>/bin/python.exe`, with
* pcbnew under `<...>/bin/Lib/site-packages` (older/alt layouts use
* `<version>/lib/python3/dist-packages`). Returns the first existing candidate,
* or undefined if pythonExe isn't a KiCAD bundled python.
*/
function deriveKiCadSitePackages(pythonExe: string): string | undefined {
if (process.platform !== "win32") return undefined;
const lower = pythonExe.toLowerCase();
if (!lower.endsWith("python.exe") || !lower.includes("kicad")) return undefined;
const binDir = dirname(pythonExe); // <root>/<version>/bin
const versionDir = dirname(binDir); // <root>/<version>
const candidates = [
join(binDir, "Lib", "site-packages"),
join(versionDir, "lib", "python3", "dist-packages"),
];
return candidates.find((p) => existsSync(p));
}
/**
* Find the Python executable to use.
* Prioritizes project venvs, then explicit overrides, then KiCAD-bundled Python
@@ -475,12 +498,21 @@ export class KiCADMcpServer {
if (!isValid) {
throw new Error("Prerequisites validation failed. See logs above for details.");
}
// PYTHONPATH precedence: explicit env override → site-packages derived
// from the detected KiCAD python (any version / install location) →
// legacy 9.0 fallback as a last resort.
const derivedSitePackages = deriveKiCadSitePackages(pythonExe);
if (derivedSitePackages && !process.env.PYTHONPATH) {
logger.info(`Using KiCAD site-packages: ${derivedSitePackages}`);
}
this.pythonProcess = spawn(pythonExe, [this.kicadScriptPath], {
stdio: ["pipe", "pipe", "pipe"],
env: {
...process.env,
PYTHONPATH:
process.env.PYTHONPATH || "C:/Program Files/KiCad/9.0/lib/python3/dist-packages",
process.env.PYTHONPATH ||
derivedSitePackages ||
"C:/Program Files/KiCad/9.0/lib/python3/dist-packages",
},
});