简体   繁体   English

如何透明地将绝对导入转换为相对内部外部模块?

[英]How to transparently convert absolute imports to relative inside external module?

I have a problem: I need to use some external code as part of my own project.我有一个问题:我需要使用一些外部代码作为我自己项目的一部分。 This seems to be a relatively common use case in machine learning research when trying to build new experiments on top of previously published solutions/models.当尝试在先前发布的解决方案/模型之上构建新实验时,这似乎是机器学习研究中相对常见的用例。

The file structure is as follows:文件结构如下:

project_root/
├── external/
│   └── SomeoneElsesCode/
│       └── src/
│           └── dir1/
│               └── subdir1/
│                   ├── codeineed.py
│                   └── anciliarycode.py
└── src/
    └── MyModule/
        └── mycode.py

When trying to run the line尝试运行线路时

from external.SomeoneElsesCode.src.dir1.subdir1.codeineed import NeededClass

in mycode.py I am running into the problem with linemycode.py我遇到了行的问题

from src.dir1.subdir1.anciliarycode import AncilliaryClass

in codeineed.py since the external code is using absolute import paths.codeineed.py中,因为外部代码使用绝对导入路径。 Since I do not control the code in SomeoneElsesCode I cannot simply adjust all import paths there.由于我不控制SomeoneElsesCode中的代码,因此我不能简单地调整那里的所有导入路径。 Is there any way to tell the Python interpreter to "relativize" all paths below SomeoneElsesCode ?有什么方法可以告诉 Python 解释器“相对化” SomeoneElsesCode下面的所有路径? If not, is there any recommended way of dealing with including external code in Python projects?如果没有,是否有任何推荐的方法来处理在 Python 项目中包含外部代码?

I believe that I have found an answer, though I hope that someone can suggest a better approach.我相信我已经找到了答案,尽管我希望有人可以提出更好的方法。

The solution to the problem seems to be to replace the builtin __import__() function with a wrapper preprocessing the path.该问题的解决方案似乎是将内置的__import__()函数替换为对路径进行预处理的包装器。 This can be done by placing the following __init__.py file inside the external directory:这可以通过将以下__init__.py文件放在external目录中来完成:

import builtins

default_import = builtins.__import__


class ExternalImport:
    def __init__(self, default_import) -> None:
        self.default_import = default_import

    def __call__(self, name, globals=None, locals=None, fromlist=(), level=0):
        if (
            ("src" in name)
            and (globals is not None)
            and (hasattr(globals, "__getitem__"))
            and (globals.get("__package__", None) is not None)
            and ("external" in globals.get("__package__", ""))
        ):
            package = globals["__package__"]
            externalLib = package.replace("external.", "").split(".")[0]
            name = f"external.{externalLib}.{name}"
        return self.default_import(name, globals, locals, fromlist, level)


builtins.__import__ = ExternalImport(default_import)

Essentially, I filter all import statements and look for those containing src and coming from the external module to adjust their names.本质上,我过滤所有导入语句并查找那些包含src并来自external模块的语句以调整它们的名称。 This is not ideal since it requires all external packages to store their code in the src directory;这并不理想,因为它要求所有外部包都将其代码存储在src目录中; however, I have not found any better way of detecting imports of actual packages (both built-in and installed).但是,我还没有找到更好的方法来检测实际包(内置和安装)的导入。

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

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