Windows 上别直接相信 python 命令:给本地校验补一个跨平台入口
最近整理 Codex Skill 命名时,最后一步需要跑一遍 quick_validate.py。这个校验脚本本身很简单:读 SKILL.md,检查 frontmatter、必填字段和命名规则。但在 Windows 机器上,事情卡在了更靠前的位置:python / python3 命令并不一定是真的 Python。
有些 Windows 设备上,python / python3 会先命中 Microsoft Store 的 App Execution Alias。这个入口负责在系统里还没有可用 Python 时,把新手带到 Microsoft Store 安装入口;带参数运行时,它通常返回失败,不执行脚本。于是你以为自己在跑校验,实际连脚本都没启动。另一个常见情况是机器上有 Python,但没有装 PyYAML,脚本仍然会在 import 阶段失败。
这种问题不适合靠每次手动记忆解决。
为什么会跳到 Microsoft Store
Windows 自己没有系统支持的 Python 安装,Python 官方的 Windows 使用文档开头也把这个差异单独说出来。为了让新用户能找到安装入口,Windows 提供了 python.exe / python3.exe 的快捷入口;Microsoft 的 Python 入门文档也说明了这个入口会把用户带到 Microsoft Store 里的 Python 包。
关键路径在 %LOCALAPPDATA%\Microsoft\WindowsApps。这个目录通常在用户的 PATH 里,里面可以放 App Execution Alias 暴露出来的 python.exe、python3.exe 等命令。终端解析 python 时会按 PATH 顺序找可执行文件:如果前面没有真实 Python,找到的就是 WindowsApps 里的别名。脚本表面写的是 python skills/.../quick_validate.py,系统实际启动的却是安装引导入口,校验脚本当然不会执行。
问题发生在 quick_validate.py、pip 和 PowerShell 接手之前:命令名解析到了一个安装引导入口,而不是 Python runtime。即使 python --version 能正常返回,也最好确认它是哪一个解释器;同一台机器上可能同时存在 Python install manager、python.org 安装包、Microsoft Store 版本、虚拟环境和旧版 launcher。
一台机器上怎么修
先看当前终端到底会执行谁:
Get-Command python -All
Get-Command python3 -All
Get-Command py -All
where.exe python
where.exe python3
py --list如果结果里 %LOCALAPPDATA%\Microsoft\WindowsApps\python.exe 排在前面,或者直接运行 python 会打开 Microsoft Store,先把真实 Python 装上:
winget install Python.Python.3.14也可以从 python.org 下载安装包。用 python.org 安装器时,勾选 Add python.exe to PATH;安装完成后重新打开终端,让新的 PATH 生效。然后再确认一次:
python --version
where.exe python如果 where.exe python 仍然先看到 %LOCALAPPDATA%\Microsoft\WindowsApps\python.exe,再回到「App execution aliases」里关掉 App Installer 下面的 python.exe 和 python3.exe,或者把真实 Python 路径调到更前面。
还有几种场景适合单独处理:
- 只想停止跳商店:直接打开 Windows 设置里的「App execution aliases」或开始菜单里的「Manage app execution aliases」,找到 App Installer 下面的
python.exe和python3.exe,把它们关掉。关掉之后,如果机器上没有真实 Python,python可能会变成找不到命令;这比静默跳到商店更适合脚本环境。- 需要管理多个 Python 版本:优先用
py --list看可用版本,用py -3或py -V:3.14选择解释器。虚拟环境已经激活时,再用当前环境里的python。- 脚本或 CI 需要稳定入口:不要只写死
python。允许传入--python <path>,支持PYTHON/CODEX_PYTHON这类环境变量,或者在仓库脚本里按顺序探测几个候选解释器。
这个入口解决到哪里
安装真实 Python、关掉 alias,或者让真实 Python 排到 PATH 前面,都会影响整台机器:其他裸 python 脚本也会跟着改变。validate-skills.mjs 的影响范围更窄。它只保证 node tools/validate-skills.mjs 这一条维护命令能找到可用解释器,不会修改 Windows 设置,不会重写 PATH,也不会替其他仓库命令绕开 Microsoft Store alias。
后续处理不需要让每个 Python 脚本都复制一遍这套探测逻辑。可以按使用频率和边界分三类处理:
- 临时自己跑一次的脚本,先用
Get-Command看清解释器,或者直接写py -3 script.py、<venv>\Scripts\python.exe script.py。 - 仓库里要求别人反复执行的维护命令,给一个稳定入口,比如
node tools/<command>.mjs、pnpm <script>、uv run ...、poetry run ...。入口内部可以复用同一段 Python 探测逻辑,不要每个脚本各写一份。 - Python 项目本身,用虚拟环境或项目工具锁解释器和依赖。
python是否跳商店应该在环境初始化阶段解决,不应该散落到每个业务脚本里。
这次给 quick_validate.py 单独包一层,是因为它处在一个 Node 仓库里,只有一个稳定使用的 Python 校验入口,还额外需要准备 PyYAML。如果后面同类 Python 入口变多,下一步应该抽公共 runner;现在直接抽一个很通用的 Python 运行器,反而会把这篇文章从具体问题拉远。
给校验包一层 Node 入口
这次我在 codex-home 里加了一个入口:
node tools/validate-skills.mjs [skill-dir ...]不传参数时,它会校验 skills/ 下全部自定义 Skill;传参数时,只校验指定目录。
它做的事很克制:
- 按顺序寻找
--python、PYTHON、CODEX_PYTHON、Codex bundled Python、Windowspy -3、python3、python。 - 每个候选都会先跑一段最小探针:
import sys; print(sys.executable)。 - 如果候选是 Microsoft Store 占位符,探针会失败,然后继续试下一个。
- 如果 Python 可用但缺
PyYAML,就把PyYAML安装到系统临时目录下的专用依赖目录。 - 真正的校验规则仍然交给原来的
skills/skill-creator/scripts/quick_validate.py。
也就是说,Node 不接管校验逻辑,只负责把「找得到一个能跑校验的 Python」这件事做稳。
为什么不是重写成 JS
把 quick_validate.py 重写成 JS 当然也能绕开 Python 问题,但那会把已有校验逻辑复制一份,后面规则变化时容易两边不一致。
更好的边界是:Python 脚本继续作为规则源,Node 包装器只处理跨平台启动问题。这样后续要改 Skill 校验规则,仍然只需要改一个地方;要处理 Windows、macOS、Linux 的解释器差异,则在包装器里消化。
这个仓库本来已经用 Node 做 setup-codex-home.mjs,所以再用一个 Node 脚本做跨平台入口也顺手。对使用者来说,记住一个命令就够了:
node tools/validate-skills.mjs skills/comment-wrap在 Windows 上,它可以选中 Codex bundled Python,再把 PyYAML 放进 %TEMP%:
python: Codex bundled Python (<home>/.cache/codex-runtimes/...)
pyyaml: <temp>/codex-home-skill-validator/...
[OK] skills\comment-wrap文档也要跟着换入口
工具加完之后,我把维护说明里原来的:
python3 skills/skill-creator/scripts/quick_validate.py <skill-dir>换成了:
node tools/validate-skills.mjs <skill-dir>comment-wrap 的自测命令也一起换掉。这样后面改 Skill 时,不会再被文档带回 python3 那条不稳定路径。
命令入口要吸收环境差异
Windows 上的 python 命令不等于「可用 Python」。它可能是 Python install manager,可能是 PATH 里的某个解释器,可能来自虚拟环境,也可能只是商店占位符。对一次性本地操作来说,发现了再手动修也可以;但对仓库维护命令来说,最好不要把这种设备状态暴露给每次执行的人。
凡是「仓库要求经常跑、而且依赖解释器和第三方包」的命令,都应该有一个稳定入口。这个入口不一定要很复杂,但至少要明确解释器来源、依赖准备位置和失败信息。环境差异被工具吸收掉,人的注意力才可以留给真正的校验结果。