python 发布包_Python包发布指南

本文详细介绍了如何构建和发布Python包,包括使用setuptools创建setup.py文件,配置基本信息、依赖、元数据等,以及打包、安装、配置文件的使用。通过设置项目元数据、依赖关系和入口点,开发者可以将自己的代码打包成Python库,并上传到PyPI供他人使用。
摘要由CSDN通过智能技术生成

前言

一门编程语言的强大,有一点在于社区是否活跃,相关库是否够多。主流的编程语言都有非常强大的包管理工具和便捷的库下载方式,Python 就有 pip 工具,一行命令就可以下载所需要的依赖库。

pip install requests

俗话说得好:轮子用的好,头发不会少。但是当我们开发到一定的阶段,还是经常会发现没有趁手的库可以用,或者一些业务代码过于冗余,需要提取抽象,那就可以自己开发依赖库。一来减少重复劳动,避免代码的复制粘贴,二来贡献开源社区,也是给需要的人做贡献。

下面就谈谈如何从头开始构建自己的 Python 包。

打包

Python 包需要发布,第一步就是打包。好比货物要出售,就需要一套标准化的包装流程,保证货物交付的可靠性。

相比与手动的代码复制方式,打包有如下好处:代码包不需要手动复制

版本管理,避免复制代码混乱

可使用 pip 工具直接安装

所以 Python 有一套非常完善的打包工具 setuptools,使用也是非常简单,我们从一个项目入手。

假设我们的项目目录如下:

my_project

|- my_package

|- __init__.py

|- main.py

my_project 是我们的项目根目录,my_package 是我们的包根目录,下面只有一个模块 main.py。

要使用 setuptools,需要创建一个 setup.py 打包配置文件,放在项目根目录下。

内容如下

from setuptools import setup

from setuptools import find_packages

VERSION = '0.1.0'

setup(

name='Flask-Board', # package name

version=VERSION, # package version

description='my package', # package description

packages=find_packages(),

zip_safe=False,

)

通过添加这么一个简单的配置文件,我们的项目就可以变身称为一个 Python 包了。

执行构建

python setup.py build

会将包的内容构建到 build 文件夹下。

执行安装

python setup.py install

会将包直接安装到当前解释器的 site-packages 下,安装完成后即可以使用 pip list 命令查看到。

Python 库的打包就这么简单?不过实际情况下我们需要更多的配置,下面我们来看看主要的配置方式。

配置

下面我将主要配置分为几类,详细讲解,基本可以涵盖大部分使用场景,可作为快速指南使用。

基本信息name:包名称

version:包版本

url:主页地址

project_urls:包相关网页地址,字典格式,对应关系见下图

author:作者名字

author_email:作者邮箱

maintainer:维护者名字

maintainer_email:维护者邮箱

classifiers:分类信息

license:使用的开源许可

description:简短描述

long_description:详细描述

long_description_content_type:详细描述的格式

keywords:关键词

platforms:支持的操作系统

和 pypi.org 上的信息对应关系如下。

name: 1

version: 2

description: 3

long_description: 4

url 和 project_urls: 5

Meta 侧栏对应 author,author_email,maintainer,maintainer_email,license,keywords, python_requires(下面依赖配置中)等信息。

这整一块都是 classifiers 信息。

常用场景

URL

项目前期比较简单,只有 github 地址,一般只配置 url,对应页面只显示 Homepage。

项目完善后,可能有独立的主页,Github 代码页,文档页等。url 可以配置项目的主页,project_urls 配置其他页面,如下所示。

project_urls={

"Documentation": "https://flask.palletsprojects.com/",

"Code": "https://github.com/pallets/flask",

"Issue tracker": "https://github.com/pallets/flask/issues",

}

详细描述配置

项目的详细描述往往很长,可以使用一个单独的文件描述,pypi 默认使用 rst 格式渲染。

with open('README.rst') as f:

LONG_DESCRIPTION = f.read()

setup(

name='my-package',

version='0.1.0',

description='short description',

long_description=LONG_DESCRIPTION,

# ...

)

不过,因为 Github 默认使用 README.md 文件作为项目的详细描述,我们也可以重复利用,markdown 的语法更简单。

with open('README.md') as f:

LONG_DESCRIPTION = f.read()

setup(

name='my-package',

version='0.1.0',

description='short description',

long_description=LONG_DESCRIPTION,

long_description_content_type='text/markdown',

# ...

)

long_description_content_type 配置可以指定 long_description 的渲染格式,支持的值是:text/plain

