首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在Python中导入模块中隐式使用命名空间

在Python中导入模块中隐式使用命名空间
EN

Stack Overflow用户
提问于 2022-10-12 08:55:36
回答 1查看 50关注 0票数 0

我试图用我不拥有的Python项目创建一个库。该项目具有以下目录布局:

代码语言:javascript
运行
复制
.
├── MANIFEST.in
├── pyproject.toml
└── src
    ├── all.py
    ├── the.py
    └── sources.py

pyproject.toml中,我有:

代码语言:javascript
运行
复制
[tool.setuptools]
packages = ["mypkg"]

[tool.setuptools.package-dir]
mypkg = "src"

我面临的问题是,当我构建和安装这个包时,我不能使用它,因为作者在各种源文件中导入没有mypkg前缀的东西。

F.ex。在all.py

代码语言:javascript
运行
复制
from the import SomeThing

因为我不拥有这个包,所以我不能修改所有的源代码,但是我仍然希望能够通过添加MANIFEST.inpyproject.toml来构建一个库。

是否可能以某种方式指示setuptools构建一个包,它不会丢弃所有的源,同时仍然允许在没有site-packages前缀的情况下导入它们?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-10-14 04:04:07

在包中添加自定义导入挂钩是不可能的。钩子采用随包附带的模块的形式,必须在从模块使用之前导入它(例如,在src/all.py中)

src/mypkgimp.py

代码语言:javascript
运行
复制
import sys
import importlib  

class MyPkgLoader(importlib.abc.Loader):
    def find_spec(self, name, path=None, target=None):
        # update the list with modules that should be treated special
        if name in ['sources', 'the']:
            return importlib.util.spec_from_loader(name, self)
        return None

    def create_module(self, spec):
        # Uncomment if "normal" imports should have precedence
        # try:
        #     sys.meta_path = [x for x in sys.meta_path[:] if x is not self]
        #     return importlib.import_module(spec.name)
        # except ImportError:
        #     pass
        # finally:
        #     sys.meta_path = [self] + sys.meta_path

        # Otherwise, this will unconditionally shadow normal imports
        module = importlib.import_module('.' + spec.name, 'mypkg')
        # Final step: inject the module to the "shortened" name
        sys.modules[spec.name] = module
        return module

    def exec_module(self, module):
        pass

if not hasattr(sys, 'frozen'):
    sys.meta_path = [MyPkgLoader()] + sys.meta_path

是的,上面使用了我之前链接过的thread描述的不同方法,因为importlib在Python3.10中反对使用这些方法,有关详细信息,请参阅文档。

无论如何,在演示中,在模块中放置一些虚拟类:

src/the.py

代码语言:javascript
运行
复制
class SomeThing: ...

src/sources.py

代码语言:javascript
运行
复制
class Source: ...

现在,修改src/all.py,使其具有以下内容:

代码语言:javascript
运行
复制
import mypkg.mypkgimp
from the import SomeThing

示例用法:

代码语言:javascript
运行
复制
>>> from sources import Source
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'sources'
>>> from mypkg import all
>>> all.SomeThing
<class 'mypkg.the.SomeThing'>
>>> from sources import Source
>>> Source
<class 'mypkg.sources.Source'>
>>> from sources import Error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: cannot import name 'Error' from 'mypkg.sources' (/tmp/mypkg/src/sources.py)

注意导入最初是如何不工作的,但是在导入mypkg.all之后,sources导入现在是全局工作的。因此,可能需要注意不要隐藏“真实”导入,并且我已经提供了使用“默认”*导入机制导入的示例。

如果您希望模块名称看起来不同(即没有mypkg.前缀),这将是一个单独的问题,因为代码通常不检查它们自己的模块名称是否具有功能(而且没关系,这实际上显示了名称空间是如何隐式使用的--更改实际名称更类似于模块重新定位,这是可以做到的,但更复杂一些,这个答案足够长)。

*“默认”,如不包括此自定义导入钩子引入的行为-其他导入钩子可能会做自己的其他诡计。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74039092

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档