You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将第三方module.pyd打包至内部PyPi并解决命名冲突实现导入?

问题描述

我有一个供应商提供的module.pyd文件,想推送到内部PyPi仓库,实现pip install module后能直接import module。但这个.pyd有两个限制:

  • 无法修改,依赖同目录下的多个dll文件
  • 因PyInit_module定义,不能重命名

我尝试了如下目录结构:

src/
├─ module/
│  ├─ __init__.py
│  ├─ module.pyd
│  ├─ other_dll.dll
MANIFEST.IN
setup.cfg

为了让包名是module(满足pip install module),我在src/module/__init__.py里写了这段代码:

import sys
import importlib.util

module_package_spec = importlib.util.find_spec("module")
package_dir = module_package_spec.submodule_search_locations[0]

module_pyd_spec = importlib.util.spec_from_file_location("module", package_dir + "\\module.pyd")
module = importlib.util.module_from_spec(module_pyd_spec)
sys.modules["module"] = module
spec.loader.exec_module(module)

但运行时触发错误:

module = importlib.util.module_from_spec(module_pyd_spec)
SystemError: initialization of module did not return an extension module

我推测是包和模块同名导致的冲突。如果在包外运行这段代码则正常:

import importlib.util

module_pyd_spec = importlib.util.spec_from_file_location("module", "<PACKAGE_ABSOLUTE_DIR>\\module.pyd")
module = importlib.util.module_from_spec(module_pyd_spec)

另外,手动把.pyd和dll直接放到用户Lib/site-packages目录是可行的,但不知道怎么用setuptools实现,求可行解决办法。

可行解决方案

方案1:直接部署到site-packages根目录

通过setuptools的data_files配置,把.pyd和dll直接安装到site-packages根目录,完全避开同名冲突,和手动操作效果一致。

配置文件示例:

  1. setup.cfg:
[metadata]
name = module
version = 0.1.0

[options]
packages = find:
package_dir =
    = src
data_files =
    = src/module/module.pyd
    = src/module/other_dll.dll
  1. MANIFEST.in:
include src/module/*.pyd
include src/module/*.dll

执行pip install module后,module.pyd和other_dll.dll会直接出现在Lib/site-packages根目录,直接import module即可正常使用。

方案2:用中转包实现同名导入

把实际包名改成module_wrapper(或其他名称),通过包内的__init__.py完成导入逻辑,让用户依然可以import module。

调整后的目录结构:

src/
├─ module_wrapper/
│  ├─ __init__.py
│  ├─ module.pyd
│  ├─ other_dll.dll
MANIFEST.IN
setup.cfg

配置文件示例:

setup.cfg:

[metadata]
name = module
version = 0.1.0

[options]
packages = find:
package_dir =
    = src
package_data =
    module_wrapper = *.pyd;*.dll

MANIFEST.in:

include src/module_wrapper/*.pyd
include src/module_wrapper/*.dll

__init__.py代码:

import sys
import os
import importlib.util

# 获取当前包的目录
package_dir = os.path.dirname(__file__)
pyd_path = os.path.join(package_dir, "module.pyd")

# 加载.pyd文件并替换sys.modules中的条目
spec = importlib.util.spec_from_file_location("module", pyd_path)
module = importlib.util.module_from_spec(spec)
sys.modules["module"] = module
spec.loader.exec_module(module)

# 将dll所在目录加入PATH,确保依赖能被找到
sys.path.insert(0, package_dir)

用户执行pip install module后,实际安装的是module_wrapper包,但可以直接import module,同时dll能被正确加载。

方案3:修复原方案的同名冲突问题

保留原包名module,通过修改__init__.py的加载逻辑,避开包与模块的同名冲突。

修改后的src/module/__init__.py:

import sys
import os
import importlib.util

# 获取当前__init__.py所在的目录
package_dir = os.path.dirname(__file__)
pyd_path = os.path.join(package_dir, "module.pyd")

# 加载.pyd文件前,先移除sys.modules中的包条目
spec = importlib.util.spec_from_file_location("module", pyd_path)
if spec:
    del sys.modules["module"]
    module = importlib.util.module_from_spec(spec)
    sys.modules["module"] = module
    spec.loader.exec_module(module)

# 添加dll目录到PATH
sys.path.insert(0, package_dir)

配套配置文件:

setup.cfg:

[metadata]
name = module
version = 0.1.0

[options]
packages = find:
package_dir =
    = src
package_data =
    module = *.pyd;*.dll

MANIFEST.in:

include src/module/*.pyd
include src/module/*.dll

这个方案通过先删除sys.modules中的包条目,再加载.pyd模块,解决了同名冲突问题,同时保留了pip install module和import module的使用方式。


内容的提问来源于stack exchange,提问作者Tomass Wilson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 13:53:16