简体   繁体   English

Python3 中的 Python 命名空间包

[英]Python Namespace Packages in Python3

The topic of namespace packages seems a bit confusing for the uninitiated, and it doesn't help that prior versions of Python have implemented it in a few different ways or that a lot of the Q&A on StackOverflow are dated.命名空间包的主题对于初学者来说似乎有点令人困惑,而且 Python 的先前版本以几种不同的方式实现它或者 StackOverflow 上的许多问答都过时了,这无济于事。 I am looking for a solution in Python 3.5 or later .我正在寻找Python 3.5或更高版本的解决方案。

The scenario:场景:

I'm in the process of refactoring a bunch of Python code into modules and submodules, and working to get each of these projects set up to operate independently of each other while sitting in the same namespace.我正在将一堆 Python 代码重构为模块和子模块,并努力使这些项目中的每一个都设置为在位于同一命名空间中时彼此独立运行。

We're eventually going to be using an internal PyPi server, serving these packages to our internal network and don't want to confuse them with external (public) PyPi packages.我们最终将使用内部 PyPi 服务器,将这些包提供给我们的内部网络,并且不想将它们与外部(公共)PyPi 包混淆。

Example: I have 2 modules, and I would like to be able to perform the following:示例:我有 2 个模块,我希望能够执行以下操作:

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

The reflected modules would be separate as such:反射的模块将这样分开:

Repository 1:存储库 1:

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

Repository 2:存储库 2:

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

My Git repositories are already setup as org_client_client1_mod1 and org_common_config , so I just need to perform the setup on the packaging and __init__.py files, I believe.我的 Git 存储库已经设置为org_client_client1_mod1org_common_config ,所以我只需要在打包和__init__.py文件上执行设置,我相信。

Questions:问题:

#1 #1

With the __init__.py , which of these should I be using (if any)?:使用__init__.py ,我应该使用哪些(如果有)?:

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

Or:或者:

 import pkg_resources pkg_resources.declare_namespace(__name__)

#2 #2

With setup.py , do I still need to add the namespace_modules parameter, and if so, would I use namespace_modules=['org.common'] , or namespace_modules=['org', 'common'] ?使用setup.py ,我是否还需要添加namespace_modules参数,如果是,我会使用namespace_modules=['org.common']还是namespace_modules=['org', 'common']

#3 #3

Could I forgo all of the above by just implementing this differently somehow?我可以通过以某种方式以不同方式实现这一点而放弃上述所有内容吗? Perhaps something simpler or more "pythonic"?也许更简单或更“pythonic”?

Late to the party, but never hurts to help fellow travellers down the namespace path in Python!聚会迟到了,但在 Python 中帮助其他旅行者沿着命名空间路径走从来没有坏处!

#1: #1:

With the __init__.py , which of these should I be using (if any)?:使用__init__.py ,我应该使用哪些(如果有)?:

It depends, There are three ways to do namespace packages as listed here :它取决于,有三种方法可以做到的命名空间的包作为上市在这里

  1. Use native namespace packages.使用本机命名空间包。 This type of namespace package is defined in PEP 420 and is available in Python 3.3 and later.这种类型的命名空间包在 PEP 420 中定义,并且在 Python 3.3 及更高版本中可用。 This is recommended if packages in your namespace only ever need to support Python 3 and installation via pip.如果您的命名空间中的包只需要支持 Python 3 并通过 pip 安装,则建议使用此方法。

  2. Use pkgutil-style namespace packages.使用 pkgutil 风格的命名空间包。 This is recommended for new packages that need to support Python 2 and 3 and installation via both pip and python setup.py install.对于需要支持 Python 2 和 3 并通过 pip 和 python setup.py install 安装的新软件包,建议使用此方法。

  3. Use pkg_resources-style namespace packages.使用 pkg_resources 风格的命名空间包。 This method is recommended if you need compatibility with packages already using this method or if your package needs to be zip-safe.如果您需要与已经使用此方法的软件包兼容,或者您​​的软件包需要是 zip 安全的,则建议使用此方法。

