1. 分发工具 setuptools
由于 distutils 无法定义包之间的依赖关系,Python 研究人员开发了增强版的打包工具 setuptools 以便更好的创建和分发 python 包。setuptools 通过添加基本的依赖系统和相关功能,提供了自动包查询程序,用来自动获取包之间的依赖关系,并完成这些包的安装,大大降低了安装各种包的难度。
通常安装 python 时会自带 setuptools,如果没有可以使用 pip 安装。setuptools 简单易用,只需写一个简短的 setup.py
安装文件,就可以将开发完成的 Python 应用打包。
$ pip install setuptools
2. 使用安装包创建 wheel 文件
在当前目录下新建文件 setup.py,然后创建包 hello 模拟需要打包分发的源码包
.
+-- hello
| +-- hello_world.py
| +-- __init__.py
+-- setup.py
.
├── hello
│ ├── hello_world.py # hello function
│ └── __init__.py
└── setup.py
一个最简单的 setup.py 文件内容如下:
from setuptools import setup
setup(
name = 'firstPKG',
version = '0.01',
packages=['hello'],
)
编辑 hello_world.py
文件
def hello():
print('Hello world, welcome to setuptools!')
wheel 是官方现在推荐的打包方式,首先安装 wheel
$ pip install wheel
然后使用 bdist_wheel 打包
$ python setup.py bdist_wheel
执行成功后,目录下除了 dist 和 *.egg-info 目录外,还有一个 build 目录用于存储打包中间数据。dist 文件夹中 wheel 包的名称如 firstPKG-0.0.1-py3-none-any.whl
,其中 py3 指明只支持 Python3。
可以使用参数 --universal
,包名如 firstPKG-0.0.1-py2.py3-none-any.whl
,表明 wheel 包同时支持 Python2 和 Python3。使用 universal 也成为通用 wheel 包,反之称为纯 wheel 包。
$ python setup.py bdist_wheel --universal
最终的目录树如下:
.
├── build
│ ├── bdist.linux-x86_64
│ └── lib
│ └── my_pkgs
│ ├── greet.py
│ └── __init__.py
├── dist
│ └── firstPKG-0.0.1-py2.py3-none-any.whl
├── firstPKG.egg-info
│ ├── dependency_links.txt
│ ├── PKG-INFO
│ ├── SOURCES.txt
│ └── top_level.txt
├── my_pkgs
│ ├── greet.py
│ ├── __init__.py
│ └── __pycache__
│ ├── greet.cpython-36.pyc
│ └── __init__.cpython-36.pyc
└── setup.py
3. 安装 wheel 文件
创建 wheel 文件之后,使用 pip 安装到本地 Python 的 site-packages 目录。
$ cd dist
$ pip install firstPKG-0.0.1-py2.py3-none-any.whl
进入 python 交互界面,就可以使用安装好的包
>>> import my_pkgs
>>> from my_pkgs import greet
>>> from my_pkgs.greet import hello
>>> hello
<function hello at 0x7fe2cd92b598>
>>> hello()
Hello, welcome to setuptools!
应用开发过程中会频繁变更,每次安装都需要先卸载旧版本很麻烦。如果使用 develop
开发模式安装,实际代码不会拷贝到 site-packages 目录下,而是在 site-packages 目录下生成一个指向当前应用的链接 firstPKG.egg-link
,使得当前位置的源码改动能够立刻反映到 site-packages 中。
$ python setup.py develop
卸载安装好的包
$ pip uninstall firstPKG
4. 将 wheel 文件上传到 PyPI
wheel 包可以自己使用和传输给其他人使用,但是维护更新不方便,而 PyPI 作为 Python 的软件仓库,可以让所有人方便的上传和下载,以及管理三方库。
首先登录 PyPI官网,注册账号。虽然 setuptools 支持使用 setup.py upload
上传包文件到 PyPI,但只支持 HTTP 而被新的 twine 取代。
$ pip install twine
$ twine upload dist/*
输入 username 和 password 即上传至 PyPI。如果不想每次输入账号密码,可以在当前目录下创建 .pypirc 文件,内容如下:
[distutils]
index-servers =
pypi
pypitest
[pypi]
username:
password:
[pypitest]
repository: https://test.pypi.org/legacy/
username:
password:
填上自己的账号密码即可,这里配置了官方的 pypi 和 pypitest,若要配置其他仓库,按格式添加。上传成功之后即回到 PyPI官网 用户主页即可看到上传的 firstPKG-0.0.1
文件。
PyPI 主页显示会有延迟,所以不能马上搜索到结果,pip search 也可能 搜索不到,但已经可以使用 pip 安装。
5. setup() 参数详细解释
5.1 完整的 setup.py 示例
from setuptools import setup
from setuptools import find_packages
with open('README.rst', 'r', encoding='utf-8') as rd:
long_description = rd.read()
setup(
# 在 PyPI 上搜索的项目名称。
name="HelloWorld",
# 项目版本号,一般由三部分组成:MAJOR, MINOR, MAINTENANCE
version="0.0.1",
# 列出项目内需要被打包的所有 package。一般使用 setuptools.find_packages() 自动发现
# exclude 用于排除不打包的 package
packages=find_packages(exclude=['contrib', 'docs', 'tests*']),
# Project uses reStructuredText, so ensure that the docutils get
# installed or upgraded on the target machine
# 项目依赖的 Python 库,使用 pip 安装本项目时会自动检查和安装依赖。
install_requires=['docutils>=0.3'],
# 指定项目依赖的 Python 版本
python_requires='>=3',
# 项目依赖数据文件,数据文件必须放在项目目录内且使用相对路径。
# 如果不指定作为目录的键为空串,则代表对所有模块操作
package_data={
# If any package contains *.txt or *.rst files, include them:
'': ['*.txt', '*.rst'],
# And include any *.msg files found in the 'hello' package, too:
'hello': ['*.msg'],
},
# 如果数据文件存在于项目外,则可以使用 data_files 参数或者 MANIFEST.in 文件进行管理。
# 如果用于源码包,则使用 MANIFEST.in;如果用于 wheel,则使用 data_files。
# 上述设置将在打包 wheel 时,将 data/conf.yml 文件添加至 mydata 目录。
data_files=[(‘mydata’, [‘data/conf.yml’])],
# metadata for upload to PyPI
# 作者信息
author="example",
author_email="example@example.com",
# 项目的简短描述,一般一句话就好,会显示在 PyPI 上名字下端。
description="This is an Example Package",
# 对项目的完整描述,使用 long_description。如果此字符串是 rst 格式的,PyPI 会自动渲染成 HTML 显示。
long_description = long_description,
# 项目许可证
license="GNU GPLv3",
# 项目关键词列表
keywords="hello world example examples",
# project home page 通常为 GitHub上 的链接或者 readthedocs 的链接。
url="http://example.com/HelloWorld/",
# 项目相关额外连接,如代码仓库,文档地址等
project_urls={
"Bug Tracker": "https://bugs.example.com/HelloWorld/",
"Documentation": "https://docs.example.com/HelloWorld/",
"Source Code": "https://code.example.com/HelloWorld/",
}
)
5.2 其他初始化文件
在阅读 Github 上的 Python 库时,除了最基本核心的 setup.py 文件和主程序之外,还会看到其他一些文件。本节将介绍它们的作用和使用方法。
(1) setup.cfg
包含了构建时候的一些默认参数,例如在使用 bdist_wheel 的时候设置默认的 —universal 参数。
[bdist_wheel]
universal = 1
(2) README.rst/README.md
项目说明文档,使用 reStrutruedText 可以在 PyPI 上很好的渲染,但 Markdown 则支持不够好。
(3) MANIFEST.in
决定 setuptools 打包源码时还需要额外打包哪些文件。
# Include the README
include *.md
# Include the license file
include LICENSE.txt
# Include the data files
recursive-include data *
(4) LICENSE.txt
项目许可说明文件
setuptools 默认打包的文件包括 README.rst/README.md
、setup.cfg
和 MANIFEST.in
,所有其他文件,如 LICENSE.txt
, 使用源码包时需要在 MANIFEST.in 里添加 include,使用 wheel 包时需要在 setup.cfg 添加
[metadata]
license_file = LICENSE.txt
PyPI 上传推荐配置
.
├── 项目文件夹
├── LICENSE.txt
├── MANIFEST.in
├── README.rst
├── setup.cfg
└── setup.py
-- name
-- version
-- author
-- author_email
-- url
-- packages
-- description
-- package_data/data_files