添加 python 个相互依赖的模块作为 git 个子模块

add python modules that depend on each other as git submodules

我在 2 个单独的 git 存储库中有这 2 个小模块:

RepoA/A.py

def foo():
    return "Hello World"

RepoB/B.py

import sys
try:
    import A
except ImportError:
    print("Dependency not available!")
    sys.exit(1)

def bar():
    return A.foo() + " EXTENDED!"

您看到 B 当前假设 A 是全局可导入的(已安装或在脚本执行目录中)。

它们的根目录都有一个空的 __init__py 以使它们可以作为子模块导入。

现在我有另一个更大的存储库 C 需要 B。它有 A 和 B 作为 git 子模块可用,所以结构实际上看起来像这样:

RepoC
|---utils/
|   |---__init__.py
|   |---RepoA/
|   |   |---__init__.py
|   |   |---A.py
|   |---RepoB/
|       |---__init__.py
|       |---B.py
|---C.py

RepoC/C.py

import utils.B

print(B.bar())

RepoC/utils/__init__.py

# remove the 1-layer indirection through the submodule
from .RepoB import B

现在打印 Dependency not available!,正如预期的那样,因为 A 不是全局可用的,而是位于 B 永远无法猜测的位置(在这种情况下,它需要做from ..RepoA import A)。如果我通过将 A 添加到 sys.path 使其再次全局可用,它将起作用:

RepoC/utils/__init__.py

import os, sys
import inspect

def fake_install(module_path):
    # the submitted module path is relative to the caller's location
    # therefore get that script's file location first
    caller_module = inspect.getmodule(inspect.stack()[1][0])
    caller_dir = caller_module.__file__.rsplit(os.path.sep, 1)[0]
    # build an absolute file path and append it to the system paths
    path = os.path.join(os.path.abspath(caller_dir), *module_path.split("."))
    sys.path.append(path)

fake_install("RepoA")

# remove the 1-layer indirection through the submodule
from .RepoB import B

虽然这感觉像是一个可怕的解决方案。

另一个想法是根本不使用 git 子模块,而是将依赖项收集为 git-链接 requirements.txt,编写一些 setup.exe 脚本并让 pip actually 安装它们。

如何优雅地克服这个问题?是否有任何导入技巧可以让我做到这一点?

您可能已经猜到了,我认为您有两个选择:要么让 A 和 B 成为 C 的一部分,要么让 B 和 C 独立打包。

是pip到"put A somewhere in sys.path"的工作,所以你还不如让他来做,而不是自己做。您可以在需求中使用 git 链接但不能在 setup.py 中使用一个私人 PyPI 服务器(devpi 很适合这个)。

(我自己想出来的)
这可以通过删除子模块存储库文件夹引入的额外间接来解决。您需要 RepoARepo 中当前为空的 __init__.py 文件作为它们的包含模块(这里是 utils)以赋予 RepoC 建立依赖项的能力在那里可用。

将您的 RepoB/__init__.py 编辑为如下所示:

from .. import *

然后通过将此添加到实用程序的 __init__.py:

中,使您的依赖项 A 在您的实用程序中可用
from .RepoA import A

现在您需要做的就是在 B.py 本地导入:

from . import A

您也可以将其设置为全局依赖项:

try:
    from . import A
except ImportError:
    import A

现在所有图书馆的用户必须做的是:

  • 全局安装依赖,或者
  • 使依赖项在包含模块中可用

为了使这种方法更通用,我会让子模块根目录中的所有 __init__.py 像这样充当桥梁:

RepoA/__init__.py

from .. import *
__all__ = ["A"]

RepoB/__init__.py

from .. import *
__all__ = ["B"]

并执行相同的操作,即删除间接寻址 "from the other side"。在这种情况下 utils/__init__.py:

from .RepoA import *
from .RepoB import *