Python 具有多个模块的扩展
Python extension with multiple modules
我正在为我编写的独立 C 库构建 Python 绑定。图书馆的文件布局如下:
<project root>
|
`- cpython
| |
| `- module1_mod.c
| `- module2_mod.c
| `- module3_mod.c
|
`- include
| |
| `- module1.h
| `- module2.h
| `- module3.h
|
`- src
| |
| `- module1.c
| `- module2.c
| `- module3.c
|
`- setup.py
我想获得一个 Python 包,这样我就可以在 my_package.module1
、my_package.module2
等名称空间中导入模块
到目前为止,这是我的setup.py
:
from os import path
from setuptools import Extension, setup
ROOT_DIR = path.dirname(path.realpath(__file__))
MOD_DIR = path.join(ROOT_DIR, 'cpython')
SRC_DIR = path.join(ROOT_DIR, 'src')
INCL_DIR = path.join(ROOT_DIR, 'include')
EXT_DIR = path.join(ROOT_DIR, 'ext')
ext_libs = [
path.join(EXT_DIR, 'ext_lib1', 'lib.c'),
# [...]
]
setup(
name="my_package",
version="1.0a1",
ext_modules=[
Extension(
"my_package.module1",
[
path.join(SRC_DIR, 'module1.c',
path.join(MOD_DIR, 'module1_mod.c',
] + ext_libs,
include_dirs=[INCL_DIR],
libraries=['uuid', 'pthread'],
),
],
)
导入 mypackage.module1
有效,但问题是 module2
和 module3
也需要外部库(并非所有模块都需要),我假设如果我在其他模块中包含相同的外部库,我会变得很臃肿。
我查看了 Github 中的示例设置,但没有找到解决此问题的示例。
什么是组织构建的好方法?
编辑:这实际上是一个更严重的问题,因为我在 module1
中有 module2
中需要的符号,等等。 module2
中的对象需要 module1
中定义的对象类型。如果我创建单独的二进制文件而不包括每个依赖项的所有源,则这些符号在链接时将不可用,从而增加了跟踪哪个模块所需内容的冗余和复杂性。
经过几天深入研究 Python 错误报告和几乎没有记录的功能,我找到了解决这个问题的答案,它解决了多个外部依赖项和内部交叉链接。
解决方案是创建一个整体“模块”,其中定义了所有模块,然后在包初始化文件中用几行 Python 代码公开它们。
为此,我将模块源文件更改为头文件,将它们的大部分方法保持静态并且只公开 PyTypeObject
结构和我的对象类型结构,以便它们可以在其他模块中使用。
然后我将定义所有模块的 PyMODINIT_FUNC
函数移到“包”模块 (py_mypackage.c
) 中,该模块还定义了一个空模块。 “包”模块定义为 _my_package
.
最后,我在 __init__.py
脚本中添加了一些内部机制,该脚本从 .so 文件中提取模块符号并将它们作为包的模块公开。这记录在 Python docs :
import importlib.util
import sys
import _my_package
pkg_path = _my_package.__file__
def _load_module(mod_name, path):
spec = importlib.util.spec_from_file_location(mod_name, path)
module = importlib.util.module_from_spec(spec)
sys.modules[mod_name] = module
spec.loader.exec_module(module)
return module
for mod_name in ('module1', 'module2', 'module3'):
locals()[mod_name] = _load_module(mod_name, pkg_path)
新布局是这样的:
<project root>
|
`- cpython
| |
| `- my_package
| |
| `- __init__.py
|
| `- py_module1.h
| `- py_module2.h
| `- py_module3.h
| `- py_mypackage.c
|
`- include
| |
| `- module1.h
| `- module2.h
| `- module3.h
|
`- src
| |
| `- module1.c
| `- module2.c
| `- module3.c
|
`- setup.py
和setup.py
:
setup(
name="my_package",
version="1.0a1",
package_dir={'my_package': path.join(CPYTHON_DIR, 'my_package')},
packages=['my_package'],
ext_modules=[
Extension(
"_my_package",
"<all .c files in cpython folder + ext library sources>",
libraries=[...],
),
],
)
出于好奇,完整代码位于 https://notabug.org/scossu/lsup_rdf/src/e08da1a83647454e98fdb72f7174ee99f9b8297c/cpython(固定在当前提交)。
我正在为我编写的独立 C 库构建 Python 绑定。图书馆的文件布局如下:
<project root>
|
`- cpython
| |
| `- module1_mod.c
| `- module2_mod.c
| `- module3_mod.c
|
`- include
| |
| `- module1.h
| `- module2.h
| `- module3.h
|
`- src
| |
| `- module1.c
| `- module2.c
| `- module3.c
|
`- setup.py
我想获得一个 Python 包,这样我就可以在 my_package.module1
、my_package.module2
等名称空间中导入模块
到目前为止,这是我的setup.py
:
from os import path
from setuptools import Extension, setup
ROOT_DIR = path.dirname(path.realpath(__file__))
MOD_DIR = path.join(ROOT_DIR, 'cpython')
SRC_DIR = path.join(ROOT_DIR, 'src')
INCL_DIR = path.join(ROOT_DIR, 'include')
EXT_DIR = path.join(ROOT_DIR, 'ext')
ext_libs = [
path.join(EXT_DIR, 'ext_lib1', 'lib.c'),
# [...]
]
setup(
name="my_package",
version="1.0a1",
ext_modules=[
Extension(
"my_package.module1",
[
path.join(SRC_DIR, 'module1.c',
path.join(MOD_DIR, 'module1_mod.c',
] + ext_libs,
include_dirs=[INCL_DIR],
libraries=['uuid', 'pthread'],
),
],
)
导入 mypackage.module1
有效,但问题是 module2
和 module3
也需要外部库(并非所有模块都需要),我假设如果我在其他模块中包含相同的外部库,我会变得很臃肿。
我查看了 Github 中的示例设置,但没有找到解决此问题的示例。
什么是组织构建的好方法?
编辑:这实际上是一个更严重的问题,因为我在 module1
中有 module2
中需要的符号,等等。 module2
中的对象需要 module1
中定义的对象类型。如果我创建单独的二进制文件而不包括每个依赖项的所有源,则这些符号在链接时将不可用,从而增加了跟踪哪个模块所需内容的冗余和复杂性。
经过几天深入研究 Python 错误报告和几乎没有记录的功能,我找到了解决这个问题的答案,它解决了多个外部依赖项和内部交叉链接。
解决方案是创建一个整体“模块”,其中定义了所有模块,然后在包初始化文件中用几行 Python 代码公开它们。
为此,我将模块源文件更改为头文件,将它们的大部分方法保持静态并且只公开 PyTypeObject
结构和我的对象类型结构,以便它们可以在其他模块中使用。
然后我将定义所有模块的 PyMODINIT_FUNC
函数移到“包”模块 (py_mypackage.c
) 中,该模块还定义了一个空模块。 “包”模块定义为 _my_package
.
最后,我在 __init__.py
脚本中添加了一些内部机制,该脚本从 .so 文件中提取模块符号并将它们作为包的模块公开。这记录在 Python docs :
import importlib.util
import sys
import _my_package
pkg_path = _my_package.__file__
def _load_module(mod_name, path):
spec = importlib.util.spec_from_file_location(mod_name, path)
module = importlib.util.module_from_spec(spec)
sys.modules[mod_name] = module
spec.loader.exec_module(module)
return module
for mod_name in ('module1', 'module2', 'module3'):
locals()[mod_name] = _load_module(mod_name, pkg_path)
新布局是这样的:
<project root>
|
`- cpython
| |
| `- my_package
| |
| `- __init__.py
|
| `- py_module1.h
| `- py_module2.h
| `- py_module3.h
| `- py_mypackage.c
|
`- include
| |
| `- module1.h
| `- module2.h
| `- module3.h
|
`- src
| |
| `- module1.c
| `- module2.c
| `- module3.c
|
`- setup.py
和setup.py
:
setup(
name="my_package",
version="1.0a1",
package_dir={'my_package': path.join(CPYTHON_DIR, 'my_package')},
packages=['my_package'],
ext_modules=[
Extension(
"_my_package",
"<all .c files in cpython folder + ext library sources>",
libraries=[...],
),
],
)
出于好奇,完整代码位于 https://notabug.org/scossu/lsup_rdf/src/e08da1a83647454e98fdb72f7174ee99f9b8297c/cpython(固定在当前提交)。