If you are using #2 ( pkgutil-style ) or #3 ( pkg_resources-style ), then you will have to use the corresponding style for __init__.py files.如果您使用 #2 ( pkgutil-style ) 或 #3 ( pkg_resources-style ),那么您将必须对__init__.py文件使用相应的样式。 If you use native namespaces then no __init__.py in the namespace directory.如果您使用本机命名空间,则命名空间目录中没有__init__.py

#2: #2:

With setup.py, do I still need to add the namespace_modules parameter, and if so, would I use namespace_modules=['org.common'], or namespace_modules=['org', 'common']?使用setup.py,我是否还需要添加namespace_modules 参数,如果是,我是使用namespace_modules=['org.common'] 还是namespace_modules=['org', 'common']?

If your choice of namespace package is not native style, then yes, you will need namespace_packages in your setup() .如果您选择的命名空间包不是本机风格,那么是的,您将需要在setup() namespace_packages

#3: #3:

Could I forgo all of the above by just implementing this differently somehow?我可以通过以某种方式以不同方式实现这一点而放弃上述所有内容吗? Perhaps something simpler or more "pythonic"?也许更简单或更“pythonic”?

Since you ended up down to a complex topic in python, it seems you know what you are doing, what you want and identified that creating a Python Namespace package is the way to do it.由于您最终在 Python 中遇到了一个复杂的主题,因此您似乎知道自己在做什么,想要什么,并确定创建 Python 命名空间包是实现此目的的方法。 This would be considered a pythonic way to solve a problem.这将被认为是解决问题的 Pythonic 方式。


Adding to your questions, here are a few things I discovered:除了您的问题之外,我还发现了以下几点:

I read PEP420 , the Python Packaging guide and spent a lot of time understanding the namespace packages, and I generally understood how it worked.我阅读了PEP420Python Packaging guide并花了很多时间来理解命名空间包,并且我大致了解它是如何工作的。 I read through a couple of answers here , here , here , and this thread on SO as well - the example here and on the Git link shared by Rob.在这里这里这里和 SO 上的这个线程阅读了几个答案 - 这里的示例和 Rob 共享的 Git 链接

My problem however was after I created my package.然而,我的问题是在我创建了我的包之后。 As all the instructions and sample code explicitly listed the package in the setuptools.setup(package=[]) function, my code failed.由于所有说明和示例代码都在setuptools.setup(package=[])函数中明确列出了包,我的代码失败了。 My sub-packages/directories were not included.我的子包/目录不包括在内。 Digging deeper, I found out that setuptools has a find_namespace_package() function that helps in adding sub-packages too深入挖掘,我发现 setuptools 有一个find_namespace_package()函数,它也有助于添加子包

EDIT :编辑

Link to find_namespace_packages() ( setuptools version greater than 40.1.0 ): https://setuptools.readthedocs.io/en/latest/setuptools.html#find-namespace-packages链接到find_namespace_packages()setuptools版本大于40.1.0 ): https : 40.1.0

EDIT (08/09/2019):编辑(08/09/2019):

To complete the answer, let me also restructure with an example.为了完成答案,让我也重组一个例子。

The following solution is assuming Python 3.3+ which has support for implicit namespace packages以下解决方案假设 Python 3.3+ 支持隐式命名空间包

Since you are looking for a solution for Python version 3.5 or later, let's take the code samples provided and elaborate further.由于您正在寻找适用于 Python 3.5或更高版本的解决方案,让我们使用提供的代码示例并进一步详细说明。

Let's assume the following:让我们假设以下情况:

Namespace/Python package name : org命名空间/Python 包名称: org

Distribution packages: org_client , org_common分发包: org_client , org_common

Python: 3.3+蟒蛇: 3.3+

setuptools: 40.1.0设置工具: 40.1.0

For you to do the following为您执行以下操作

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

And keeping your top level directories the same, viz.并保持您的顶级目录相同,即。 org_client_client1_mod1 and org_common_config , you can change your structure to the following org_client_client1_mod1org_common_config ,您可以将结构更改为以下内容

Repository 1:存储库 1:

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

Updated setup.py更新了setup.py

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

Repository 2:存储库 2:

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

Updated setup.py :更新setup.py

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

To install (using pip ):安装(使用pip ):

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

Updated pip list will show the following:更新的 pip 列表将显示以下内容:

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

