本文时间于 2017年3月17,如果内容有失效,请自行验证,截至目前时间,以下内容可信。
python代码打包发布的以及各种工具介绍的 在这里:
https://packaging.python.org/key_projects/
目前支持的Python打包工具,官方支持的且认可度高的:
Distutils is still the standard tool for packaging in Python. It is included in the standard library (Python 2 and Python 3.0 to 3.6). It is useful for simple Python distributions, but lacks features. It introduces the distutils Python package that can be imported in your setup.py script.
-
Official docs | distutils section of Python Package User Guide (https://docs.python.org/3/library/distutils.html)
Setuptools was developed to overcome Distutils' limitations, and is not included in the standard library. It introduced a command-line utility called easy_install. It also introduced the setuptools Python package that can be imported in your setup.py script, and the pkg_resources Python package that can be imported in your code to locate data files installed with a distribution. One of its gotchas is that it monkey-patches the distutils Python package. It should work well with pip. It sees regular releases.
-
Official docs | Pypi page | GitHub repo | setuptools section of Python Package User Guide(https://setuptools.readthedocs.io/en/latest/)
上面两个工具 ,distutils 是 官方标准库,目前从python2 到python3.6全部内置支持,通过编写setup.py 可以实现对python 代码的打包发布。
distutils : Python 自带的基本安装工具, 适用于非常简单的应用场景; 使用:
为项目创建 setup.py 脚本
执行 setup.py install 可进行安装
setuptools : 针对 distutils 做了大量扩展, 尤其是加入了包依赖机制. 在部分 Python 子社区已然是事实上的标准
以下两款工具 已经废弃,故不推荐使用:
Deprecated/abandoned tools:
-
Distribute was a fork of Setuptools. It shared the same namespace, so if you had Distribute installed, import setuptools would actually import the package distributed with Distribute. Distribute was merged back into Setuptools 0.7, so you don't need to use Distribute any more. In fact, the version on Pypi is just a compatibility layer that installs Setuptools.
-
Distutils2 was an attempt to take the best of Distutils, Setuptools and Distribute and become the standard tool included in Python's standard library. The idea was that Distutils2 would be distributed for old Python versions, and that Distutils2 would be renamed to packaging for Python 3.3, which would include it in its standard library. These plans did not go as intended, however, and currently, Distutils2 is an abandoned project. The latest release was in March 2012, and its Pypi home page has finally been updated to reflect its death.
那么今天我就来用实际例子来讲讲我的一个python工程打包脚本:
我的目的很简单,意图让离线的机器能不依赖其他而通过我生成的zip包能直接安装我工程所需的所有依赖。
http://www.bjhee.com/setuptools.html
以我最新开发一款python小工具为例
描述下我如何编写setup.py 打包的
首先看我工程目录的结构
我列了三层目录,第一层是我工程名, sec_conf_check。工程下 有src目录存放了我我的源码以及源码相关的文件,比如:源码可能依赖一些配置,源码可能需要依赖一些第三方包,本例中依赖了 futures3.0.5.tar.gz;
我的入口程序是 sec_conf_check.py, 我希望用户解压后直接可以在本目录执行;
那么 我如何打包呢,如何编写 setup.py?
网上的教程多如牛毛,针对我遇到的坑, 我在这里再熬述一遍!因为踩了坑也是读了一大堆文档,最终用自己办法解决了:
我们看下 我的setup.py:
12 from warnings import warn
13 import sys,re
14 extras = {}
15 try:
16 from setuptools import setup,find_packages
17 extras['zip_safe'] = False
18 except ImportError:
19 from distutils.core import setup,find_packages
20
21 import os
这几行主要是为了 导入两个函数 一个 setup, 一个find_packages, 我们优先用setuptools的这两个函数,因为setuptools的是对 distutils的增强。
我们看下setup函数设置, 很平常,但需要留意几个配置:
25 setup(
27 name="sec_conf_check",
29 version="0.50",
31 description="An useful conf check Tool",
33 author="Berlinsaint/yangyanbo",
35 url="http://itest.iflytesting.com",
36 include_package_data = True,
38 license="LGPL",
39 author_email='ybyang2@iflytek.com',
40 install_requires=['futures==3.0.5'],
41 packages=find_packages('src'),
42 package_dir={'':'src'},
45 scripts=["src/sec_conf_check/sec_conf_check.py"],
46 dependency_links=["src/sec_conf_check/packages/futures-3.0.5.tar.gz"],
47 **extras
48 )
首先是: install_requires 这个描述了我们依赖的第三方库,添加这个后,后续执行python setup.py install的时候会安装依赖的这个库。
这个选项会和 下面的dependency_links关联,setup.py install futures3.0.5.tar.gz的时候 会首先去读dependency_links,如果有则按照dependency_links的地址下载包并安装,如果在dependency_links没有指定future的安装包位置,会去从pypi下载,这就需要联网,我们不希望这样,所以在dependency_links我指定了本地位置 是以我的工程目录(setup.py同级)为相对路径的 :src/sec_conf_check/packages/futures-3.0.5.tar.gz
再来说说:packages 选项。
这个设置值是一个列表,但是我们方便的使用了 find_packages ,这会让setuptools自己去寻找当前目录的所有packages(python包的定义是 目录含有__Init__.py文件的目录叫包),我们的包从上图可以看到是放在src目录下的,所以 find_packages("src")
package_dir={'':'src'}, 这个参数指明你的工程代码真正源码存放位置 ,被安装到 python的site-packages里的时候是 src下的文件被安装过去,而不是包含src这一层。如下图:(注意安装后位置:)
上述只是说道了 源码的打包,那么源码对应的配置文件,配置目录,日志 (像我上图中的 logs conf scripts packages是如何打到包中的呢?)
官方给了两种方式 一种是 像上面一样的配置参数。我觉得麻烦,不好,采用普遍更推荐的 MANIFEST.in 方式。
使用setup.py 打包的时候 ,会优先读取 MANIFEST.in 文件。
将非代码文件打入到发布包中有两种方式: 参数配置 和 MANIFEST.in方式
在调用setup.py sdist/bdist的时候有一个抉择:
(Note: although the package_data argument was previously only available in setuptools, it was also added to the Python distutils package as of Python 2.4; there is some documentation for the feature available on the python.org website. If using the setuptools-specific include_package_data argument, files specified by package_data will not be automatically added to the manifest unless they are listed in the MANIFEST.in file.)
doc在:(http://setuptools.readthedocs.io/en/latest/setuptools.html#id65)
看不懂洋文这一段我大概说一下吧: 就是如果你setup参数设置了 package_data ,且你设置了 include_package_data=True 那么参数指定的package_data 是无效的,不会生效,文件打不进去。
那么就是说 include_package_data 和 MANIFEST.in配合使用的:
include_package_data
If set to True, this tells setuptools to automatically include any data files it finds inside your package directories that are specified by your MANIFEST.in file. For more information, see the section below on Including Data Files.
还有其它的参数自行参阅文档。
那么我们使用MANIFEST.in 配置方式来打包我们的非代码文件到包里:
MANIFEST.in 语法: https://docs.python.org/2/distutils/sourcedist.html#manifest
这里不是说MANIFEST.in没有坑,有的!
我贴我的MANIFEST.in一小段:
我的MANIFEST.in 很长,并不是我一行行写的, graft是包含目录 include包含文件,再包含目录的时候,我发现了一个问题,就是当我们的非代码目录嵌套子目录的 时候,我们必须显式指定每一层目录,如 过你graft的一个目录 包含子目录,那么在 bdist的时候会报错: 大概是 xxx目录 is not regular file 之类的
其实就起内部没有处理子目录的情况拷贝情况,针对这样有多层子目录的非代码文件,如果想打入包,手写太烦,于是我写了一个collect.py ,大意是将我 scripts下的 文件(除去目录)找出来,并按照Manifest语法写入MANIFEST.in
简单贴下这段low代码:
这样 我们的 setup.py MANIFEST.in 都准备好了!!!
那么开始打包吧!!
你说你想要个 pypi下载的那种tar.gz包?
No problem!
执行完毕后 当前目录生成了:
是不是有一种非常高大上的感觉,没错pypi的源码都是这样生成的。
Windows上想要exe安装形式的?
No problem!
使用python setup.py bdist_wininst
此外 还支持 msi的打包 命令是 python setup.py bdist_msi
我们亲手 实验下源码安装:使用命令 python setup.py install 可以看到自动装了依赖包。
安装完后,执行我们的 自己的工具看看安装是否成功?
show下工具logo:
好了,python打包就说到这里,想了解更多Python技术 请关注爱测未来公众号,并持续关注我们的博客
公众号:itest_forever
CSDN:http://blog.csdn.net/itest_2016
QQ群:274166295(爱测未来2群)、610934609(爱测未来3群)