如果你只使用 Python 3、也只面向 Python 3 的用户,那么有好消息要告诉你。PEP 420

(隐式命名空间包,Implicit Namespace Packages)引入了一种定义命名空间包的新方法。

它是标准路径的一部分,并从 3.3 版开始成为语言官方内容的一部分。简而言之,对于每

个包含 Python 包或模块(也包括命名空间包)的目录来说,如果它不包含__init__.py

文件,那么它就被看作是命名空间包。下面是上一节介绍的文件结构示例:

$ tree acme.sql/

acme.sql/

├── acme

│ └── sql

│ └── __init__.py

└── setup.py

2 directories, 2 files

$ tree acme.templating/

acme.templating/

├── acme

│ └── templating

│ └── __init__.py

└── setup.py

2 directories, 2 files

在 Python 3.3 以及更高版本中,这些足以说明 acme 是一个命名空间包。使用安装工

具的最小 setup.py 脚本如下所示:

from setuptools import setup

setup(

name='acme.templating',

packages=['acme.templating'],

)

不幸的是,在写作本书时,setuptools.find_packages()还不支持 PEP 420。不

管怎样,这在未来可能会改变。此外,要想实现命名空间包的简单集成,要求显示地定义

包列表似乎只是非常小的代价。

以前 Python 版本中的命名空间包

在 3.3 版以前的 Python 版本中,无法使用 PEP 420 布局中的命名空间包。但是这个概

念非常古老,也常用于像 Zope 这样的成熟项目,因此完全可以使用它,但是没有隐式定义。

在旧版 Python 中,有几种方法可以将包定义为命名空间。

最简单的方法就是为每个组件创建一个文件结构,类似于没有命名空间包的普通包布

局,并将所有事情都留给 setuptools。因此,acme.sql 和 acme.templating 的布

局示例可能如下所示:

$ tree acme.sql/

acme.sql/

├── acme

│ ├── __init__.py

│ └── sql

│ └── __init__.py

└── setup.py

2 directories, 3 files

$ tree acme.templating/

acme.templating/

├── acme

│ ├── __init__.py

│ └── templating

│ └── __init__.py

└── setup.py

2 directories, 3 files

注意,acme.sql 和 acme.templating 都有一个额外的源代码文件 acme/__init__.py。这个文件必须是空的。如果我们提供 acme 作为 setuptools.setup()函数的

namespace_packages 关键字参数的值,那么将会创建如下的 acme 命名空间包:

from setuptools import setup

setup(

name='acme.templating',

packages=['acme.templating'],

namespace_packages=['acme'],

)

最简单的方法不一定是最好的。为了注册一个新的命名空间,setuptools 将会在

__init__.py 文件中调用 pkg_resources.declare_namespace() 函数。即使

__init__.py 文件是空的也会调用。无论如何,正如官方文档所说,你自己负责在

__init__.py 文件中声明命名空间,并且未来可能会删除 setuptools 的这个隐式行为。

为了保证安全,也为了未来依然可用(future-proof),你需要将下面这行代码添加到

acme/__init__.py 文件中:

import('pkg_resources').declare_namespace(name上传一个包

对于 Python 包而言,如果没有有组织的保存、上传和下载方式,那么它是没有用的。

Python 包索引是 Python 社区开源包的主要来源。任何人都可以免费上传新的包,唯一的要

求就是在 PyPI 网站上进行注册。

当然,你不必局限于这个索引,而且所有打包工具都支持使用其他包仓库。对于在内

部组织分发或为了开发目的而分发的闭源代码来说,这一点特别有用。下一章将会解释这

些打包用法的细节,以及关于如何创建你自己的包索引的说明。本章我们只关注向 PyPI

开源上传,并稍微介绍一下指定其他仓库的方法。

PyPI—Python 包索引

如前所述,PyPI 是开源包发行版的官方来源。从 PyPI 下载不需要任何账号或者权限。

你唯一需要的是一个包管理器,可以从 PyPI 下载新的发行版。你的首选应该是 pip。

1.上传到 PyPI 或其他包索引

任何人都可以注册并向 PyPI 上传包,只要他/她有注册账号就行。包与用户绑定,因此在默认情况下,只有注册了包名称的用户是它的管理员,并且可以上传新的发行版。对

于大型项目来说,这可能是一个问题,因此有一个选项可以指定其他用户作为包的维护者,

以便他们能够上传新的发行版。

上传一个包的最简单方法就是使用 setup.py 脚本的 upload 命令:

$ python setup.py <dist-commands> upload

这里的<dist-commands>是创建要上传的发行版的命令列表。只有在相同的

setup.py 执行期间创建的发行版才会被上传到仓库中。因此,如果你想要同时上传源代

码发行版、构建发行版和 wheel 包,那么你需要使用下列命令:

$ python setup.py sdist bdist bdist_wheel upload

使用 setup.py 进行上传时,你不能重复使用已经构建的发行版,每次上传时都必须

重新构建。这可能是有意义的,但对于大型项目或复杂项目来说很不方便,对于这些项目

来说创建发行版实际上可能需要相当长的时间。setup.py upload 的另一个问题是,在

某些 Python 版本中它可以使用纯文本 HTTP 连接或未验证的 HTTPS 连接。这就是为什么

推荐使用 twine 作为 setup.py upload 的安全替代。

twine 是与 PyPI 交互的实用程序,它目前只有一个作用将包安全地上传到仓库

中。它支持任何打包格式,并始终确保连接安全。它还允许你上传已经创建的文件,这样

你能够在发布之前对发行版进行测试。twine 的一个示例用法仍然需要调用 setup.py 来

构建发行版,如下所示:

$ python setup.py sdist bdist_wheel

$ twine upload dist/*

如果你还没有注册过这个包,那么上传会失败,因为你需要首先注册它。你也可以使

用 twine 来完成注册,如下所示:

$ twine register dist/*

2..pypirc

.pypirc 是一个配置文件,其中保存有关 Python 包仓库的信息。它应该位于你的主

目录中。这个文件的格式如下所示:

[distutils]

index-servers =

pypi

other

[pypi]

repository: <repository-url>

username: <username>

password: <password>

[other]

repository: https://example.com/pypi

username: <username>

password: <password>

distutils 区段应该包含 index-servers 变量,其中列出描述所有可用仓库及其

证书的所有区段。每个仓库区段中只能修改下面这 3 个变量。

repository:包仓库的 URL(默认是 https://www.python.org/pypi)。

• username:给定仓库中授权的用户名。

• password:明文的授权用户密码。

注意,明文保存你的仓库密码可能不是最明智的安全选择。你可以一直将其空着,必

要时再提示你输入。

所有为 Python 构建的打包工具都应该遵守.pypirc 文件。虽然不是每个打包相关的

实用程序都满足这一要求,但大多数重要的实用程序都支持这一点,例如 pip、twine、

distutils 和 setuptools。