使用nuitka打包python包为pyd

快速流程

环境准备

python -m pip install nuitka
python -m pip install mypy

生成pyd文件

python -m nuitka --module --output-dir=nuitka_build --include-package=pkgName PkgName

生成pyi文件(递归)

stubgen -p pkgName -o nuitka_build/

详细说明

下面是详细说明用 Nuitka 把一个 Python 包(含子包)编译成单个 .pyd,并为 IDE 生成 .pyi 类型桩以获得完善的自动补全的流程。

一、目标

  • 把包yourpkg编译为一个二进制扩展模块:nuitka_build/yourpkg.cp3xx-<platform>.pyd(Windows下为.pyd,Linux/macOS下为.so)。
  • 为该包生成配套的.pyi类型桩,IDE(PyCharm/VS Code)将有高质量的自动补全与跳转。

二、前置条件

  • Python 3.8+(与目标环境一致,位数也要一致,例如都为 x64)。
  • 编译器环境:
    • Windows:安装 Visual Studio Build Tools(含 MSVC 与 Windows SDK),确保 cl 在 PATH 中。
    • Linux:安装 gcc 和必要的构建工具(如 build-essential)。
    • macOS:安装 Xcode Command Line Toolsxcode-select --install)。
  • 包必须是合法包:包目录及其子目录需要包含 __init__.py(PEP 420 的命名空间包不建议用于此流程)。
  • 建议使用虚拟环境,以减少环境差异。

三、安装工具

  • 基本依赖
    • python -m pip install -U nuitka
    • python -m pip install -U mypy
  • 可选(让 Nuitka 更快/更稳):python -m pip install orderedset zstandard

四、编译为单个.pyd/.so

  • 样例说明
    • 假设包的导入名为 yourpkg(目录名同 yourpkg/),下面命令从项目根目录执行。
  • 基本命令
    • Windows/Linux/macOS 通用:
      • python -m nuitka --module yourpkg --include-package=yourpkg --output-dir=nuitka_build
  • 选项解释
    • --module:以“扩展模块”模式输出 .pyd/.so(而非可执行文件)。
    • --include-package=yourpkg:把 yourpkg 包及其子包编进同一个扩展模块里,支持后续 import yourpkg.submod
    • --output-dir:产物输出目录。
  • 常见补充
    • 动态导入(importlib.import_module/字符串拼接)可能被静态分析漏掉,需显式包含:
      • --include-module=yourpkg.submod_a --include-package=yourpkg.plugins
    • 如需附带包内的数据文件(json/模型等):
      • --include-data-files="yourpkg/data/*=yourpkg/data/"
      • 运行时建议用 importlib.resourcespkgutil.get_data 访问相对资源。
  • 注意
    • 模块模式”并不是“独立运行”打包,第三方依赖不会被合并到 .pyd,它们仍需在运行时可用(pip 安装到环境即可)。
    • 输出文件名包含 Python 版本与平台标签,例如 yourpkg.cp311-win_amd64.pyd

五、生成 .pyi 类型桩(递归)

  • 推荐从源码生成(保留注解信息更完整)
    • 确保yourpkg可被当前解释器导入(例如在项目根执行:python -m pip install -e .,或将项目根加入 PYTHONPATH)。
    • 生成:
      • python -m mypy.stubgen -p yourpkg -o nuitka_build
    • 完成后,nuitka_build 下会出现一个 yourpkg 目录,里面是 __init__.pyi 与各子模块 .pyi
  • 如果无法从源码生成(退而求其次)
    • 在能 import 到编译产物的环境中执行:
      • python -m mypy.stubgen -m yourpkg -o nuitka_build
    • 这种方式主要依赖运行时反射,类型信息通常较少,优先使用“从源码生成”。

六、放置与使用建议

  • 目录布局(示例)

    nuitka_build/
    
    - yourpkg.cp311-win_amd64.pyd   <-- 编译产物
    - yourpkg/                      <-- 仅包含 .pyi 的“桩包”
      - __init__.pyi
      - submod.pyi
      - utils/__init__.pyi
    
  • 为什么这样放

    • yourpkg.pyd(扩展模块)提供实际运行实现。
    • 旁边的 yourpkg/(只有 .pyi,没有 .py)只被类型检查器/IDE读取,不会影响运行时导入顺序。
    • Python 导入 yourpkg 时会选择 .pyd 作为实现;IDE 会优先读取 .pyi 进行补全与类型推断。
  • 测试导入

    • 在干净虚拟环境中,把 nuitka_build 加入 sys.path(或复制到 site-packages):
      • 交互式测试:PYTHONPATH=./nuitka_build python -c "import yourpkg, yourpkg.submod; print('OK')"
    • 在 PyCharm 中将 nuitka_build 标记为 Source Root,或把它安装到解释器的 site-packages 中。

七、常见问题排查

  • 找不到编译器(cl/gcc/clang)
    • Windows:安装VS Build Tools,用x64 Native Tools Command Prompt for VS执行命令。
    • Linux/macOS:安装对应工具链并确认在 PATH 中。
  • ModuleNotFoundError(第三方依赖)
    • 记住模块模式不会打包外部依赖,需在目标环境pip install这些依赖。
  • 包结构问题
    • 子目录缺少__init__.py会导致子包未被包含。补齐后重编译。
  • .pyi 没被 IDE 识别
    • 确认 .pyi.pyd 位于同一层级,.pyi 位于 yourpkg/ 目录中,且目录内不要放 __init__.py.py 文件),否则会遮蔽 .pyd
  • 大小与编译时间
    • 只包含需要的包:--include-package=yourpkg 足够。谨慎添加过多 include 选项以免体积膨胀。

八、从零到一的完整示例

  • 安装依赖
    • python -m pip install -U nuitka mypy
  • 编译包为 .pyd
    • python -m nuitka --module yourpkg --include-package=yourpkg --output-dir=nuitka_build
  • 生成 .pyi(递归)
    • python -m mypy.stubgen -p yourpkg -o nuitka_build
  • 验证
    • 在干净环境测试:PYTHONPATH=./nuitka_build python -c "import yourpkg, yourpkg.submod; print('OK')"
    • 在 IDE 中把 nuitka_build 加到解释器路径,检查自动补全是否生效。

到这里,就拥有了一个“单文件 .pyd + 桩包 .pyi”的可分发形态:运行时只需 .pyd(和运行所需的第三方依赖),开发体验上 IDE 通过 .pyi 获得与源码同等的补全、跳转与类型提示。

posted @ 2026-01-26 10:01  乌合之众  阅读(8)  评论(0)    收藏  举报
clear