相对导入在Python 3中出现问题

4
首先,我需要描述我写作的环境。我正在编写Python代码,该代码将由在CAD应用程序内运行的Python运行时加载和执行。CAD应用程序使用Python作为其脚本引擎。因此,我无法访问Python运行时,并且作为对所有其他脚本的良好公民,不应修改任何系统设置。我的脚本只是加载和运行的众多脚本之一。
这一切都很好,除非我想要使用非标准库。在这种情况下,我需要安装库的本地副本,以便我的脚本可以访问它。我遇到的问题是,大多数库希望被安装并添加到sys路径中,而我不应这样做,因为这可能会与其他脚本所做的操作产生冲突。相反,我尝试设置库的本地副本,然后编辑其源代码,使其导入是相对的,而不是依赖于sys路径。这样,我的程序将拥有自己的本地库副本,不依赖于其他内容,并且不会干扰其他脚本。
我使用PIP的-t选项将Requests和PyOpenSSL安装到我的脚本文件夹的“Packages”子文件夹中。以下是我拥有的内容的简略列表。
RequestsTest/
    RequestsTest.py 
    Packages/
        OpenSSL/
            cryptography/
                x509/
                    __init__.py
                    base.py
                hazmat/
                    __init__.py
                    backends/
                        __init__.py
                        interfaces.py
                        openssl/
                            __init__.py
                            backend.py
                            x509.py
            OpenSSL/
                __init__.py
                SSL.py
        Requests/
            chardet/
                __init__.py
            requests/
                __init__.py
            urllib3/
                __init__.py
                request.py
                contrib/
                    __init__.py
                    pyopenssl.py
                util/
                    __init__.py
                    request.py
                    ssl_.py

虽然追踪各种导入语句并将它们变成相对路径很繁琐,但似乎确实有效。然而,我在处理一个特定的导入集时遇到了问题。

在Packages/Requests/urllib3/contrib/pyopenssl.py中,它包含以下导入语句,我已经进行了修改:

from ....OpenSSL.OpenSSL import SSL 
from ....OpenSSL.cryptography import x509

他们最初是这样的:

from OpenSSL import OpenSSL.SSL
from cryptography import x509

我在第一行代码处遇到了"ImportError: No module named 'OpenSSL'"的错误,第二行代码处遇到了"ImportError: No module named 'cryptography'"的错误。我相当确定路径是正确的,因为如果我改变点的数量,我会得到没有模块命名错误,但它会列出正在尝试加载的内容的完整路径,而不仅仅是模块的名称。
我需要针对这个具体问题的帮助,也可以使用一些关于如何设置和使用私有库的整体建议。请记住,我的程序只是系统正在加载的众多程序之一,更改系统或设置虚拟环境都不是选项。

2
这听起来像是一场绝对的噩梦...别人怎么能够维护这个? - gold_cy
我不认同“建立虚拟环境不可行”的说法。这恰恰是你应该在这里做的事情,如果你可以访问用户空间,那么这就是一个选择。 - wim
这是什么CAD应用程序? - FabienP
@amanb,感谢您的建议。我的问题是,在我的情况下,我没有自己的环境,而是必须在Fusion 360 Python环境中与其他人“玩”。因此,最好让我的程序完全自包含,除了使用系统库以外,这样我就不会为其他人搞乱环境。 - Brian Ekins
@FabienP,CAD应用程序是Fusion 360。它正在运行自己的Python解释器,并在该解释器中加载和运行Python程序。我无法控制Python运行时或加载我的程序。使用Python虚拟环境,它不是有自己的私有Python解释器吗?我没有这个奢侈条件。 - Brian Ekins
显示剩余2条评论
1个回答

3

请查看localimport模块,它似乎是您特定用例的解决方案。从README中可以了解到:

Given your Python script, application or plugin comes with a directory that contains modules for import, you can use localimport to keep the global importer state clean.

app.py
res/modules/
  some_package/
    __init__.py


# app.py
with localimport('res/modules') as _importer:
    import some_package
    assert 'some_package' not in sys.modules
标语是“用于嵌入式应用程序的Python模块隔离导入”,因此它似乎非常相关。
使用该模块时,以下内容可能有助于使事情保持整洁:
1. 将实际脚本逻辑放入自己的文件中。 2. 有一个包装脚本(这将是CAD软件加载的脚本),其中执行README中提到的localimport,然后进行相对导入您的模块。如果您的模块足够大,可以将其放入自己的软件包中,并以与其他所有内容相同的方式使用它(只需在with localimport(): …的主体中执行from RequestsTest import *)。 3. 尽量在您编写的源代码与在CAD Python运行时上下文中使用该源代码所需的最终组织好的文件之间建立清晰的边界。有一个构建/打包步骤来创建localimport脚本、下载所需的软件包等是可以的。甚至更好,因为它是自动化的,而不是手动完成的,未来的某个人可能需要重新创建。

这似乎是有效的,尽管它违背了我最初不更改系统任何内容的要求。只是在这种情况下,系统路径在完成后被管理和清理。 - Brian Ekins

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接