繁体   English   中英

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

[英]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

问题是我想把这个模块放在命名空间 package 中,文件夹结构是这样的:

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
>>>

该模块是用这个简单的代码构建的,适用于 python2。 我也有python3 的相同示例

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

为了在一个共享库中支持多个 Python 模块,除了与库的文件名对应的符号之外,该库还可以导出其他 PyInit* 符号。

注意这个机制目前只能用于加载额外的模块,不能用于查找 (这是加载器机制的限制,本 PEP 不尝试修改。)...

换句话说,您需要按如下方式重组项目,以便importlib能够在sysipc package 中找到子模块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.sosysipc.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 function 的名称从init_sysipc_light更改为init_light的 init_light,或从PyInit_sysipc_light更改为PyInit_light (对于 Python3),因为importlib通过从动态模块中查找导出的PyInit_<module name>来加载模块,并且这里的模块名称只是light ,即父 package 前缀不是(子)模块名称的一部分。

这是扩展代码 (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()

Output:

[*] sysipc.bar
[*] sysipc.light.foo

这里有两个问题:首先, Py_InitModule和朋友期望创建被导入的模块。 提示是您传递给它的字符串不是模块的完全限定名称:Python 使用它已知的名称来确定在sys.modules中放置新的 object 的位置。但是,您可以使用完全限定名称; __file__等其他魔法属性将具有正确的值。

第二个问题是属性light需要在包含模块上设置才能使from imports 起作用。

同时,没有理由单独初始化 function(解释器永远不会调用),并且将它们组合起来避免了以后恢复指向模块的指针的需要:

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仍然不是正确的 package:至少,它缺少__path__ 如果这很重要,您可能更喜欢MEE 的答案,它使用真实的(如果更复杂的话)package 架构。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM