用Python写过不少的脚本,现在要把脚本打包成模块并进行发布,然后才明白脚本Boy到正规的码农差距有多大= =。 踩了很多天的坑之后,自己学习到了Python的包分发机制,以及如何利用Pypi向全世界分发自己的模块。现在简单地做一些整理。
Python包机制
包是一个模块或模块/子模块的集合,一般情况下被压缩到一个压缩包中。 其中包含
- 依赖信息
- 将文件拷贝到标准的包搜索路径的指令。
- 编译指令(如果在安装前代码必须被编译的话)。
也就是说,为了分发模块,我们需要把模块的依赖信息和模块一起打包。在Python中,这个打包好的可分发的文件一般以.egg
结尾,其作用可以理解为java中的jar。Python的包管理以及分发曾经经历过非常混乱的一段时期,但是如今已经基本稳定(或者说,流行?)为两个套件:
- 打包&分发:Setuptools
- 安装&管理:pip
准备工作
注册Pypi
Pypi - the Python Package Index Pypi是Python语言包的仓库,全世界所有开源Python开发者都会在Pypi上提交&下载软件包
为了我们后续的提交操作,我们需要首先注册一个Pypi的账号,注册非常简单,提供用户名,密码以及邮箱,经过验证之后就注册完成了。
目录结构
Python没有严格的工程目录要求,只要有__init__.py
在的地方,就会被认为是一个Python的包。但是出于方便协作考虑,可以把自己的源代码与各种脚本分开存放。具体的结构可以学习Github上比较流行的Python项目,选择自己喜欢的即可。
环境配置
首先你需要有pip,pip自从3.4
版本开始已经随python内置发布,如果使用的版本比较低,可以自己手动进行安装:
sudo apt-get install python-pip
然后我们需要安装setuptools
:
pip install setuptools
编写安装脚本
准备工作就绪之后,我们就可以开始编写安装脚本了。
填写配置信息
基本框架
from setuptools import setup, find_packages # 引入setuptools包
setup(
option = values, # 本质上是一个函数的参数,分行写便于维护
)
参数介绍
指定整个包
指定单独模块
依赖关系
包内数据
其他数据
Meta-Data
Meta-Data | 描述值 |
---|---|
name | 包名 |
version | 此次发布版本 |
author | 作者名 |
author_email | 作者邮箱 |
maintainer | 维护者名 |
maintainer_email | 维护者邮箱 |
url | 主页 |
description | 简要描述 |
long_description | 详细描述 |
download_url | 下载地址 |
classifiers | 分类,参见此处 |
platforms | 平台列表 |
license | 授权协议 |
典型配置
from setuptools import setup, find_packages
setup(
name = "HelloWorld",
version = "0.1",
packages = find_packages(),
scripts = ['say_hello.py'],
# Project uses reStructuredText, so ensure that the docutils get
# installed or upgraded on the target machine
install_requires = ['docutils>=0.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'],
},
# metadata for upload to PyPI
author = "Me",
author_email = "me@example.com",
description = "This is an Example Package",
license = "PSF",
keywords = "hello world example examples",
url = "http://example.com/HelloWorld/", # project home page, if any
# could also include long_description, download_url, classifiers, etc.
)
打包
这个坑踩了很久- -,没有老司机的带的痛苦 与开发环境不同的时候,当用户运行你的包时,使用open等命令是以当前目录为根运行的,所以你必须指定数据所在位置,否则会出现IOError甚至更糟糕的情况
指定需要分发的文件
自动处理
当没有MANIFEST.in
文件时,Setuptools将会按照下面的原则处理文件:
- 所有
py_modules
和packages
选项包含的Python源文件 - 所有
ext_modules
或libraries
选项指定的C源文件 scripts
指定的脚本(参见Installing Scripts)- 形如
test/test*.py
的文件 README.txt
(或README
),setup.py
和setup.cfg
- 符合
package_data
选项的所有文件(参见Installing Package Data) - 符合
data_files
选项的所有文件(参见Installing Additional Files)
翻译自Python官方文档 Specifying the files to distribute
手动处理
一般而言,自动处理已经足够,但是如果想要自己指定的话,则需要编辑MANIFEST.in
模板文件。
MANIFEST.in
模板文件很简单,每一行都导入或者导出表示符合正则的一类文件。比如说:
# 导入根目录下满足*.txt的文件
include *.txt
# 递归导入examples目录下满足*.txt和*.py的文件
recursive-include examples *.txt *.py
# 导入满足examples/sample?/build的文件夹下所有文件
prune examples/sample?/build
详细语法参见此处
注意: 当根目录下存在
MANIFEST.in
文件时,Setuptools将不会再采用自动处理的设定,因此需要在MANIFEST.in
文件中指明所有需要导入的文件。
调用数据
当你需要调用Python包中的文件时,你可以使用下面的方法:
from pkg_resources import resource_string
data = resource_string(__name__, 'data.dat')
此时,指定的data.dat
文件将会以二进制文件流的形式赋值到data变量中,你可以按照自己的需要进行进一步处理。比如说:
from pkg_resources import resource_string
data = resource_string(__name__, 'example.yml')
with open('config.yml', 'w') as f:
f.write(str(data, encoding='utf-8'))
f.close()
上述代码实现了从example.yml
中读取数据并保存到config.yml
文件中。
创建源码分发包
在包的根目录下执行:
python setup.py sdist
默认情况下,sdist
命令将会为Unix创建gzip
压缩文件,为Windows创建zip
压缩文件
你也可以添加参数--formats=zip
指定生成的文件类型,所有支持的参数见此处
源码分发似乎不会导入
package_data
中指定的数据,如果上传本分发包可能导致用户通过pip
安装的包中没有需要的数据。
创建二进制分发包
除了创建源码分发之外,我们还可以创建基于平台的二进制分发包。 在包的根目录下执行:
python setup.py bdist
默认情况下,这个命令将会创建基于自身平台的分发包。
同样的,你也可以添加--format=zip
参数来指定生成的文件,支持的参数见此处
除此之外,也可以使用以下的命令直接生成对应格式的分发包:
命令 | 格式 |
---|---|
bdist_dumb | tar, gztar, bztar, xztar, ztar, zip |
bdist_rpm | rpm, srpm |
bdist_wininst | wininst |
bdist_msi | msi |
这一命令无法跨平台, 在Linux上选择制作
wininst
分发包时会提示缺乏相应的支持。
创建wheel分发包
wheel是新出的分发格式,旨在取代egg,你可以通过下列命令进行安装:
pip install wheel
然后在包的根目录下执行:
python setup.ppy bdist_wheel
上传到Pypi
注册包
在上传我们的包之前,我们需要首先向Pypi提交包的相关信息。 在包的根目录下执行:
python setup.py register
没有登陆的话,需要进行登陆;如果已经登陆,直接回车使用默认设置即可。
上传包
注册完毕后,我们可以提交我们的包了。 在包的根目录下执行:
python setup.py sdist bdist_wininst upload
这条命令将会向Pypi提交源码和Win下的安装包,如果需要上传别的包,只要直接写出即可。
参考资料
更新日志
- 2015年11月03日 初步完成
- 2016年03月30日 修复了部分笔误,添加了一些注释,增加了wheel相关的内容