text/x-rst

text/markdown

分类信息

classifiers 配置主要用来帮助 pypi 更好的分类和索引包,同时告诉其他人包相关特点。双冒号前面是分类的名称,后面是分类的值,包含了包的各个方面,视情况填写就行。这里可以看到所有的分类列表。

classifiers=[

"Development Status :: 5 - Production/Stable",

"Environment :: Web Environment",

"Framework :: Flask",

"Intended Audience :: Developers",

"License :: OSI Approved :: BSD License",

"Operating System :: OS Independent",

"Programming Language :: Python",

"Programming Language :: Python :: 2",

"Programming Language :: Python :: 2.7",

"Programming Language :: Python :: 3",

"Programming Language :: Python :: 3.5",

"Programming Language :: Python :: 3.6",

"Programming Language :: Python :: 3.7",

"Programming Language :: Python :: 3.8",

"Programming Language :: Python :: Implementation :: CPython",

"Programming Language :: Python :: Implementation :: PyPy",

"Topic :: Internet :: WWW/HTTP :: Dynamic Content",

"Topic :: Internet :: WWW/HTTP :: WSGI :: Application",

"Topic :: Software Development :: Libraries :: Application Frameworks",

"Topic :: Software Development :: Libraries :: Python Modules",

]

依赖信息install_requires:依赖的其他库列表,安装该库之前也会安装

extras_require:其他的可选依赖库,安装该库不会自动安装

setup_requires:构建依赖的库,不会安装到解释器库,安装到本地临时目录

python_requires:Python 版本依赖

use_2to3:布尔值,True 则自动将 Python2 的代码转换为 Python3

这些主要是配置依赖信息,常用的主要就是 install_requires,配置该库依赖的其他库。

setup(

...

python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",

install_requires=[

"Werkzeug>=0.15",

"Jinja2>=2.10.1",

"itsdangerous>=0.24",

"click>=5.1",

],

extras_require={

"dotenv": ["python-dotenv"],

"dev": [

"pytest",

"coverage",

"tox",

"sphinx",

"pallets-sphinx-themes",

"sphinxcontrib-log-cabinet",

"sphinx-issues",

],

"docs": [

"sphinx",

"pallets-sphinx-themes",

"sphinxcontrib-log-cabinet",

"sphinx-issues",

],

},

)

常用场景

特定 Python 版本依赖

如果一些依赖是只有某些 Python 版本才需要的,可以这样指定

setup(

...

install_requires=[

"enum34;python_version

]

)

特定操作系统依赖

如果一些依赖是特定操作系统才需要安装的,可以这样指定

setup(

...

install_requires=[

"pywin32 >= 1.0;platform_system=='Windows'"

]

)

功能管理packages:该库包含的 Python 包

package_dir:字典配置包的目录

package_data:配置包的其他数据文件

include_package_data:布尔值,为 True 则根据 MANIFEST.in 文件自动引入数据文件

exclude_package_data:字典配置需要移除的数据文件

zip_safe:布尔值,表明这个库能否安全的使用 zip 安装和执行

entry_points:库的入口点配置,可用来做命令行工具和插件

这些配置主要用来指定那些文件需要打包,哪些不需要,以及打包的行为等。

常用场景

包文件配置

让 setuptools 自动搜索包文件,使用 find_packages 工具函数即可。

from setuptools import setup

from setuptools import find_packages

setup(

...

packages=find_packages(),

)

会自动引入当前目录下的所有 Python 包(即包含 __init__.py 的文件夹),只会自动引入 py 文件,不会引入所有的文件。

如果所有的包需要统一放置在一个独立的目录下,例如 src,如下所示的目录结构

my_project

|- src

|- my_package

|- __init__.py

|- main.py

setup.py

可以如下配置

from setuptools import setup

from setuptools import find_packages

setup(

...

packages=find_packages("src"),

package_dir={"": "src"},

)

引入其他的数据文件

默认只会引入满足条件文件(例如 py),如果需要引入其他的文件,例如 txt 等文件,需要配置导入数据文件。

setup(

...

package_data={

# 引入任何包下面的 *.txt、*.rst 文件

"": ["*.txt", "*.rst"],

# 引入 hello 包下面的 *.msg 文件

"hello": ["*.msg"],

},

)

通过 MANIFEST.in 文件配置

setup(

include_package_data=True,

# 不引入 README.txt 文件

exclude_package_data={"": ["README.txt"]},

)

MANIFEST.in 文件位于 setup.py 同级的项目根目录上,内容类似下面。

