繁体   English   中英

Python3 中的 Python 命名空间包

[英]Python Namespace Packages in Python3

命名空间包的主题对于初学者来说似乎有点令人困惑,而且 Python 的先前版本以几种不同的方式实现它或者 StackOverflow 上的许多问答都过时了,这无济于事。 我正在寻找Python 3.5或更高版本的解决方案。

场景:

我正在将一堆 Python 代码重构为模块和子模块,并努力使这些项目中的每一个都设置为在位于同一命名空间中时彼此独立运行。

我们最终将使用内部 PyPi 服务器,将这些包提供给我们的内部网络,并且不想将它们与外部(公共)PyPi 包混淆。

示例:我有 2 个模块,我希望能够执行以下操作:

from org.client.client1 import mod1
from org.common import config

反射的模块将这样分开:

存储库 1:

org_client_client1_mod1/
  setup.py
  mod1/
    __init__.py
    somefile.py

存储库 2:

org_common_config/
  setup.py
  config/
    __init__.py
    someotherfile.py

我的 Git 存储库已经设置为org_client_client1_mod1org_common_config ,所以我只需要在打包和__init__.py文件上执行设置,我相信。

问题:

#1

使用__init__.py ,我应该使用哪些(如果有)?:

 from pkgutil import extend_path __path__ = extend_path(__path__, __name__)

或者:

 import pkg_resources pkg_resources.declare_namespace(__name__)

#2

使用setup.py ,我是否还需要添加namespace_modules参数,如果是,我会使用namespace_modules=['org.common']还是namespace_modules=['org', 'common']

#3

我可以通过以某种方式以不同方式实现这一点而放弃上述所有内容吗? 也许更简单或更“pythonic”?

聚会迟到了,但在 Python 中帮助其他旅行者沿着命名空间路径走从来没有坏处!

#1:

使用__init__.py ,我应该使用哪些(如果有)?:

它取决于,有三种方法可以做到的命名空间的包作为上市在这里

  1. 使用本机命名空间包。 这种类型的命名空间包在 PEP 420 中定义,并且在 Python 3.3 及更高版本中可用。 如果您的命名空间中的包只需要支持 Python 3 并通过 pip 安装,则建议使用此方法。

  2. 使用 pkgutil 风格的命名空间包。 对于需要支持 Python 2 和 3 并通过 pip 和 python setup.py install 安装的新软件包,建议使用此方法。

  3. 使用 pkg_resources 风格的命名空间包。 如果您需要与已经使用此方法的软件包兼容,或者您​​的软件包需要是 zip 安全的,则建议使用此方法。

如果您使用 #2 ( pkgutil-style ) 或 #3 ( pkg_resources-style ),那么您将必须对__init__.py文件使用相应的样式。 如果您使用本机命名空间,则命名空间目录中没有__init__.py

#2:

使用setup.py,我是否还需要添加namespace_modules 参数,如果是,我是使用namespace_modules=['org.common'] 还是namespace_modules=['org', 'common']?

如果您选择的命名空间包不是本机风格,那么是的,您将需要在setup() namespace_packages

#3:

我可以通过以某种方式以不同方式实现这一点而放弃上述所有内容吗? 也许更简单或更“pythonic”?

由于您最终在 Python 中遇到了一个复杂的主题,因此您似乎知道自己在做什么,想要什么,并确定创建 Python 命名空间包是实现此目的的方法。 这将被认为是解决问题的 Pythonic 方式。


除了您的问题之外,我还发现了以下几点:

我阅读了PEP420Python Packaging guide并花了很多时间来理解命名空间包,并且我大致了解它是如何工作的。 在这里这里这里和 SO 上的这个线程阅读了几个答案 - 这里的示例和 Rob 共享的 Git 链接

然而,我的问题是在我创建了我的包之后。 由于所有说明和示例代码都在setuptools.setup(package=[])函数中明确列出了包,我的代码失败了。 我的子包/目录不包括在内。 深入挖掘,我发现 setuptools 有一个find_namespace_package()函数,它也有助于添加子包

编辑

链接到find_namespace_packages()setuptools版本大于40.1.0 ): https : 40.1.0

编辑(08/09/2019):

为了完成答案,让我也重组一个例子。

以下解决方案假设 Python 3.3+ 支持隐式命名空间包

由于您正在寻找适用于 Python 3.5或更高版本的解决方案,让我们使用提供的代码示例并进一步详细说明。

让我们假设以下情况:

