添加 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 很适合这个)。
(我自己想出来的)
这可以通过删除子模块存储库文件夹引入的额外间接来解决。您需要 RepoA
和 Repo
中当前为空的 __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 *
我在 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 很适合这个)。
(我自己想出来的)
这可以通过删除子模块存储库文件夹引入的额外间接来解决。您需要 RepoA
和 Repo
中当前为空的 __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 *