关于setuptools:在Python中创建命名空间包的方法

The way to make namespace packages in Python

从distribute中的名称空间包中,我知道可以利用名称空间包将一个大的python包分为几个小的包。真是太棒了。文件还提到:

Note, by the way, that your project’s source tree must include the
namespace packages’ __init__.py files (and the __init__.py of any
parent packages), in a normal Python package layout. These __init__.py
files must contain the line:

1
__import__('pkg_resources').declare_namespace(__name__)

This code ensures that the namespace package machinery is operating
and that the current package is registered as a namespace package.

我想知道将目录的层次结构与包的层次结构保持一致有什么好处吗?或者,这仅仅是分发/设置工具的名称空间包特性的技术要求?

前任,

我想提供一个子包foo.bar,这样我必须构建以下文件夹层次结构,并准备一个uu init_uuuuy.py以使setup.py工作于命名空间包:

1
2
3
4
5
~foo.bar/
~foo.bar/setup.py
~foo.bar/foo/__init__.py    <=    one-lined file dedicated to namespace packages
~foo.bar/foo/bar/__init__.py
~foo.bar/foo/bar/foobar.py

我不熟悉名称空间包,但在我看来1)foo/bar和2)(几乎)一行的init_uuuuy是常规任务。它们确实提供了一些"这是一个名称空间包"的提示,但我认为在setup.py中已经有了这些信息?

编辑:

如下面的块中所示,我的工作目录中是否可以有一个没有嵌套目录和一行uuinit_uuuuy.py的名称空间包?也就是说,我们可以要求setup.py通过只放一行namespace_packages = ['foo']来自动生成这些内容吗?

1
2
3
4
~foo.bar/
~foo.bar/setup.py
~foo.bar/src/__init__.py    <=    for bar package
~foo.bar/src/foobar.py


当需要导入子包时,名称空间包主要具有特定的效果。基本上,这里是导入foo.bar时发生的情况。

  • 进口商扫描sys.path寻找类似foo的东西。
  • 当它发现什么东西时,它会在发现的foo的内部寻找bar
  • 如果找不到bar
  • 如果foo是一个正常的包,则会引发一个ImportError,表示foo.bar不存在。
  • 如果foo是名称空间包,则导入程序将返回到sys.path中查找foo的下一个匹配项。只有在所有路径都已用尽时,才会提升ImportError

所以这就是它的作用,但不能解释为什么你会想要它。假设您设计了一个大的、有用的库(foo),但作为其中的一部分,您还开发了一个小的、但非常有用的实用程序(foo.bar,其他Python程序员发现它很有用,即使它们不使用大的库。

您可以将它们作为包中的一个大斑点(按照您的设计)一起分发,即使大多数使用它的人只导入子模块。您的用户会发现这非常不方便,因为他们必须下载整个东西(全部200MB!)即使他们只对10行实用程序类感兴趣。如果你有一个开放式的许可证,你可能会发现有几个人最终会把它分出来,现在你的实用程序模块有六个不同的版本。

您可以重写整个库,使实用程序位于foo名称空间之外(只是bar而不是foo.bar)。您将能够单独分发该实用程序,并且您的一些用户会很高兴,但是这是一项大量的工作,特别是考虑到实际上有许多用户使用整个库,因此他们必须重写他们的程序才能使用新的。

因此,您真正想要的是一种单独安装foo.bar的方法,但是如果需要的话,也可以与foo愉快地共存。

一个名称空间包允许两个完全独立的foo包安装共存。setuptools将认识到,这两个包设计为彼此相邻,并礼貌地移动文件夹/文件,使它们都位于路径上并显示为foo,一个包含foo.bar,另一个包含foo的其余部分。

您将有两个不同的setup.py脚本,每个脚本一个。两个包中的foo/__init__.py都必须指明它们是名称空间包,因此导入程序知道不管首先发现哪个包,都要继续。