命名空间/Python 包名称: org

分发包: org_client , org_common

蟒蛇: 3.3+

设置工具: 40.1.0

为您执行以下操作

from org.client.client1 import mod1
from org.common import config

并保持您的顶级目录相同,即。 org_client_client1_mod1org_common_config ,您可以将结构更改为以下内容

存储库 1:

org_client_client1_mod1/
  setup.py
  org/
    client/
      client1/
        __init__.py
        submod1/
          __init__.py
        mod1/
          __init__.py
          somefile.py
        file1.py

更新了setup.py

from setuptools import find_namespace_packages, setup
setup(
    name="org_client",
    ...
    packages=find_namespace_packages(), # Follows similar lookup as find_packages()
    ...
)

存储库 2:

org_common_config/
  setup.py
  org/
    common/
      __init__.py
      config/
        __init__.py
        someotherfile.py

更新setup.py

from setuptools import find_namespace_packages, setup
setup(
    name="org_common",
    ...
    packages=find_namespace_packages(), # Follows similar lookup as find_packages()
    ...
)

安装(使用pip ):

(venv) $ pip3 install org_common_config/
(venv) $ pip3 install org_client_client1_mod1/

更新的 pip 列表将显示以下内容:

(venv) $ pip3 list
...
org_client
org_common
...

但它们不可导入,导入时您必须遵循org.clientorg.common符号。

要了解原因,您可以浏览此处(假设在 venv 中):

(venv) $ cd venv/lib/python3.5/site-packages/
(venv) $ ls -l | grep org

您将看到没有org_clientorg_common目录,它们被解释为命名空间包。

(venv) $ cd venv/lib/python3.5/site-packages/org/
(venv) $ ls -l
client/
common/
...

这是一个棘手的话题。 所有- 's, _ 's 和__init__.py 's 无处不在并不能完全让我们轻松。

首先,我来回答你的问题:

对于__init__.py ,我应该使用哪些(如果有)?

  • __init__.py可以完全为空,它只需要在正确的位置。 即(双关语)它们应该在任何包含 python 代码的子包中(不包括setup.py 。)遵循这些规则,你应该没问题。

使用 setup.py,我是否还需要添加namespace_modules参数,如果是,我会使用namespace_modules=['org.common']还是namespace_modules=['org', 'common']

  • 不! 只有name=packages= 但是,请注意与目录结构相比的packages= arg 格式。
  • 这是package= arg 的格式: 命名空间包的 setup.py
  • 对应的目录结构如下: example_package/a/<子包>

我可以通过以某种方式以不同方式实现这一点而放弃上述所有内容吗? 也许更简单或更“pythonic”?

  • 如果您希望能够单独安装多个功能,但在同一个顶级命名空间下,那么您就走在正确的轨道上。

我将在本答案的其余部分以本机格式重新实现您的命名空间包:

我会把我能找到的所有有用的文档放在帖子的底部。

K 所以我假设你想要本机命名空间包。 首先让我们看一下您的 2 个存储库的当前结构:

org_client_client1_mod1/
  setup.py
  mod1/
    __init__.py
    somefile.py

&

org_common_config/
  setup.py
  config/
    __init__.py
    someotherfile.py

这^太简单了!!!

得到你想要的:

我的大脑不够灵活,无法知道我们是否可以使用命名空间包深入 3 层,但是要执行您想要的操作,我很确定您会执行以下操作:

org-client/
  setup.py
  org/
    client/
      client1/
        __init__.py
        mod1/
          __init__.py
          somefile.py

&

org-common-but-also-note-this-name-doesnt-matter/
  setup.py
  org/
    common/
      __init__.py
      config/
        __init__.py
        someotherfile.py

基本上,关键是在每个setup.pystuptools.setup()指定正确的name= & packages= args。

这些将是:

name='org_client',
...
packages=['org.client']

&

name='org_common'
...
packages['org.common']

分别。

然后只需使用pip install .安装每一个pip install . 在每个顶级目录中。

安装第一个会让你访问somefile.py模块,安装第二个会让你访问someotherfile.py 它也不会因为您尝试在同一环境中安装 2 个名为org包而感到困惑。

K 所以文档中最有用的部分: https : //packaging.python.org/guides/packaging-namespace-packages/#packaging-namespace-packages

然后这就是我实际上是如何理解这一点的: https : //github.com/pypa/sample-namespace-packages/tree/master/native

暂无
暂无

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

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