Python stubmaker 存根生成:语法参数、实战案例与常见错误排查
stubmaker 是 Python 官方配套的 .pyi 存根文件自动生成工具,它对纯 Python 源码和 C/C++/Rust 编写的编译扩展(.pyd / .so)都有效,能自动推导函数签名、类属性、泛型并生成符合 PEP484/561 标准的类型提示文件。与内置的 stubgen 相比,stubmaker 最大的优势是可以解析二进制扩展库。安装方式:
pip install stubmaker
# 可选安装类型检查依赖
pip install stubmaker mypy typing-extensions
从源码安装最新开发版:
git clone https://github.com/osandov/stubmaker.git
cd stubmaker
pip install .
要求 Python ≥3.7,可选依赖 mypy、cython、setuptools。
命令行基础语法:
stubmaker [全局参数] 子命令 [子命令参数] 目标路径
两个核心子命令:generate(生成存根)和 check(校验存根)。
全局参数:-h/--help、-v/--verbose、-q/--quiet、--python-exec PATH(指定解释器)、--cache-dir DIR(缓存目录)。
generate 子命令主要参数:
-o/--output DIR 指定输出目录,默认与源码同目录
-r/--recursive 递归扫描文件夹
--ignore-private 跳过下划线开头的私有方法/变量
--include-docstrings将源码 docstring 写入 .pyi
--no-types-eval 关闭运行时类型推导(安全模式,仅 AST 静态解析)
--module-filter REGEX 只生成匹配正则的模块
--exclude REGEX 排除匹配正则的文件/模块
--incremental 增量更新,不覆盖手动修改的 .pyi
--force 强制覆盖旧存根
--stub-extension SUFFIX 自定义存根后缀,默认 .pyi
--add-type-imports 自动补充 typing 模块导入
--skip-constants 不生成模块常量存根
--export-all 自动生成 __all__ 列表
check 子命令参数:--strict(严格校验)、--mypy-config FILE(指定 mypy 配置文件)。
Python 内部 API 调用方式:
from stubmaker import StubGenerator, GeneratorConfig
config = GeneratorConfig(
recursive=True,
ignore_private=True,
output_dir="./stubs",
incremental=True
)
gen = StubGenerator(config=config)
gen.generate_file("mylib/main.py") # 生成单文件
# gen.generate_package("mylib/") # 生成整个包
# gen.check_stub("stubs/main.pyi") # 校验存根
以下为 8 个典型实战案例,覆盖常见场景。
案例1:为单个纯 Python 文件生成基础存根(忽略私有方法)
stubmaker generate --ignore-private utils.py
案例2:递归批量为整个项目包生成存根,输出到独立 stubs 文件夹,使用增量更新并导出所有公有对象。
命令行:
stubmaker generate -r -o ./stubs --incremental --export-all myproject/
API 版本:
from stubmaker import StubGenerator, GeneratorConfig
cfg = GeneratorConfig(recursive=True, output_dir="./stubs", incremental=True, export_all=True)
gen = StubGenerator(cfg)
gen.generate_package("./myproject")
案例3:解析 Cython 编译扩展 .pyd 二进制模块生成存根。注意不能加 --no-types-eval,必须运行时加载模块。
stubmaker generate --python-exec python3 fastcalc.pyd -o ./stubs
案例4:过滤模块并排除测试文件。只处理 core.* 模块,排除所有 test_*.py。
stubmaker generate -r --module-filter "^core\." --exclude "test_.*" src/
案例5:生成带文档注释和完整类型导入的标准化存根,适合发布给第三方。
stubmaker generate --include-docstrings --add-type-imports --force src/api.py -o dist/stubs
案例6:安全静态解析,防止执行恶意代码。使用 --no-types-eval 仅作 AST 分析,但无法处理二进制扩展库。
stubmaker generate --no-types-eval untrusted_lib.py
案例7:在自动化打包时生成存根并集成到 dist 中。编写 build_stubs.py:
from stubmaker import StubGenerator, GeneratorConfig
import os
def build_package_stubs():
cfg = GeneratorConfig(
recursive=True,
output_dir="./build/stubs",
ignore_private=True,
export_all=True
)
gen = StubGenerator(cfg)
gen.generate_package("./mypackage")
print("存根生成完成,路径:", os.path.abspath("./build/stubs"))
if __name__ == "__main__":
build_package_stubs()
然后配合 setup.py 的 cmdclass 调用该脚本。
案例8:校验已手写存根与源码类型一致性,严格模式阻断发布。
stubmaker check --strict stubs/core.pyi --mypy-config mypy.ini
API 校验:
from stubmaker import StubGenerator, GeneratorConfig
gen = StubGenerator(GeneratorConfig())
result = gen.check_stub("./stubs/core.pyi", strict=True)
if not result.success:
raise SystemExit("存根与源码类型不匹配")
常见错误与解决方案:
错误1:ModuleNotFoundError: No module named ‘stubmaker’。原因:未安装或 pip 环境不对。统一使用 python -m pip install stubmaker 安装。
错误2:Failed to load module xxx.pyd / segmentation fault。原因:Python 版本或位数不匹配,或系统库缺失。解决:使用匹配的解释器 --python-exec,或安装对应运行库;纯 Python 文件可尝试 --no-types-eval。
错误3:Type evaluation disabled, cannot process binary module。原因:同时使用了 --no-types-eval 和解析二进制模块。移除 --no-types-eval 即可。
错误4:Stub file not updated with --incremental but source changed。原因:增量模式保护了手动修改的文件。如需全量更新,加 --force 参数。
错误5:SyntaxError in generated .pyi, invalid type annotation。原因:源码存在元编程或 Python 版本不支持 | 联合类型。手动修正存根,或升级 Python 到 3.10+。
错误6:Recursive scan not working, subfolder stubs missing。原因:忘记加 -r/--recursive。命令行追加 -r,API 设置 recursive=True。
错误7:ImportError: cannot import name ‘StubGenerator’。原因:stubmaker 版本过低,不支持编程 API。升级到最新版。
错误8:Mypy reports “Missing type annotation”。原因:源码无类型注解,自动推导为 Any。建议在源码中补充基础类型注解,或手动修改存根。
使用注意事项:
- 默认模式会导入并执行目标模块,解析不信任的代码时应加 --no-types-eval(仅对纯 Python 有效)。
- 类型推导对元编程、动态属性、monkey patch 不准确,生成后建议人工校对。
- 生产项目建议将 .pyi 放入独立 stubs/ 目录,并添加 py.typed 文件(PEP561)。
- 版本迭代时使用 --incremental 保护手写注释。
- 大型项目开启 --cache-dir 缓存加速,并用 --exclude 过滤无关目录。
- Python 3.7 及以下生成存根会使用 Union[] 兼容写法,而非 |。
- 建议使用 stubmaker 0.12+ 稳定版以获得递归批量生成等特性。
Re: Python stubmaker 存根生成:语法参数、实战案例与常见错误排查
感谢楼主这么详细的整理!stubmaker 确实是对 stubgen 的一个有力补充,特别能处理二进制扩展这点很赞。案例写得清晰实用,尤其是增量更新和 `--no-types-eval` 安全模式,对接手旧项目或处理第三方库时帮助很大。有个小问题想请教:对于动态生成属性(比如 `__getattr__` 返回的)或装饰器修改过的函数签名,stubmaker 是否能正确推导?或者有没有推荐的兜底做法?Re: Python stubmaker 存根生成:语法参数、实战案例与常见错误排查
感谢楼主这么详细的分享!之前一直用 stubgen 但处理 .pyd 时就头疼,看到 stubmaker 能直接解析二进制扩展太实用了。案例 7 的自动化打包思路特别好,可以直接集成到构建流程里。 顺便请教一下:解析编译扩展时,如果模块依赖其他动态库(比如 dll/so),是否需要额外设置环境变量或参数?还是只要指定目标文件就能自动处理依赖?Re: Python stubmaker 存根生成:语法参数、实战案例与常见错误排查
感谢楼主分享,非常系统全面的教程。案例3关于Cython编译扩展的存根生成正好是我最近在折腾的,之前用stubgen没法解析.pyd文件,换stubmaker直接解决了。有个小疑问:对于包含多个动态链接库的大项目,如果用`-r`递归扫描,会不会因为循环导入或模块加载顺序出问题?楼主有没有遇到过?
页:
[1]