include CHANGES.rst

graft docs

prune docs/_build

有如下几种语法include pat1 pat2 ...:引入所有匹配后面正则表达式的文件

exclude pat1 pat2 ...:不引入所有匹配后面正则表达式的文件

recursive-include dir-pattern pat1 pat2 ...:递归引入匹配 dir-pattern 目录下匹配后面正则表达式的文件

recursive-exclude dir-pattern pat1 pat2 ...:递归不引入匹配 dir-pattern 目录下匹配后面正则表达式的文件

global-include pat1 pat2 ...:引入源码树中所有匹配后面正则表达式的文件,无论文件在哪里

global-exclude pat1 pat2 ...:不引入源码树中所有匹配后面正则表达式的文件,无论文件在哪里

graft dir-pattern:引入匹配 dir-pattern 正则表达式的目录下的所有文件

prune dir-pattern:不引入匹配 dir-pattern 正则表达式的目录下的所有文件

添加命令

如果需要用户安装库之后添加一些命令,例如 flask 安装之后添加了 flask 命令,可以使用 entry_points 方便的配置。

setup(

...

entry_points={

"console_scripts": ["flask = flask.cli:main"]

},

)

console_scripts 键用来配置命令行的命令,等号前面的 flask 是命令的名称,等号后面是模块名:方法名。

setup(

...

entry_points={

"console_scripts": [

"foo = my_package.some_module:main_func",

"bar = other_module:some_func",

],

"gui_scripts": [

"baz = my_package_gui:start_func",

]

}

)

自动发现插件

entry_points 还可以用开开发插件,在无需修改其他库的情况下,插入额外的功能。

插件库在 setup.py 中的 entry_points 中定义插件入口。

setup(

...

entry_points={

"console_scripts": [

"foo = my_package.some_module:main_func",

],

}

)

而主体库可以通过 pkg_resources 遍历获取同一组的 entry_points,

from pkg_resources import iter_entry_points

group = 'console_scripts'

for entry_point in iter_entry_points(group):

fun = entry_point.load()

print(fun)

这里的 fun 就是所有定义在 entry_points 上的类或者方法。

这样就可以在主体类不变更的情况下,轻松实现插件的插入,Flask 就是利用这个机制实现自定义命令扩展的。

setup(

...

entry_points={

'flask.commands': [

'test=my_package.commands:cli'

],

},

)

而对应 Flask 库中有如下代码自动载入命令。

def _load_plugin_commands(self):

if self._loaded_plugin_commands:

return

try:

import pkg_resources

except ImportError:

self._loaded_plugin_commands = True

return

for ep in pkg_resources.iter_entry_points("flask.commands"):

self.add_command(ep.load(), ep.name)

self._loaded_plugin_commands = True

配置文件

setuptools 同时还支持配置文件来配置,在 setup.py 文件同级的项目根目录下创建 setup.cfg 文件。

配置内容同上,只是按照 cfg 配置文件的格式,加上一些分块,同时支持一些特殊的语法。相对于 setup.py 中配置,更利于阅读和管理,但是缺少了灵活性。

[metadata]

name = my_package

version = attr: src.VERSION

description = My package description

long_description = file: README.rst, CHANGELOG.rst, LICENSE.rst

keywords = one, two

license = BSD 3-Clause License

classifiers =

Framework :: Django

License :: OSI Approved :: BSD License

Programming Language :: Python :: 3

Programming Language :: Python :: 3.5

[options]

zip_safe = False

include_package_data = True

packages = find:

install_requires =

requests

importlib; python_version == "2.6"

发布

打包配置完成后就是发布我们的库了。

打包成 tar 包

python setup.py sdist

安装 wheel 库后可以打包成 whl 包

安装 wheel

pip install wheel

打包 whl

python setup.py bdist_wheel

打包完后的包可以直接通过 pip 安装

pip install

如果我们需要包被全世界的同好通过 pip install 直接安装的话,需要将包上传到 pypi 网站。首先注册 pypi,获得用户名和密码。

上传 tar 包

python setup.py sdist upload

上传 whl 包

python setup.py bdist_wheel upload

如果要更安全和方便地上传包就使用 twine 上传。

安装 twine

pip install twine

上传所有包

twine upload dist/*

如果嫌每次输入用户名和密码麻烦可以配置到文件中。

编辑用户目录下的 .pypirc 文件,输入

[pypi]

username=your_username

password=your_password

好了,我们就可以尽情发布我们开发的 Python 包了。

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值