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+ 稳定版以获得递归批量生成等特性。 |