关于 pystub
pystub 是面向 Python 打包开发者的 MCP 服务,适用于 PyInstaller、Nuitka 和 cx_Freeze。它分析导入关系与运行时使用情况,识别可替换为桩代码的包,并生成最小桩代码和构建指引。
主要功能
- 01识别可安全替换为桩代码的包
- 02分析导入关系和包使用情况
- 03可视化依赖图
- 04生成包及子模块桩代码
- 05标记受保护导入和 C 扩展
适用场景
- 01通过桩化未使用依赖缩小可执行文件
- 02检查包是否为运行所必需
- 03生成桩代码以排除 C 扩展依赖
- 04在打包前检查依赖路径
MCP 相关
向 MCP 客户端提供分析、依赖图、包检查和桩代码生成工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
mcp-pystub
一个 MCP 服务,可在 Python exe 构建(PyInstaller / Nuitka / cx_Freeze)时自动检测可替换为存根的包,并生成最小存根代码。
一个 MCP 服务,可在 Python exe 构建(PyInstaller / Nuitka / cx_Freeze)时自动检测可替换为存根的包,并生成最小存根代码,以减小可执行文件的体积。
背景
将 Python 应用构建为可执行文件时,依赖项在模块级别导入的重量级包会被完整打包,导致 exe 体积膨胀。将实际代码路径中未使用的包替换为最小化的虚拟包(存根),即可大幅减小体积。
实证数据
asammdf 项目 — PyInstaller exe 构建(已通过端到端运行验证):
| exe 体积 | 运行状态 | PySide6 | |
|---|---|---|---|
| 未使用存根 | 431 MB | 正常 | 已加载 |
| 应用 asammdf.gui 存根 | 259 MB | 正常 | 已排除 |
| 减少 | -40%(172 MB) | 无影响 |
asammdf 转换工具(analyze 结果):
stubbable: pandas (59.7 MB), canmatrix (4.0 MB) — 合計 63.7 MB
submodule hints: asammdf.gui → PySide6 (523 MB) 排除可能 — 50 hints 検出
手动存根化的 3 个包(pandas、canmatrix、asammdf.gui)均已自动检测
功能
| 工具 | 说明 |
|---|---|
analyze | 分析 import 图并自动检测可存根化的候选包。还会输出用于间接排除 C 扩展包的提示 |
graph | 将 import 图显示为节点和边 |
check | 深入分析特定包的使用情况 |
generate | 生成最小包存根代码 + 构建说明 |
generate_submodule | 生成子模块存根,以间接排除 C 扩展包 |
判定结果
| 判定 | 含义 |
|---|---|
| stubbable | 可以存根化。在项目的运行路径中未使用 |
| nofollow | 有 try/except 保护。建议使用 --nofollow-import-to 排除 |
| required | 不可存根化。运行时实际使用 |
可以做什么
- 通用检测,不绑定特定库:仅根据 AST 结构判定,不使用硬编码规则
- 追踪函数级别的使用情况:使用
mdf.get(),但不使用mdf.to_dataframe()→ pandas 可存根化 - 区分调用与引用:
isinstance(x, pd.DataFrame)可安全使用存根;pd.DataFrame(data)则属于实际使用 - 检测类继承:
class User(BaseModel)→ pydantic 为 required - 检测模块级调用:追踪导入时执行的代码
- 自动检测 try/except 保护:将受保护的 import 判定为
nofollow - 自动生成存根代码:仅包含被引用符号的最小存根 + 构建说明(备份、恢复、验证)
- 自动检测 C 扩展:判定包含 .pyd / .so 的包不可直接存根化
- 间接排除 C 扩展 (v0.2 新增):如果导入 C 扩展包的子模块未被使用,则可以存根化该子模块以排除 C 扩展。已验证 PySide6(523 MB)→ 使用 asammdf.gui 存根使 exe 体积减少 40%
- 安全恢复设计 (v0.2 新增):通过备份 + 固定版本的 pip + 验证命令实现三重安全保障
- PyInstaller 钩子信息:提示需要禁用的钩子文件
无法实现的功能与限制
- C 扩展包(.pyd / .so):直接存根化会导致 C 扩展缺失,因此判定为 required。不过,可以通过
generate_submodule间接排除(v0.2 已支持) - 动态 import(
importlib.import_module(变量)):静态分析无法追踪(会通过 warnings 提示) - PyInstaller 自定义钩子:可能与存根冲突,需要手动禁用钩子
- 延迟初始化模式:在
__init__中不直接调用、而在后续方法中使用的包,检测精度会降低(为确保安全,会判定为 required) - pip 名称与 import 名称不一致:尚不支持
python-dateutil→dateutil等名称映射 - 运行时条件分支:静态分析无法判定
if sys.version < (3,11)中的 import
安全策略
设计目标是**绝不发生“判定为可存根化,但实际上必需”**的情况。难以确定时,将判定为 required,以确保安全。相反方向的误判(实际可存根化,却判定为 required)可以接受。
分析算法
- 提取 Import:使用
ast.parse()分析各文件中的 import 语句(区分模块级、函数级和 try/except 保护) - 构建 Import 图:从入口点开始,通过 BFS 递归解析依赖关系(自动分类 stdlib / third_party / local)
- 使用情况分析:确定网关函数(在依赖库中调用包的函数),并追踪项目代码是否调用这些函数。仅引用名称(isinstance、类型注解)时,视为可安全使用存根并排除
- 模块级检测:如果导入时调用了包的函数,则提升为 required
- 子模块间接排除 (v0.2):找出导入 C 扩展包的子模块;如果项目没有直接导入该子模块,也没有调用其重新导出的符号,则输出存根提示
安装
pip install mcp-pystub
依赖包 / Dependencies
mcp>=1.0.0- Model Context Protocol SDK- 解析引擎仅使用 Python 标准库(ast、importlib、pathlib)
使用方法 / Usage
作为 MCP 服务启动 / Run as MCP server
mcp-pystub
Claude Desktop / Claude Code 配置
{
"mcpServers": {
"pystub": {
"command": "mcp-pystub"
}
}
}
工具使用示例 / Tool Examples
analyze
入力 / Input:
entry_point: "C:/project/converter.py"
python_path: "C:/project/.venv/Lib/site-packages"
出力 / Output:
{
"stubbable": [
{"package_name": "pandas", "estimated_size_mb": 59.7, "reason": "依存ライブラリ経由でのみ import..."}
],
"required": [
{"package_name": "numpy", "reason": "プロジェクトコードが直接 import し使用"},
{"package_name": "PySide6", "estimated_size_mb": 523.2,
"reason": "C 拡張...ただし asammdf.gui をスタブ化することで間接排除が可能",
"submodule_stubs": [{"submodule": "asammdf.gui", "target_package": "PySide6"}]}
],
"nofollow": [
{"package_name": "mpmath"}
],
"submodule_stub_hints": [
{"submodule": "asammdf.gui", "parent_package": "asammdf",
"target_package": "PySide6", "imported_symbols": ["plot"]}
],
"analysis_time_ms": 6478
}
generate
入力 / Input:
entry_point: "C:/project/converter.py"
package_name: "pandas"
出力 / Output:
{
"files": {
"pandas/__init__.py": "...",
"pandas/core/api.py": "class DataFrame: pass\nclass Series: pass\n..."
},
"original_file_count": 2980,
"stub_file_count": 266,
"stub_total_bytes": 209530,
"build_instructions": {
"install_commands": ["pip uninstall -y pandas", "# cp stubs to site-packages"],
"uninstall_commands": ["pip install pandas"],
"hook_disable": ["# hook-pandas*.py → .disabled"]
}
}
generate_submodule (v0.2 new)
入力 / Input:
entry_point: "C:/project/converter.py"
parent_package: "asammdf"
submodule: "asammdf.gui"
出力 / Output:
{
"parent_package": "asammdf",
"submodule": "asammdf.gui",
"files": {
"asammdf/gui/__init__.py": "\"\"\"Auto-generated stub...\"\"\"\ndef plot(*args, **kwargs): ..."
},
"eliminated_packages": ["PySide6", "scipy", "lxml"],
"original_size_bytes": 5907331,
"stub_size_bytes": 144,
"build_instructions": {
"backup_commands": ["cp -r .../asammdf/gui .../asammdf/gui.bak"],
"install_commands": ["rm -rf .../asammdf/gui", "cp -r _stubs/asammdf/gui/ .../asammdf/gui/"],
"uninstall_commands": ["mv .../asammdf/gui.bak .../asammdf/gui"],
"verify_commands": ["python -c \"import asammdf; print('OK')\""]
}
}
性能 / Performance
| 项目规模 | 解析时间 | 节点数 |
|---|---|---|
| 轻量(click) | 335ms | 73 |
| 中等规模(flask) | 1,743ms | 230 |
| 大型(pandas) | 4,820ms | 491 |
| 超大型(sympy) | 6,919ms | 681 |
测试 / Testing
python -m pytest tests/ -v
测试结果
| 测试类型 | 数量 | 结果 |
|---|---|---|
| 单元测试(9 个模块) | 91 | 全部通过 |
| PyPI 库大规模测试(requests、flask、pandas 等) | 84 | 0 次崩溃 |
| PyInstaller exe 构建 + 运行测试 | 22 | 全部 PASS |
| asammdf E2E(MDF 创建→读取→重采样) | 1 | exe 减少 40% + 运行正常 |
| 恢复测试(备份 → 恢复 → 验证) | 4 | 全部成功 |
许可证 / License
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。