Python
安装包在不同系统有不同的安装方式,其底层依赖 setup.py
实现,本文详细介绍 setuptools
的实现原理和使用指南。
打包工具介绍
distutils
: Python 官方开发的标准库distribute utils
,Python 打包工具的始祖,核心配置文件 setup.cfg/setup.py
setuptools
: distutils
的升级版本,主流实现
distribute
: 是 setuptools
的分支版本,关系比较密切。
本文重点介绍 setuptools
的使用。
安装
pip2 install setuptools
# or
pip3 install setuptools
# or
pip3 install setuptools==39.2.0
help
$ python3 setup.py --help-commands
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
check perform some checks on the package
upload upload binary package to PyPI
Extra commands:
egg_info create a distribution's .egg-info directory
develop install package in 'development mode'
rpm_version Output the rpm *compatible* version string of this package
deb_version Output the deb *compatible* version string of this package
alias define a shortcut to invoke one or more commands
bdist_egg create an "egg" distribution
dist_info create a .dist-info directory
easy_install Find/get/install Python packages
install_egg_info Install an .egg-info directory for the package
rotate delete older distributions, keeping N newest files
saveopts save supplied options to setup.cfg or other config file
setopt set an option in setup.cfg or another config file
test run unit tests after in-place build
upload_docs Upload documentation to PyPI
usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
or: setup.py --help [cmd1 cmd2 ...]
or: setup.py --help-commands
or: setup.py cmd --help
常用命令
# install everything from build directory
python3 setup.py install
# create a source distribution
python3 setup.py sdist
# create an RPM distribution
python3 setup.py bdist_rpm
# create an "egg" distribution
python3 setup.py bdist_egg
# create an executable installer for MS Windows, 必须在 Windows 上执行
python3 setup.py bdist_wininst
包格式
- 源码包:以压缩格式存在,一般为
tar.gz
,安装时需要根据不同平台编译。包一般比较大。
- 二进制包:已经编译,一般以
eggs
、wheels/whl
形式存在,安装速度比源码快。
- egg(.egg): 由 setuptools 在 2004 年引入,本质上是一个包含了项目代码以及元数据(一个名为egg-info的子目录)的zip压缩包
- wheels(.whl): 由 PEP427 在 2012 年定义,Wheel 格式的包中包含一个名为
.dist-info
的子目录,目标是替代 egg
区别
Wheel 和 Egg 的主要区别:
- Egg 既是一种分发格式,也是一种运行时安装的格式,并且是可以被直接 import
- Egg 格式的包需要使用 easy_install 进行安装,
- Wheel 有一个官方的 PEP427 来定义,而 Egg 没有 PEP 定义
- Wheel 是一种分发格式,即打包格式。
- Wheel 文件不会包含 .pyc 文件
- Wheel 使用和 PEP376 兼容的 .dist-info 目录,而 Egg 使用 .egg-info 目录
- Wheel 有着更丰富的命名规则
- Wheel 是有版本的。每个 Wheel 文件都包含 wheel 规范的版本和打包的实现
- Wheel 在内部被 sysconfig path type 管理,因此转向其他格式也更容易
- Wheel 可以使用 pip 安装(tgz的也可以)
.whl
文件有一点与 .egg
文件相似:实际上它们都是 .zip
文件。可以将 .whl
文件名扩展改为 .zip
,就可以使用 zip
应用程序打开它
配置
以 Pecan 使用介绍 为例
setup.py
# -*- coding: utf-8 -*-
try:
from setuptools import setup, find_packages
except ImportError:
from ez_setup import use_setuptools
use_setuptools()
from setuptools import setup, find_packages
setup(
name='test_pecan',
version='0.1',
description='',
author='',
author_email='',
install_requires=[
"pecan",
],
test_suite='test_pecan',
zip_safe=False,
include_package_data=True,
packages=find_packages(exclude=['ez_setup'])
)
setup
函数参数:
- classifiers: 包的分类信息
- packages: 告诉Distutils需要处理那些包(包含__init__.py的文件夹)
- package_dir: 告诉Distutils哪些目录下的文件被映射到哪个源码包。一个例子:
package_dir = {'': 'lib'}
,表示 root package
中的模块都在lib目录中
- ext_modules: 是一个包含Extension实例的列表,Extension的定义也有一些参数
- ext_package: 定义extension的相对路径
- requires: 定义依赖哪些模块
- provides: 定义可以为哪些模块提供依赖
- scripts: 指定python源码文件,可以从命令行执行。在安装时指定–install-script
- package_data: 通常包含与包实现相关的一些数据文件或类似于readme的文件。如果没有提供模板,会被添加到MANIFEST文件中
- exclude_package_data 不打包的文件
- data_files: 静态文件,如配置文件、service文件等
cmdclass={'my_command': XXXCommand}
用来指定用户自定义的命令类
XXXCommand
用户自定义命令类,继承自 distutils.core.Command
类,用来执行一些特定的任务
- 当执行
python setup.py my_command
时,将会调用 XXXCommand
类的 run()
方法来执行任务
setup(...,
data_files=[
('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
('config', ['cfg/data.cfg']),
('/etc/init.d', ['init-script'])]
)
规定了哪些文件被安装到哪些目录中。如果目录名是相对路径,则是相对于 sys.prefix
或 sys.exec_prefix
的路径
setup.cfg
[nosetests]
match=^test
where=test_pecan
nocapture=1
cover-package=test_pecan
cover-erase=1
MANIFEST.in
MANIFEST.in
用来控制文件的分发,如下例:
include *.txt
recursive-include examples *.txt *.py
prune examples/sample?/build
作用如下:
- 第一行:所有根目录下的以
txt
为后缀名的文件,都会分发
- 第二行:根目录下的
examples
目录 和 txt、py
文件都会分发
- 第三行:路径匹配上
examples/sample?/build
不会分发
MANIFEST.in
和 setup.py
同级的目录下,setuptools
时会自动读取该文件
PBR
pbr
是 setuptools
的辅助工具,最初由 OpenStack
开发(https://launchpad.net/pbr)。pbr
工作运行时会读取和过滤 setup.cfg
中的数据解析后给 setup.py
作为参数。包含如下功能:
- 从 git 中获取
Version、AUTHORS and ChangeLog
信息
- Sphinx Autodoc。pbr 会扫描 project,找到所有模块,生成 stub files
- 读取
requirements.txt
,生成 setup 函数需要的 xxx.egg-info/requires.txt
setup.py
import setuptools
try:
import multiprocessing # noqa
except ImportError:
pass
setuptools.setup(
setup_requires=['pbr>=2.0.0'],
pbr=True)
setup.cfg
参考:https://opendev.org/openstack/octavia/src/branch/master/setup.cfg
[metadata]
name = octavia
summary = OpenStack Octavia Scalable Load Balancer as a Service
description-file =
README.rst
author = OpenStack
author-email = openstack-discuss@lists.openstack.org
home-page = https://docs.openstack.org/octavia/latest/
python-requires = >=3.6
classifier =
Development Status :: 5 - Production/Stable
Environment :: OpenStack
Intended Audience :: Developers
Intended Audience :: Information Technology
Intended Audience :: System Administrators
License :: OSI Approved :: Apache Software License
Operating System :: POSIX :: Linux
Programming Language :: Python
Programming Language :: Python :: 3
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
[files]
packages =
octavia
data_files =
etc/octavia =
etc/octavia.conf
share/octavia =
LICENSE
README.rst
share/octavia/diskimage-create =
diskimage-create/diskimage-create.sh
diskimage-create/image-tests.sh
diskimage-create/README.rst
diskimage-create/requirements.txt
diskimage-create/test-requirements.txt
diskimage-create/tox.ini
diskimage-create/version.txt
[entry_points]
wsgi_scripts =
octavia-wsgi = octavia.api.app:setup_app
console_scripts =
octavia-api = octavia.cmd.api:main
...
[extras]
# Required in case of AmphoraV2 redis jobboard is used
redis =
redis>=2.10.0
# Required in case of AmphoraV2 zookeeper jobboard is used
zookeeper =
kazoo>=2.6.0 # Apache-2.0
zake>=0.1.6 # Apache-2.0
说明:
- packages: 指定需要包含的包,功能类似于 setuptools.find_packages
- data_files: 指定目的目录和源文件路径
- entry_points: 入口,会放到
/usr/local/bin/
目录下
扩展 setup.cfg
版本信息(python3 setup.py sdist)会在目标文件中自动生成:
...
[egg_info]
tag_build =
tag_date = 0
...
指定版本打包
基于 pbr
的打包,指定 tag 版本会显示为软件包的版本,示例:
git tag v1.0.0
git checkout v1.0.0
- 打 source 包,打包后可以在
dist/
目录下找到对应的压缩包
# 默认,下面示例采用该方式
python3 setup.py sdist
# 指定包格式,查看指的到包格式:python2 setup.py bdist --help-formats
python3 setup.py sdist --formats=gztar,zip
# 指定用户
python3 setup.py sdist --owner=root --group=root
# egg 包
python3 setup.py bdist_egg
# rpm 包,更多参考:https://www.xiexianbin.cn/tags/rpmbuild/index.html
yum install rpm-build make gcc -y
python3 setup.py bdist_rpm --help
python3 setup.py bdist_rpm --release=1
$ cd dist/ && tar -zxvf *.tar.gz
$ tree .
...
├── MANIFEST.in
├── PKG-INFO # 包版本信息,为 v1.0.0
├── README.rst
├── requirements.txt
├── setup.cfg
├── setup.py
├── test-requirements.txt
└── tox.ini
easy_install xxx.tar.gz
制作 whl
bdist_wheel
命令默认不存在,需要安装如下包后,才能使用:
$ pip3 install wheel check-wheel-contents
制作包
$ python3 setup.py bdist_wheel
$ ls dist/
xxx-py3-none-any.whl
whl 包本质
$ file xxx-py3-none-any.whl
xxx-py3-none-any.whl: Zip archive data, at least v2.0 to extract, compression method=deflate
# 解压
tar -zxvf xxx-py3-none-any.whl
使用 setup.py 安装包
# 正式安装
python3 setup.py install
# 开发模式安装,软链接方式实现
python3 setup.py develop
与pip区别
python setup.py install
和pip install
都是用来安装python包的,实际上,pip提供了更多的特性,更易于使用。体现在以下几个方面:
- pip 会自动下载依赖,而如果使用 setup.py,则需要手动搜索和下载
- pip 会自动管理包的信息,使卸载/更新更加方便和容易,使用pip uninstall即可。而使用setup.py,必须手动删除,有时容易出错
- pip 提供了对 virtualenv 更好的整合
其他
Babel 语言包
Babel
是 Python
的一个国际化工具包,提供了对 distutils
或 setuptools
的支持,包含一些命令。
- compile_catalog
类似于msgfmt工具,takes a message catalog from a PO file and compiles it to a binary MO file.
$ ./setup.py compile_catalog --directory foobar/locale --locale pt_BR
running compile_catalog
compiling catalog to foobar/locale/pt_BR/LC_MESSAGES/messages.mo
- extract_messages
类似于xgettext,it can extract localizable messages from a variety of difference source files, and generate a PO (portable object) template file from the collected messages.
$ ./setup.py extract_messages --output-file foobar/locale/messages.pot
running extract_messages
extracting messages from foobar/__init__.py
extracting messages from foobar/core.py
...
writing PO template file to foobar/locale/messages.pot
- update_catalog
类似于msgmerge,it updates an existing translations catalog based on a PO template file (POT).