0、setup.py
示例 先来个简单示例,下面依次讲解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from setuptools import setup, find_packages setup( name ="mytest" , version ="1.0" , author ="flp" , author_email ="flepeng@163.com" , description ="这只是一次测试" , url ="http://iswbm.com/" , packages =find_packages(), packages=['devops' , "devops.dev" , "devops.ops" ], classifiers = [], data_files=[], package_data={}, exclude_package_data={} )
1、基础参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from setuptools import setup, find_packages setup( name ="mytest" , # 指定项目名称,我们在后期打包时,这就是打包的包名称,当然打包时的名称可能还会包含下面的版本号 version ="1.0" , # 指定版本号 author ="flp" , # 作者 author_email ="flepeng@163.com" , # 作者邮箱 description ="这只是一次测试" , # 这是对当前项目的一个描述 url ="http://iswbm.com/" , # 项目主页 # 你要安装的包,通过 setuptools.find_packages 找到当前目录下有哪些包。 # 也可以制定目录,如下,搜索名称为(xyz)的包,以及xyz的下层目录的包搜索出来,并一起打成egg文件包。 # find_packages(xyz) packages =find_packages() # 指定包名,即你需要打包的包名称,要实际在你本地存在哟,它会将指定包名下的所有"*.py" 文件进行打包哟,但不会递归去拷贝所有的子包内容。 # 综上所述,我们如果想要把一个包的所有"*.py" 文件进行打包,应该在packages列表写下所有包的层级关系哟~这样就开源将指定包路径的所有".py" 文件进行打包! packages=['devops' , "devops.dev" , "devops.ops" ], )
setup 函数常用的参数如下:
参数
说明
name
包名称
version
包版本
author
程序的作者
author_email
程序的作者的邮箱地址
maintainer
维护者
maintainer_email
维护者的邮箱地址
url
程序的官网地址
license
程序的授权信息
description
程序的简单描述
long_description
程序的详细描述
platforms
程序适用的软件平台列表
classifiers
程序的所属分类列表
keywords
程序的关键字列表
packages
需要处理的包目录(通常为包含 init .py 的文件夹)
py_modules
需要打包的 Python 单文件列表
download_url
程序的下载地址
cmdclass
添加自定义命令
package_data
指定包内需要包含的数据文件
include_package_data
自动包含包内所有受版本控制(cvs/svn/git)的数据文件
exclude_package_data
当 include_package_data 为 True 时该选项用于排除部分文件
data_files
打包时需要打包的数据文件,如图片,配置文件等
ext_modules
指定扩展模块
scripts
指定可执行脚本,安装时脚本会被安装到系统 PATH 路径下
package_dir
指定哪些目录下的文件被映射到哪个源码包
entry_points
动态发现服务和插件,下面详细讲
python_requires
指定运行时需要的Python版本
requires
指定依赖的其他包
provides
指定可以为哪些模块提供依赖
install_requires
安装时需要安装的依赖包
extras_require
当前包的高级/额外特性需要依赖的分发包
tests_require
在测试时需要使用的依赖包
setup_requires
指定运行 setup.py 文件本身所依赖的包
dependency_links
指定依赖包的下载地址
zip_safe
不压缩包,而是以目录的形式安装
更多参数可见:https://setuptools.readthedocs.io/en/latest/setuptools.html
2、classifiers
程序分类信息 classifiers
参数说明包的分类信息。
所有支持的分类列表见:https://pypi.org/pypi?%3Aaction=list_classifiers
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 from setuptools import setup, find_packages setup( classifiers = [ # 发展时期,常见的如下 # 3 - Alpha # 4 - Beta # 5 - Production/Stable 'Development Status :: 3 - Alpha' , # 开发的目标用户 'Intended Audience :: Developers' , # 属于什么类型 'Topic :: Software Development :: Build Tools' , # 许可证信息 'License :: OSI Approved :: MIT License' , # 目标 Python 版本 'Programming Language :: Python :: 2' , 'Programming Language :: Python :: 2.7' , 'Programming Language :: Python :: 3' , 'Programming Language :: Python :: 3.4' , 'Programming Language :: Python :: 3.5' , ] )
3、依赖包安装 一个项目库可能会依赖于很多其他库,比如我们安装pandas,该库依赖于numpy。那我们用pip conda这些命令安装时,从来不用操心哪些依赖包需要安装,它们的版本限制是怎么样的,而这些信息是setuptools打包分发库时就确定的。
针对依赖包安装与版本管理这项功能,setup函数提供了一些参数install_requires
、 setup_requires
、tests_require
、extras_require
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 from setuptools import setup, find_packages setup( ... # 1 、表明当前模块依赖哪些包,若环境中没有,则会从pypi中下载安装 install_requires=['docutils>=0.3' ], # 2 、setup.py 本身要依赖的包,这通常是为一些setuptools的插件准备的配置。这里列出的包,不会自动安装。 setup_requires=['pbr' ], # 3 、仅在测试时需要使用的依赖,在正常发布的代码中是没有用的。 # 在执行python setup.py test时,可以自动安装这三个库,确保测试的正常运行。 tests_require=[ 'pytest>=3.3.1' , 'pytest-cov>=2.5.1' , ], # 4 、 用于安装setup_requires或tests_require里的软件包。这些信息会写入egg的 metadata 信息中 dependency_links=[ "http://example2.com/p/foobar-1.0.tar.gz" , ], # 5 、install_requires 在安装模块时会自动安装依赖包 # 而 extras_require 不会,这里仅表示该模块会依赖这些包 # 但是这些包通常不会使用到,只有当你深度使用模块时,才会用到,这里需要你手动安装 extras_require={ 'PDF' : ["ReportLab>=1.2" , "RXP" ], 'reST' : ["docutils>=0.3" ], } )
关于 install_requires
, 有以下五种常用的表示方法:
'argparse'
,只包含包名。 这种形式只检查包的存在性,不检查版本。方便,但不利于控制风险。
'setuptools==38.2.4'
,指定版本。 这种形式把风险降到了最低,确保了开发、测试与部署的版本一致,不会出现意外。 缺点是不利于更新,每次更新都需要改动代码。
'docutils >= 0.3'
,这是比较常用的形式。 当对某个库比较信任时,这种形式可以自动保持版本为最新。
'Django >= 1.11, != 1.11.1, <= 2'
,这是比较复杂的形式。 如这个例子,保证了Django的大版本在1.11和2之间,也即1.11.x;并且,排除了已知有问题的版本1.11.1(仅举例)。 对于一些大型、复杂的库,这种形式是最合适的。
'requests[security, socks] >= 2.18.4'
,这是包含了额外的可选依赖的形式。 正常安装requests会自动安装它的install_requires
中指定的依赖,而不会安装security
和socks
这两组依赖。 这两组依赖是定义在它的extras_require
中。 这种形式,用在深度使用某些库时。
4、安装环境的限制 有些库并不是在所有的 Python 版本中都适用的,若一个库安装在一个未兼容的 Python 环境中,理论上不应该在使用时才报错,而应该在安装过程就使其失败,提示禁止安装。
这样的功能,可以使用 python_requires
来实现。
1 2 3 4 setup( .. . python_requires ='>=2.7, <=3' , )
5、生成脚本 有时候我们的库包含了一些非常重要的功能,每次都提供python XXX.py来运行不太方便,最好是把脚本放入系统环境path,以命令行的形式来执行。比如tensorRT就提供了trtexec命令。
那么setup函数提供了 entry_points
和 scripts
这两个参数。它们的区别在于:
entry_points
是把python文件中的函数 自动生成为可执行脚本
scripts
是把 **.sh、.py
等可执行脚本**生成到系统path中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from setuptools import setup setup( # 把python 中的函数自动生成为一个可执行的脚本,放在 /usr/bin/foo 目录下 # 如下:把fool.main文件中的main函数自动生成为一个可执行脚本,可以通过命令foo执行该脚本 entry_points={ 'console_scripts' : [ 'foo = foo.main:main' ] }, # 将 bin/foo.sh 和 bar.py 脚本,生成到系统 PATH中 # 执行 python setup.py install 后会生成 /usr/bin/foo.sh 和 /usr/bin/bar.py scripts=['bin/foo.sh' , 'bar.py' ] )
上面的 scripts 里有的脚本中有 sh
和 py
后缀,那么安装后,setuptools 会原封不动的移动到 /usr/bin 中,并添加可执行权限。
若你想对这些文件再作一些更改,比如去掉多余的后缀,可以这样做
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from setuptools.command.install_scripts import install_scripts class InstallScripts(install_scripts): def run (self): setuptools.command.install_scripts.install_scripts.run (self) # Rename some script files for script in self.get_outputs(): if basename.endswith(".py" ) or basename.endswith(".sh" ): dest = script[:-3] else : continue print ("moving %s to %s" % (script, dest)) shutil.move(script, dest) setup( .. . scripts=['bin/foo.sh' , 'bar.py' ], cmdclass={ "install_scripts" : InstallScripts } )
6、ext_modules
C/C++ 扩展 ext_modules
参数用于构建 C 和 C++ 扩展扩展包。其是 Extension 实例的列表,每一个 Extension 实例描述了一个独立的扩展模块,扩展模块可以设置扩展包名,头文件、源文件、链接库及其路径、宏定义和编辑参数等。如:
1 2 3 4 5 6 7 8 9 setup( # other arguments here... ext_modules=[ Extension('foo', glob(path .join(here , 'src', '*.c')), libraries = [ 'rt' ], include_dirs=[numpy.get_include()]) ] )
详细了解可参考:https://docs.python.org/3.6/distutils/setupscript.html#preprocessor-options
7、指定release setup.py 里只能指定 version,而不能指定 release,如果你需要变更版本号,可以使用 --release
参数进行指定
1 python setup.py bdist_rpm --release=20200617
8、自定义命令行为 自定义命令行为是setuptools进阶知识。setuptools包括许多命令,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Standard commands: build build everything needed to install build_py "build" pure Python modules (copy to build directory) build_ext build C/C++ extensions (compile/link to build directory) build_clib build C/C++ libraries used by Python extensions build_scripts "build" scripts (copy and fixup #! line) clean clean up temporary files from 'build' command install install everything from build directory install_lib install all Python modules (extensions and pure Python) install_headers install C/C++ header files install_scripts install scripts (Python or otherwise) install_data install data files sdist create a source distribution (tarball, zip file, etc.) register register the distribution with the Python package index bdist create a built (binary) distribution bdist_dumb create a "dumb" built distribution bdist_rpm create an RPM distribution bdist_wininst create an executable installer for MS Windows upload upload binary package to PyPI Extra commands: 见:https://pythonhosted.org/an_example_pypi_project/setuptools.html
这些命令具体是由定义在setuptools.command中的类 执行的。比如python setup.py bdist
由setuptools.command.bdist类来执行。因此我们可以继承于setuptools.command中的类来执行自定义的命令行为。比如pytorch的Build.Extension就继承于setuptools.command.build_ext。具体怎么继承并改写这个command类就需要阅读源码了。
继承完command类后,需要通过cmdclass参数告诉setuptools,该参数为一个字典,key为str命令名,value为继承于command类。
1 2 3 4 5 6 7 from setuptools import setup import setuptools.command.build_ext as build_ext class BuildExtension(build_ext,object): ………… setup( cmdclass={'build_ext': BuildExtension} )