如果放入命名空间包中,则子模块的python导入路径

标签 python python-2.7 python-module python-c-api

我有一个用 C 编写的 python 模块,它有一个主模块和一个子模块(带点的名称,不确定这可以称为真正的子模块):

PyMODINIT_FUNC initsysipc(void) {
    PyObject *module = Py_InitModule3("sysipc", ...);
    ...
    init_sysipc_light();
}

static PyTypeObject FooType = { ... };
PyMODINIT_FUNC init_sysipc_light(void) {
    PyObject *module = Py_InitModule3("sysipc.light", ...);
    ...
    PyType_Ready(&FooType);
    PyModule_AddObject(module, "FooType", &FooType);
}
该模块编译为sysipc.so ,当我把它放在当前目录中时,下面的导入工作没有问题:
import sysipc
import sysipc.light
from sysipc.light import FooType
问题是我想把这个模块放在一个命名空间包中,文件夹结构是这样的:
company/
company/__init__.py
company/dept/
company/dept/__init__.py
company/dept/sys/
company/dept/sys/__init__.py
company/dept/sys/sysipc.so
所有三个__init__.py仅包含标准 setuptool进口线:
__path__ = __import__('pkgutil').extend_path(__path__, __name__)
在当前目录中,以下导入不起作用:
from company.dept.sys import sysipc;
from company.dept.sys.sysipc.light import FooType;
我应该如何导入模块 sysipc.light 中定义的类型和方法在这种情况下?
====================================
更新实际错误:
我有 sysipc.so构建,如果我在当前目录中运行 python 作为这个模块,导入将按预期工作:
[root@08649fea17ef 2]# python2
Python 2.7.18 (default, Jul 20 2020, 00:00:00)
[GCC 10.1.1 20200507 (Red Hat 10.1.1-1)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sysipc
>>> import sysipc.light
>>>
但是,如果我将其放入命名空间文件夹中,如下所示:
company/
company/__init__.py
company/dept
company/dept/__init__.py
company/dept/sys
company/dept/sys/sysipc.so
company/dept/sys/__init__.py
导入子模块将不起作用:
>>> from company.dept.sys import sysipc
>>> from company.dept.sys import sysipc.light
  File "<stdin>", line 1
    from company.dept.sys import sysipc.light
                                   ^
SyntaxError: invalid syntax
>>> from company.dept.sys.sysipc import light
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: cannot import name light
>>>
该模块是用 this simple code 构建的,它适用于python2。我也有same example for python3 .

最佳答案

引自 https://www.python.org/dev/peps/pep-0489/#multiple-modules-in-one-library :

To support multiple Python modules in one shared library, the library can export additional PyInit* symbols besides the one that corresponds to the library's filename.

Note that this mechanism can currently only be used to load extra modules, but not to find them. (This is a limitation of the loader mechanism, which this PEP does not try to modify.) ...


换句话说,您需要为importlib 重组项目如下能够找到子模块lightsysipc包裹:
company/__init__.py
company/dept/__init__.py
company/dept/sys/__init__.py
company/dept/sys/sysipc/__init__.py
company/dept/sys/sysipc/sysipc.so
company/dept/sys/sysipc/light.so -> sysipc.so  # hardlink
light.so 之间的硬链接(hard link)和 sysipc.so可以通过以下方式创建:
ln company/dept/sys/sysipc/sysipc.so company/dept/sys/sysipc/light.so
然后在 company/dept/sys/sysipc/__init__.py您从 sysipc.so 导入所有符号使用:
from .sysipc import *
此外,您需要将子模块 C 扩展初始化函数的名称从 init_sysipc_light 更改为至init_light对于 Python2,或来自 PyInit_sysipc_lightPyInit_light对于 Python3,因为 importlib通过查找导出的 PyInit_<module name> 来加载模块来自动态模块,这里的模块名只有light ,即父包前缀不是(子)模块名称的一部分。
这是扩展代码(Python3)和几个测试函数:
#include <Python.h>

PyObject *sysipc_light_foo(PyObject *self, PyObject *args) {
  printf("[*] sysipc.light.foo\n");
  return PyLong_FromLong(0);
}

static PyMethodDef sysipc_light_methods[] = {
    {"foo", (PyCFunction)sysipc_light_foo, METH_VARARGS, "sysipc.light.foo function"},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef sysipc_light_module = {
    PyModuleDef_HEAD_INIT,
    "sysipc.light",
    "sysipc child module",
    -1,
    sysipc_light_methods
};

PyMODINIT_FUNC PyInit_light(void)
{
    PyObject *module = NULL;

    module = PyModule_Create(&sysipc_light_module);

    return module;
}

PyObject *sysipc_bar(PyObject *self, PyObject *args) {
  printf("[*] sysipc.bar\n");
  return PyLong_FromLong(0);
}

static PyMethodDef sysipc_methods[] = {
    {"bar", (PyCFunction)sysipc_bar, METH_VARARGS, "sysipc.bar function"},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef sysipc_module = {
    PyModuleDef_HEAD_INIT,
    "sysipc",
    "sysipc parent module",
    -1,
    sysipc_methods
};

PyMODINIT_FUNC PyInit_sysipc(void)
{
    PyObject *module = NULL;

    module = PyModule_Create(&sysipc_module);

    PyInit_light();

    return module;
}
测试.py:
#!/usr/bin/env python3

from company.dept.sys import sysipc
from company.dept.sys.sysipc import light

sysipc.bar() 
light.foo()
输出:
[*] sysipc.bar
[*] sysipc.light.foo

关于如果放入命名空间包中,则子模块的python导入路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63978903/

相关文章:

python - 将字符串传递给模块 "once"

python - 在已安装的 Python 包中导入

python - 在 Python 中对日期列表进行排序

python - 从数组生成 Django 表单

python - 如何使用shutil让python文件在计算后进行 self 复制?

windows - python pygtk window 7 64 位

python - Python 中的 Hello World

python - Python装饰器的典型应用和用例

python - 我有两个 .py 文件。如何将一个程序的多行输出转换为另一个程序 GUI 的 tkinter 文本?

python - 从 mp4 python 下载 Youtube 音频