赞
踩
从 1998 年引入的 distutils 包开始,随后在 2003 年 setuptools 对其进行改进。包括出现的其他新项目。最后,Python Packaging Authority (PyPA)组织,将秩序和组织性带回到打包生态系统中。PyPA 维护的 Python 打包用户指南(Python Packaging User Guide)是关于最新打包工具和最佳实践的权威信息来源。
由于 PyPA 的存在,Python 打包的现状。
PyPA 除了提供一份权威的打包指南之外,还维护着打包项目与新的官方打包的标准化过程。由于 PyPA 的参与,构建发行版已经正在逐步弃用 egg 格式,而是支持使用 wheel 格式。未来可能会为我们带来全新的方法(fresh breath)。
工具推荐。
Python 打包用户指南有关使用包的推荐工具给出了一些建议。这些工具大体可分为两组:用于安装包的工具和用于包的创建与分发的工具。PyPA 推荐的第一组实用工具如下:
在 Python 包用户指南中,推荐包的创建与分发的工具如下:
很显然,组织大型应用的代码的最简单方法是将其分成几个包。这使得代码更加简单,也更容易理解、维护和修改。这样也使每个包的可复用性最大化。它们的作用就像组件一样。
setup.py
对于一个需要被分发的包来说,其根目录包含一个 setup.py
脚本。它定义了 distutils 模块中描述的所有元数据,并将其合并为标准的 setup() 函数调用的参数。虽然 distutils 是一个标准库模块,但建议你使用 setuptools 包来代替,它对标准的 distutils 做了一些改进。
setup.cfg
setup.cfg 文件包含 setup.py
脚本命令的默认选项。如果构建和分发包的过程更加复杂,并且需要向 setup.py
命令中传入许多可选参数,那么这个文件非常有用。setup.cfg 文件的语法与内置 configparser 模块提供的语法相同,因此它类似于常见的 Microsoft Windows INI 文件。
MANIFEST.in
使用 sdist 命令构建发行版时,distutils 将浏览包的目录,查找需要包含在存档中的文件。distutils 将包含:
匹配 glob 模式 test/test*.py 的文件包括:README、README.txt、setup.py
和 setup.cfg。此外,如果你的包是由 subversion 或 CVS 管理,那么 sdist 将浏览诸如 .svn 之类的文件夹,查找需要包含的文件。利用扩展也可以与其他版本控制系统集成。sdist 将构建一个 MANIFEST 文件,列出所有文件并将它们包含在存档中。假设你不使用这些版本控制系统,并且需要包含更多的文件。现在,在与 setup.py
相同的目录中,你可以为 MANIFEST 文件定义一个名为 MANIFEST.in
的模板,在其中你可以指定 sdist 要包含哪些文件。
最重要的元数据。
除了被分发包的名称和版本之外,setup 可以接受的最重要的参数包括:
trove 分类器。
PyPI 和 distutils 为应用程序分类提供了一种解决方案,就是使用一套被称为 trove 分类器(trove classifiers)的分类器。所有分类器都形成一个树状结构。每个分类器都是字符串形式,其中用 ::
字符串分隔每个命名空间。分类器列表在包定义中是作为 setup() 函数的 classifiers 参数。trove 分类器还可以提供以下信息:支持的 Python 版本或系统、项目的开发阶段或发布代码所使用的许可证。
常见模式。
如果不考虑元数据可能在项目其他部分找到的事实,setuptools 或 distuitls 在 setup() 函数调用中接受的大多数元数据都可以手动输入。setuptools 和 distuitls 都不能从项目源代码中自动提取各种元数据信息,因此你需要自己提供这些信息。在 Python 社区中有一些常见模式可以解决最常见的问题,例如依赖管理、包含版本/自述文件等。
(1)自动包含包中的版本字符串。
PEP 440(版本标识和依赖规范,Version Identification and DependencySpecification)文档规定了版本和依赖规范的标准。这是一份很长的文档,包含已接受的版本规范方案和Python打包工具中应该如何做版本匹配和比较。
PEP 396(模块版本号,Module Version Numbers)解决了将包或模块的版本标识符包含在什么位置。根据 PEP 396,如果一个包或模块要指定一个版本,那么应该将其包含在包的根目录(__init__.py
)或模块文件的 __version__
属性中。另一个事实上的标准是,也要将包括版本元组的 VERSION 属性包含其中。PEP 396 的另一个建议是,在 distutils 的 setup() 函数中提供的版本应该从 __version__
派生,反之亦然。
(2)README 文件。
Python 包索引可以在PyPI门户的包页面中显示一个项目的 readme 或者 long_description 的值。最常见的选择是 Markdown,它是 GitHub 上默认的标记语言——目前大多数开源的 Python 开发都是在 GitHub 上。
(3)管理依赖。
许多项目需要安装和/或使用一些外部包。如果依赖列表很长的话,就会出现一个问题:如何管理依赖?在大多数情况下答案很简单。不要过度设计(over-engineer)问题。保持简单,并在 setup.py
脚本中明确提供依赖列表。或者使用 requirements.txt 文件来追踪包的依赖列表。
利用 distutils 可以创建新的命令。新的命令可以用一个入口点(entry point)来注册,这是由 setuptools 引入的,是一种将包定义为插件的简单方法。
入口点是类或函数的命名链接,通过 setuptools 中的一些 API 变得可用。任何应用都可以扫描所有已注册的包,并且将链接代码作为插件使用。
所有命名链接都集中在已命名的部分(named section)。distutils 被加载时,它将扫描在 distutils.commands 中注册的链接。
使用 setuptools 主要是用于构建并分发包。但是,你仍然需要知道如何使用它们直接从项目源代码安装包。其原因很简单。在向 PyPI 提交包之前,最好测试一下你的打包代码是否正常工作。最简单的测试方法就是安装它。
setup.py install
。
install 命令可以将包安装到 Python 环境中。如果之前没有构建过的话,它会尝试构建包,然后将结果注入到 Python 树中。如果提供了源代码发行版,那么可以在临时文件夹中将其解压,然后用这个命令安装。install 命令还将安装在 install_requires 元数据中定义的依赖。安装一个包时,对 setup.py
脚本的一个替代方法是使用 pip。
卸载包。
setuptools 和 distutils 都没有 uninstall(卸载)命令,使用 pip 可以卸载任何 Python 包。
setup.py develop
或 pip -e
。
setuptools 提供了一个额外的 develop 命令,允许我们在开发模式(development mod)下安装包。这个命令在部署目录(site-packages)中创建一个指向项目源代码的特殊链接,而不是将整个包复制过去。可以编辑包的源代码而无需重新安装,并且它在 sys.path 中可用,就像正常安装一样。
pip 也可以用这种模式来安装包。这个安装选项叫作可编辑模式(editablemode),可以使用 install 命令的 -e 参数来启用。
Python 之禅中关于命名空间的说法如下:命名空间是一个绝妙的想法,我们要多加利用!
第一种是语言上下文中的命名空间。
另一种命名空间可以在包的层面提供。它们就是命名空间包(namespace packages)。
可以将命名空间包理解成在高于元包(meta-package)的层面对相关的包或模块进行分组的方法,其中每个包都可以单独安装。
只使用 Python 3、也只面向 Python 3 的用户。PEP 420(隐式命名空间包,ImplicitNamespace Packages)引入了一种定义命名空间包的新方法。它是标准路径的一部分,并从3.3版开始成为语言官方内容的一部分。简而言之,对于每个包含 Python 包或模块(也包括命名空间包)的目录来说,如果它不包含 __init__.py
文件,那么它就被看作是命名空间包。
在 3.3 版以前的 Python 版本中,无法使用 PEP 420 布局中的命名空间包。最简单的方法就是为每个组件创建一个文件结构,类似于没有命名空间包的普通包布局,并将所有事情都留给 setuptools。
Python 包索引是 Python 社区开源包的主要来源。任何人都可以免费上传新的包,唯一的要求就是在 PyPI 网站上进行注册。
PyPI 是开源包发行版的官方来源。唯一需要的是一个包管理器,可以从 PyPI 下载新的发行版,首选应该是 pip。
上传到 PyPI 或其他包索引。
上传一个包的最简单方法就是使用 setup.py
脚本的upload命令:python setup.py <dist-commands> upload
。<dist-commands>
是创建要上传的发行版的命令列表。只有在相同的 setup.py
执行期间创建的发行版才会被上传到仓库中。如果你想要同时上传源代码发行版、构建发行版和wheel包,那么你需要使用下列命令:python setup.py sdist dbdist bdist_wheel upload
。使用 setup.py
进行上传时,你不能重复使用已经构建的发行版,每次上传时都必须重新构建。setup.py upload
的另一个问题是,在某些 Python 版本中它可以使用纯文本 HTTP 连接或未验证的 HTTPS 连接。
twine 是与 PyPI 交互的实用程序,它目前只有一个作用——将包安全地上传到仓库中。它支持任何打包格式,并始终确保连接安全。它还允许你上传已经创建的文件,这样你能够在发布之前对发行版进行测试。需要首先注册它,才能上传。可以使用 twine 来完成注册:twine register dist/*
。
.pypirc。
.pypirc 是一个配置文件,其中保存有关 Python 包仓库的信息。它应该位于你的主目录中。所有为 Python 构建的打包工具都应该遵守 .pypirc 文件。
通常来说,Python 包有两种类型的发行版:
源代码发行版是最简单的,也是最不依赖于平台的。这种发行版只包含 Python 源代码,应该已经是高度可移植的。
sdist。
sdist 命令是最简单的命令。它创建一棵分发树,其中复制了运行一个包所需要的全部内容。然后这棵树被归档到一个或多个存档文件中(通常只创建一个 tar 文件)。这个存档基本上是源代码树的副本。
bdist 和 wheels。
为了能够分发预构建的发行版,distutils 提供了 build 命令,可以通过 4 个步骤来编译包。
bdist 命令使用 build 命令来构建二进制发行版。它调用 build 和所有依赖的命令,然后用和 sdist 相同的方式创建一份存档。
另一种构建发行版是 wheel 包提供的“wheel”。安装完 wheel 后(例如使用 pip),它会向 distutils 中添加一个新的 bdist_wheel 命令。使用 wheel 的优点:
setup.py
)。根据 PyPA 的推荐,wheel 应该是你的默认分发格式。Linux 平台特定的 wheel 还不可用,因此如果你必须分发带有 C 扩展的包,那么你需要为 Linux 用户创建 sdist 发行版。
Python 标准库中缺少合适的工具能够让程序员创建简单的可执行文件,创建独立可执行文件是经常被忽略的一个主题。
Python 代码作为一个包分发时,需要有 Python 解释器才能运行。而编译语言有一个很大的优点,就是它允许为给定的系统架构创建可执行的应用程序,用户不需要知道底层技术就可以运行。
对开发者友好的操作系统(例如 Mac OS X 或大多数 Linux 发行版)都预装了 Python。因此对于它们的用户来说,基于 Python 的应用仍然可以作为源代码包分发,依赖于主脚本文件中特定的解释器指令(interpreter directive),这一指令通常被称为 shebang。对于大多数 Python 应用而言,其格式如下所示:#! /usr/bin/env python
。这种指令放在脚本的第一行,会将其标记为默认由指定环境的 Python 版本进行解释。shebang 在 Windows 上无法使用。
如果用户体验的简单性比用户与应用代码交互的能力更加重要,那么独立可执行文件非常有用。独立可执行文件应该是对非技术最终用户分发应用的首选方式,也可能是分发 Windows 上的 Python 应用的唯一合理方式。
独立可执行文件通常适用于以下情形。
Python 没有任何内置库支持构建独立可执行文件。一些社区项目解决了这一问题,并取得了不同程度的成功。最有名的如:PyInstaller;cx_Freeze;py2exe;py2app。
PyInstaller。
到目前为止,PyInstaller(http://www.pyinstaller.org)是将 Python 包冻结为独立可执行文件的最先进的程序。它在目前每种可用的解决方案中提供最广泛的多平台兼容性,所以它也是最受推荐的方法。PyInstaller支持的平台包括:
支持的 Python 版本包括 Python 2.7 与 Python 3.3、3.4 和 3.5。它可以在 PyPI 上找到,所以可以利用 pip 在工作环境中安装它。它不支持跨平台构建(交叉编译),因此如果你想要为某个特定平台构建独立可执行文件,那么你需要在那个平台上执行构建。你可以随时使用 Vagrant,它会为你提供所需要的操作系统作为虚拟机。
想要为 Windows 用户创建一个独立可执行文件,利用下面这个简短的命令可以将我们的应用打包:pyinstaller myscript.py
,生成的目录和文件结构,其中 dist/myscript 目录包含构建应用,必须分发整个目录,它包含运行应用所需要的所有附加文件(DLL、编译扩展库等)。利用 pyinstaller 命令的 --onefile 开关可以得到更紧凑的发行版。如果使用 --onefile 选项进行构建,你唯一需要向用户分发的文件就是 dist 目录中找到的单一可执行文件(这里是 myscript.exe)。
运行 pyinstaller 命令的一个副作用是创建了 *.spec 文件。这是一个自动生成的 Python 模块,其中包含如何从源代码创建可执行文件的说明。
cx_Freeze。
cx_Freeze(http://cx-freeze.sourceforge.net)是另一种用于创建独立可执行文件的工具。它是一种比 PyInstaller 更加简单的解决方案,但也支持 3 个主要平台:Windows;Linux;Mac OS X。
与 PyInstaller 一样,它不允许我们执行跨平台构建,因此你需要在想要分发的同一个操作系统中创建可执行文件。cx_Freeze 的主要缺点是它不允许我们创建真正的单文件可执行文件。用它构建的应用都需要与相关的 DLL 文件和库一起分发。用法:cxfreeze myscript.py
。
py2exe 和 py2app。
py2exe 和 py2app 另外两种用于创建独立可执行文件的程序,通过 distutils 或 setuptools 与 Python 打包进行集成。它们的用法和限制都非常相似,只需要修改 setup.py
脚本。用法:python setup.py py2exe
和 python setup.py py2app
。py2exe 和 py2app 的主要缺点是它们只面向一种平台:py2exe 允许构建 Windows 可执行文件。py2app 允许构建 Mac OS X 应用。
独立可执行文件决不会让应用代码变得安全,知道这一点是很重要的。从这样的可执行文件中反编译嵌入代码并不是一件容易的任务,但它的确是可行的。更重要的是,这种反编译的结果(如果使用适当的工具)可能与原始源代码非常相似。
使反编译更难。
通常来说,反编译过程包括以下几个步骤,如下所示:
阻止反编译过程或者使其结果变得没有价值:
这些解决方案都会使开发过程变得更加困难,同时每一个想法都有许多陷阱和缺点。大多数情况下,它们只是将不可避免的结果推迟了而已。
要想不让闭源代码泄露到应用之外,唯一可靠的方法是就是不以任何形式将其直接发送给用户。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。