python 如果放在命名空间包中,子模块的导入路径
python import path for sub modules if put in namespace package
我有一个用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, it is for python2. I also have 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
能够在 sysipc
包中找到子模块 light
:
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
和 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 *
此外,对于Python2,您需要将子模块C扩展初始化函数的名称从init_sysipc_light
更改为init_light
,或者从PyInit_sysipc_light
更改为PyInit_light
for 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;
}
test.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
这里有两个问题:首先,Py_InitModule
和朋友希望创建被导入 的模块。提示是您传递给它的字符串不是模块的 fully-qualified 名称:Python 使用它已知的名称来确定 sys.modules
中放置新对象的位置。但是,您可以使用 完全限定的 名称; __file__
等其他魔法属性将具有正确的值。
第二个问题是 属性 light
需要在包含模块上设置 from
导入才能工作。
同时,没有理由拥有一个单独的初始化函数(解释器永远不会调用),将它们组合起来避免了以后恢复指向模块的指针的需要:
static PyTypeObject FooType = { ... };
PyMODINIT_FUNC initsysipc(void) {
PyObject *module = Py_InitModule3("sysipc", ...);
...
PyObject *const sub = Py_InitModule3("company.dept.sys.sysipc.light", ...);
...
PyType_Ready(&FooType);
// PyModule_AddObject steals a reference:
Py_INCREF(FooType);
PyModule_AddObject(sub, "FooType", &FooType);
Py_INCREF(sub);
PyModule_AddObject(module, "light", sub);
}
也就是说,sysipc
仍然不是一个合适的包:至少,它缺少 __path__
。如果这很重要,您可能更喜欢 使用真实的(如果更复杂的话)包架构。
我有一个用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, it is for python2. I also have 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
能够在 sysipc
包中找到子模块 light
:
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
和 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 *
此外,对于Python2,您需要将子模块C扩展初始化函数的名称从init_sysipc_light
更改为init_light
,或者从PyInit_sysipc_light
更改为PyInit_light
for 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;
}
test.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
这里有两个问题:首先,Py_InitModule
和朋友希望创建被导入 的模块。提示是您传递给它的字符串不是模块的 fully-qualified 名称:Python 使用它已知的名称来确定 sys.modules
中放置新对象的位置。但是,您可以使用 完全限定的 名称; __file__
等其他魔法属性将具有正确的值。
第二个问题是 属性 light
需要在包含模块上设置 from
导入才能工作。
同时,没有理由拥有一个单独的初始化函数(解释器永远不会调用),将它们组合起来避免了以后恢复指向模块的指针的需要:
static PyTypeObject FooType = { ... };
PyMODINIT_FUNC initsysipc(void) {
PyObject *module = Py_InitModule3("sysipc", ...);
...
PyObject *const sub = Py_InitModule3("company.dept.sys.sysipc.light", ...);
...
PyType_Ready(&FooType);
// PyModule_AddObject steals a reference:
Py_INCREF(FooType);
PyModule_AddObject(sub, "FooType", &FooType);
Py_INCREF(sub);
PyModule_AddObject(module, "light", sub);
}
也就是说,sysipc
仍然不是一个合适的包:至少,它缺少 __path__
。如果这很重要,您可能更喜欢