But they won't be importable, for importing you will have to follow org.client and org.common notation.但它们不可导入,导入时您必须遵循org.clientorg.common符号。

To understand why, you can browse here (assuming inside venv):要了解原因,您可以浏览此处(假设在 venv 中):

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

You'll see that there's no org_client or org_common directories, they are interpreted as a namespace package.您将看到没有org_clientorg_common目录,它们被解释为命名空间包。

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

This is a tough subject.这是一个棘手的话题。 All the - 's, _ 's, and __init__.py 's everywhere don't exactly make it easy on us.所有- 's, _ 's 和__init__.py 's 无处不在并不能完全让我们轻松。

First, I'll answer your questions:首先,我来回答你的问题:

With the __init__.py , which of these should I be using (if any)?对于__init__.py ,我应该使用哪些(如果有)?

  • __init__.py can be completely empty, it just needs to be in the correct place. __init__.py可以完全为空,它只需要在正确的位置。 Namely (pun) they should be in any subpackage containing python code (excluding setup.py .) Follow those rules and you should be fine.即(双关语)它们应该在任何包含 python 代码的子包中(不包括setup.py 。)遵循这些规则,你应该没问题。

With setup.py, do I still need to add the namespace_modules parameter, and if so, would I use namespace_modules=['org.common'] , or namespace_modules=['org', 'common'] ?使用 setup.py,我是否还需要添加namespace_modules参数,如果是,我会使用namespace_modules=['org.common']还是namespace_modules=['org', 'common']

  • Nope!不! Only name= and packages= .只有name=packages= However, note the format of the packages= arg compared against the directory structure.但是,请注意与目录结构相比的packages= arg 格式。
  • Here's the format of the package= arg:这是package= arg 的格式: 命名空间包的 setup.py
  • Here's the corresponding directory structure:对应的目录结构如下: example_package/a/<子包>

Could I forgo all of the above by just implementing this differently somehow?我可以通过以某种方式以不同方式实现这一点而放弃上述所有内容吗? Perhaps something simpler or more "pythonic"?也许更简单或更“pythonic”?

  • If you want to be able to install multiple features individually, but under the same top-level namespace, you're on the right track.如果您希望能够单独安装多个功能,但在同一个顶级命名空间下,那么您就走在正确的轨道上。

I'll spend the rest of this answer re-implementing your namespace package in native format:我将在本答案的其余部分以本机格式重新实现您的命名空间包:

I'll put all helpful documentation I've been able to find at the bottom of the post.我会把我能找到的所有有用的文档放在帖子的底部。

K so I'm going to assume you want native namespace packages. K 所以我假设你想要本机命名空间包。 First let's look at the current structure of your 2 repos:首先让我们看一下您的 2 个存储库的当前结构:

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

& &

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

This^ would be too easy!!!这^太简单了!!!

To get what you want:得到你想要的:

My brain isn't elastic enough to know if we can go 3-levels deep with namespace packages, but to do what you want, here's what I'm pretty sure you'd want to do:我的大脑不够灵活,无法知道我们是否可以使用命名空间包深入 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

Basically then the key is going to be specifying the correct name= & packages= args to stuptools.setup() inside of each setup.py .基本上,关键是在每个setup.pystuptools.setup()指定正确的name= & packages= args。

These are going to be:这些将是:

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

& &

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

respectively.分别。

Then just install each one with pip install .然后只需使用pip install .安装每一个pip install . inside each top-level dir.在每个顶级目录中。

Installing the first one will give you access to the somefile.py module, and installing the second will give you access to someotherfile.py .安装第一个会让你访问somefile.py模块,安装第二个会让你访问someotherfile.py It also won't get confused about you trying to install 2 packages named org in the same environment.它也不会因为您尝试在同一环境中安装 2 个名为org包而感到困惑。

K so the most helpful section of the docs: https://packaging.python.org/guides/packaging-namespace-packages/#packaging-namespace-packages K 所以文档中最有用的部分: https : //packaging.python.org/guides/packaging-namespace-packages/#packaging-namespace-packages

And then here's how I actually came to understand this: https://github.com/pypa/sample-namespace-packages/tree/master/native然后这就是我实际上是如何理解这一点的: https : //github.com/pypa/sample-namespace-packages/tree/master/native

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

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