怎么将自己写的包打包上传到PyPi供其他人下载
我们先装好依赖的包
打包和上传依赖wheel和twine这两个包。
pip install whell
pip install twine
先准备好项目
比方说我们想写这样一个包,这个包只有一个功能,那就是打印程序员佛祖字符画。
# __init__.py
buddha = ...
def print_buddha():
print(buddha)
这个包就提供了一个佛祖字符画和一个打印函数。
# __main__.py
from . import print_buddha
print_buddha()
__main__.py
让这个包可以被python -m
直接运行,而__main__.py
其实也就只干了一件事,那就是调用print_buddha
打印字符画。
打包项目
打包项目你需要学习以下知识
- 创建
setup.py
- 打包wheel
- 用twine上传的PyPi
一个标准的包的目录结构
packaging_tutorial
├── LICENSE # 开源协议证书
├── README.md # 说明文件
├── * your_pkg1
│ └── __init__.py
├── * your_pkg2
│ └── __init__.py
├──*setup.py # 顾名思义了
└── tests # 单元测试
加星的是必须的,其他的可有可无不影响打包和上传PyPi,就是不规范而已。
创建setup.py
setup.py是setuptools的构建脚本。它告诉setuptools有关您的软件包(例如名称和版本)以及要包括的代码文件的信息。
最基本的setup.py
# -*- coding: utf-8 -*-
from setuptools import setup, find_packages
try:
long_description = open("README.md").read()
except IOError:
long_description = ""
setup(
name="Buddha",
version="0.1.0",
description="A pip package",
license="MIT",
author="你的名字",
packages=find_packages(),
long_description=long_description,
classifiers=[
"Programming Language :: Python",
"Programming Language :: Python :: 3.6",
]
)
然后我们试一下打包:
python setup.py sdist bdist_wheel
这是个组合命令,意思是先打sdist,再打bdist_wheel。
sdist打出来的是tar.gz格式压缩包,里面就是包源文件,可以供直接解压然后python setup.py install
安装。
bdist_wheel打出来的是whl格式文件,这是pip官方安装包文件格式。
注意:egg格式已经过时了,现在PyPi的包都是用whl格式。
什么是egg?
Wheel和Egg都是打包格式,旨在支持不需要构建或编译的安装工件的用例,这在测试和生产工作流程中可能会耗费大量成本。
python setup.py bdist_egg 就可以打出egg格式的包。
Egg格式由setuptools于2004年推出,而Wheel格式由PEP 427于2012年推出。
目前,Wheel被认为是Python构建和二进制打包的标准。
这是Wheel和Egg之间重要区别的细分。
PiPy上传规范是同时要tar.gz和whl两个格式的包。
setup.py参数详解
注意:打了星的是必须参数
描述性参数 —— 提供包信息,供PiPy识别管理。
参数 | 类型 | 说明 |
---|---|---|
*name | str | 包名称 |
*version | str | 包版本 |
*author | str | 程序的作者,这个包从头到尾都是你先开发的,那么你就是原作者。 |
*author_email | str | 程序的作者的邮箱地址 |
maintainer[^maintainer] | str | 维护者,如果你不是原作者,这个包是你修改原作者的包,那么你就是维护者。 |
maintainer_email | str | 维护者的邮箱地址 |
*url | str | 程序的官网地址 |
license | str | 程序的授权信息 |
description | str | 程序的简单描述 |
long_description | str | 程序的详细描述,详细描述在包上传PyPi后会在包页面下显示,支持RST和MD两种格式。 |
platforms | str | 程序适用的软件平台列表 |
classifiers | str | 程序的所属分类列表。影响上传PyPi会被分类到哪个类别。 |
keywords | str | 程序的关键字列表。同上。 |
download_url | str | 程序的下载地址 |
描述性参数只是作为PKG_INFO用的,没有什么特殊作用,就是描述用的,用来供pip和PyPi管理的,因此看上面就可以了。
包文件搜集 —— 设置包的哪些文件需要打包,怎么找这些文件。(很重要)
参数 | 说明 |
---|---|
*package_dir | 用来给setuptools 指示packages 里的包名要从哪些目录下找,默认是setup.py所在的根目录下找packages。 |
*packages | 打包的包目录(通常为包含 __init__.py 的文件夹)。可以手动一个个包名添加,也可以直接用find_package 自动找指定目录下所有带 __init__.py的文件夹。默认只将文件夹内所有的.py 文件打包。如果文件夹内有其他类型文件,并且包依赖这些文件,需要通过设置package_data 来声明要打包的文件/类型。搜集的文件夹会被安装到site-packages 目录下。 |
*package_data | 指定包内需要包含的数据文件类型。默认packages只会把.py文件打包进去,如果包依赖其他类型文件就需要在package_data 里声明要打包进去的文件类型。 |
include_package_data | 如果设置为True,这将告诉setuptools自动将它找到的所有数据文件包含在MANIFEST.in文件指定的软件包目录中。MANIFEST.in可通过setuptool插件自动跟踪版本控制工具生成。 |
exclude_package_data | 将包名称映射到应该从包目录中排除的全局模式列表的字典。您可以使用它来修剪include_package_data包含的所有多余文件。有关完整的描述和示例,请参见“包括数据文件”部分。这个参数服务于include_package_data。 |
*py_modules | 需要打包的 Python 单文件列表。如果模块只是一个py文件,那么添加到这里打包进去。注意只需要写文件名,不要带.py后缀。 |
*data_files | 打包时需要打包的数据文件,如图片,配置文件等。格式("路径","文件"),路径是PYTHON_HOME目录下的相对路径。 |
scripts | 指定可执行脚本,安装时脚本会被安装到系统PATH路径下(PYTHON_HOME\Scripts)。注意,一般是指命令行脚本,只有这种脚本安装到系统PATH路径下可以直接在命令行里调用。 |
setup.py 需要提供以上参数数据用来搜集需要打到包里的文件:
注意:打了星的是重要参数
根据上面的说明,我们来演练一下。首先项目要做些修改和添加一些功能。
- 我们把包的源码都放到src目录下了。
- 在
buddha
模块里增加一个buddha.txt
的文本用来存我们的佛祖字符画,并将buddha模块里写死的字符画改为读这个文件。 - 增加一个buddha.bat脚本,这个脚本里面就一行
python -m buddha
,调用buddha
模块打印字符画,让buddha包支持命令行直接调用,就省去输入python -m
的麻烦。 - 增加一个字符画txt文本,里面就是佛祖字符画,这个文本就当做是个文档,我们会放到PYTHON_HOME的Doc文件夹里。
- 增加一个
single_module
,试一下单文件模块怎么打包到包里。
接着修改setup.py:
setup(
# 包信息
...
# 包搜集
package_dir={"":"src"},
packages=find_packages("src"),
package_data={"buddha":["*.txt"]},
py_modules=["single_module"],
package_data={"buddha":["*.txt"]}
data_files=[("Doc",["src/buddha_doc.txt"])],
scripts=["src/buddha.bat"],
)
-
find_packages
需要指定从遍历哪个文件夹下的包。你可能会有疑问,为什么设置了package_dir
还需要指明,不是会默认从src
目录下找吗?其实是这样的,find_package
这个函数只是返回一个字符串数组,在我们的项目里返回的就是["buddha"]
。如果你不用find_package
那么就是手写["buddha"]
,find_package
只是个工具函数,省去你手写的麻烦,但是并不是setuptools
内部的机制,find_package
不认package_dir
。setuptools
最终会用packages
的字符串数组到package_dir
去找包,然后copy
到build
文件夹,最终一起打到dist
目录下生成二进制whl文件。 -
packages
默认只搜集py文件,buddha.txt
不会被打到包里,因此需要在package_data里指明。 - 我们的
single_module
是一个文件模块,packages
只处理文件夹模块。因此要用py_modules
参数来添加。 - 我们有个
buddha_doc.txt
字符画文本,我们把它当做文档,希望包安装的时候能够被放到PYTHON_HOME目录下的Doc文件夹里。
(这本身没什么意义,只是演示怎么将自己的文件安装到自己希望的位置,如果你了解pth
文件你就知道将文件安装到自己想要的位置有什么意义。) - 我们有个
buddha.bat
脚本,希望其能够被安装到PYTHON_HOME/Scripts
目录下,这样命令行就能直接调用。
注意:data_files
、scripts
这两个参数不享受package_dir
的效果,必须指定根目录下相对路径。
打包看看:
python setup.py sdist bdist_wheel
试一下安装:
pip isntall dist\buddha-0.1.0-py3-none-any.whl
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Processing c:\users\administrator\desktop\pylab\dist\buddha-0.1.0-py3-none-any.whl
Installing collected packages: buddha
Successfully installed buddha-0.1.0
我们去PYTHON_HOME看看我们的包被安装到哪里。
我们的包各种文件都被安装到了我们想要的位置。
试一下命令行调用buddha.bat
:
python里导入buddha
模块:
python里导入single_module
模块:
依赖
参数 | 说明 |
---|---|
ext_modules | 指定c扩展模块 |
指定依赖的其他包。你的包依赖了其他外部包就添加到这里,安装你的包的时候会一并安装。(已过时,被install_requires代替了) | |
install_requires | 安装时需要安装的依赖包,同上。 |
setup_requires | 指定运行 setup.py 文件本身所依赖的包。如果你在setup.py里面引用了依赖的包,就需要添加到这里。 |
extras_require | 当前包的高级/额外特性需要依赖的分发包。extras你可以理解为可选外部依赖,比如你的包有导出数据功能,默认支持csv格式,如果要导出excel格式则需要安装openxlrd,那么openxlrd就是可选外部依赖。 |
指定可以为哪些模块提供依赖。pip已经忽略这个参数了。 | |
dependency_links | 指定依赖包的下载地址。pip已经不支持了。 |
命令行
参数 | 类型 | 说明 |
---|---|---|
cmdclass | Dict[str, class] | 添加自定义命令到setup.py命令行界面。 |
entry_points | Dict[str,str] | 动态发现服务和插件 |
- cmdclass 比较少用到。我们知道setup.py有build install两个子命令,sdist和bdist_wheel也是其子命令。cmdclass可以让我们自己自定义setup.py的子命令。推荐文章《How To Add Custom Build Steps and Commands To setup.py》
- entry_points,某种意义上讲,跟前面的
scripts
参数很像,其作用就是为了公开一个命令给控制台。
例子:
entry_points={
"console_scripts": [
"print_buddha = buddha:print_buddha",
],
"gui_scripts": [
"single_module = sindle_module:a_module",
]
}
- print_buddha是要生成的命令名,调用pring_buddha会去执行buddha模块里的print_buddha函数。
- entry_point 格式是 命令名=模块路径(与import路径一致):函数名。
- 包安装后会在Scripts目录下生成一个print_buddha.exe,这也是为什么我们可以直接在控制台调用print_buddha的原因。
- console_scripts和gui_scripts的区别在于console_scripts下生成的exe调用的时候会用标准输入输出重定向到控制台,用起来就跟用命令一样;而gui_scripts生成的exe则不会重定向到控制台,控制台上调用了什么都不会打印,因此主要用来启动一个gui程序,例如用tkinter写的界面。
- entry_points的作用有两个,当我们的入口函数不是模块的main.py而是某个py文件里的函数的时候,entry_points可以那个函数直接暴露到命令行。另一个作用是公开入口点给其他
压缩选项
参数 | 类型 | 说明 |
---|---|---|
zip_safe | bool | 不压缩包,而是以目录的形式安装。这个选项在打egg格式的包的时候有用,现在都用whl不用egg了。 |
更加详细的说明可以去参考官方文档《setuptools documeng: Building and Distributing Packages with Setuptools》
上传包到PyPi
注册PyPi账号
到PyPi官网注册一个账号,通过各种验证通。
上传PyPi
python -m twine upload dist/*
将dist下的tar.gz和whl文件上传到PyPi。
会提示输入用户名密码,照着做就行。
上传成功后就会出现在PyPi和你的项目页里。
更新版本
你无法重新上传覆盖版本,所以如果你的包有bug或者有修改,你必须修改setup.py里的version,然后再打一次包上传。