Bob*_*bby 26 python setuptools pypi python-3.x namespace-package
命名空间包的主题似乎对于初学者来说有点混乱,并且它的先前版本的Python以几种不同的方式实现它或者StackOverflow上的许多问答都是过时的并没有帮助.我正在寻找Python 3.5或以后的解决方案.
我正在将一堆Python代码重构为模块和子模块,并且努力使这些项目中的每一个都设置为在坐在同一名称空间中时彼此独立地运行.
我们最终将使用内部PyPi服务器,将这些软件包提供给我们的内部网络,并且不希望将它们与外部(公共)PyPi软件包混淆.
示例: 我有2个模块,我希望能够执行以下操作:
from org.client.client1 import mod1
from org.common import config
Run Code Online (Sandbox Code Playgroud)
反映的模块将分开:
存储库1:
org_client_client1_mod1/
setup.py
mod1/
__init__.py
somefile.py
Run Code Online (Sandbox Code Playgroud)
存储库2:
org_common_config/
setup.py
config/
__init__.py
someotherfile.py
Run Code Online (Sandbox Code Playgroud)
我的Git仓库已经设置为org_client_client1_mod1和org_common_config,所以我只需要在包装和执行安装__init__.py文件,我相信.
随着
__init__.py,我应该使用哪些(如果有的话)?:Run Code Online (Sandbox Code Playgroud)from pkgutil import extend_path __path__ = extend_path(__path__, __name__)要么:
Run Code Online (Sandbox Code Playgroud)import pkg_resources pkg_resources.declare_namespace(__name__)
有
setup.py,我还需要添加namespace_modules参数,如果是,我会使用namespace_modules=['org.common'],还是namespace_modules=['org', 'common']?
我可以通过以某种方式实现这一点而放弃以上所有内容吗?也许是更简单或更"pythonic"的东西?
这是一个很难的话题。到处都是-'s、_'s 和__init__.py's 并不能让我们感到轻松。
首先我来回答一下大家的问题:
对于
__init__.py,我应该使用其中哪一个(如果有)?
__init__.py可以完全为空,只需位于正确的位置即可。也就是说(双关语)它们应该位于任何包含 python 代码的子包中(不包括setup.py。)遵循这些规则,你应该没问题。使用 setup.py,我是否还需要添加
namespace_modules参数,如果需要,我会使用namespace_modules=['org.common'], 或namespace_modules=['org', 'common']?
我可以通过以不同方式实现这一点来放弃上述所有内容吗?也许是更简单或更“Pythonic”的东西?
我将用这个答案的其余部分以本机格式重新实现您的命名空间包:
我将把我能找到的所有有用的文档放在帖子的底部。
K 所以我假设您需要本机名称空间包。首先让我们看看您的 2 个存储库的当前结构:
org_client_client1_mod1/
setup.py
mod1/
__init__.py
somefile.py
Run Code Online (Sandbox Code Playgroud)
&
org_common_config/
setup.py
config/
__init__.py
someotherfile.py
Run Code Online (Sandbox Code Playgroud)
这^太容易了!!!
我的大脑没有足够的弹性来知道我们是否可以深入使用命名空间包 3 层,但是为了做你想做的事,我很确定你会想做以下事情:
org-client/
setup.py
org/
client/
client1/
__init__.py
mod1/
__init__.py
somefile.py
Run Code Online (Sandbox Code Playgroud)
&
org-common-but-also-note-this-name-doesnt-matter/
setup.py
org/
common/
__init__.py
config/
__init__.py
someotherfile.py
Run Code Online (Sandbox Code Playgroud)
基本上,关键是在每个 .args 内部指定正确的name=& packages=args 。stuptools.setup()setup.py
这些将是:
name='org_client',
...
packages=['org.client']
Run Code Online (Sandbox Code Playgroud)
&
name='org_common'
...
packages['org.common']
Run Code Online (Sandbox Code Playgroud)
分别。
pip install .然后只需在每个顶级目录中安装每个即可。
安装第一个将使您能够访问该somefile.py模块,安装第二个将使您能够访问someotherfile.py. 它也不会因为您尝试安装org在同一环境中命名的 2 个软件包而感到困惑。
K 所以文档中最有用的部分: https://packaging.python.org/guides/packaging-namespace-packages/#packaging-namespace-packages
晚会晚了,但是从不帮助同行旅行者沿着Python的命名空间走下去!
使用init .py,我应该使用哪个(如果有)?:
它取决于,有三种方法可以做到的命名空间的包作为上市在这里:
使用本地名称空间包。这种类型的名称空间包在PEP 420中定义,并且在Python 3.3和更高版本中可用。如果命名空间中的软件包仅需要支持Python 3和通过pip安装,则建议使用此方法。
使用pkgutil样式的名称空间包。对于需要支持Python 2和3并通过pip和python setup.py install进行安装的新软件包,建议使用此选项。
使用pkg_resources样式的名称空间包。如果您需要与已经使用此方法的软件包兼容,或者您的软件包需要使用zip安全,则建议使用此方法。
如果您使用的是#2(pkgutil-style)或#3(pkg_resources-style),则必须对__init__.py文件使用相应的样式。如果使用本机名称空间,则__init__.py名称空间目录中的否。
使用setup.py,我是否仍需要添加namespace_modules参数,如果是,我将使用namespace_modules = ['org.common']还是namespace_modules = ['org','common']?
如果您选择的名称空间包不是本机样式,则可以,您需要namespace_packages在中setup()。
我可以通过以不同的方式实现上述方法来放弃上述所有方法吗?也许更简单或更“ pythonic”的东西?
由于您最终遇到了python中的comple主题,因此您似乎知道自己在做什么,想要什么,并确定了创建Python命名空间包的方法。这将被认为是解决问题的一种Python方法。
除了发现您的问题之外,还有一些我发现的内容:
我阅读了《Python打包指南》PEP420,并花了很多时间来了解命名空间包,而且我通常也了解它是如何工作的。我在这里,这里,这里以及StackOverflow上的这个答案都读了几个答案。此处和Rob共享的Git 链接上的示例。
但是,我的问题是在创建程序包之后。由于所有说明和示例代码都在setuptools.setup(package=[])函数中明确列出了该软件包,因此我的代码失败了。我的子软件包/目录未包括在内。深入研究,我发现setuptools也具有find_namespace_package()有助于添加子软件包的功能
编辑:
链接到find_namespace_packages()(setuptools版本大于40.1.0):https : //setuptools.readthedocs.io/en/latest/setuptools.html#find-namespace-packages
编辑(08/09/2019):
为了完成答案,让我也通过示例进行重组。
以下解决方案假设Python 3.3+支持隐式名称空间包
由于您正在寻找适用于Python版本3.5或更高版本的解决方案,因此让我们采用提供的代码示例并进一步进行详细说明。
让我们假设以下内容:
命名空间/ Python包名称: org
分发包:org_client,org_common
蟒蛇: 3.3+
setuptools: 40.1.0
为您执行以下操作
from org.client.client1 import mod1
from org.common import config
Run Code Online (Sandbox Code Playgroud)
并保持顶层目录相同,即。org_client_client1_mod1和org_common_config,您可以将结构更改为以下内容
仓库1:
org_client_client1_mod1/
setup.py
org/
client/
client1/
__init__.py
submod1/
__init__.py
mod1/
__init__.py
somefile.py
file1.py
Run Code Online (Sandbox Code Playgroud)
更新 setup.py
from setuptools import find_namespace_packages, setup
setup(
name="org_client",
...
packages=find_namespace_packages(), # Follows similar lookup as find_packages()
...
)
Run Code Online (Sandbox Code Playgroud)
仓库2:
org_common_config/
setup.py
org/
common/
__init__.py
config/
__init__.py
someotherfile.py
Run Code Online (Sandbox Code Playgroud)
更新时间setup.py:
from setuptools import find_namespace_packages, setup
setup(
name="org_common",
...
packages=find_namespace_packages(), # Follows similar lookup as find_packages()
...
)
Run Code Online (Sandbox Code Playgroud)
要安装(使用pip):
(venv) $ pip3 install org_common_config/
(venv) $ pip3 install org_client_client1_mod1/
Run Code Online (Sandbox Code Playgroud)
更新的点列表将显示以下内容:
(venv) $ pip3 list
...
org_client
org_common
...
Run Code Online (Sandbox Code Playgroud)
但是它们将不可导入,因为导入时必须遵循org.client并org.common注明。
要了解原因,您可以在此处浏览(假设在venv中):
(venv) $ cd venv/lib/python3.5/site-packages/
(venv) $ ls -l | grep org
Run Code Online (Sandbox Code Playgroud)
您会看到没有目录org_client或org_common目录,它们被解释为名称空间包。
(venv) $ cd venv/lib/python3.5/site-packages/org/
(venv) $ ls -l
client/
common/
...
Run Code Online (Sandbox Code Playgroud)