前言

最近几年,Python 变得更加火热,今年的 TIOBE 年度编程语言很大概率又将被 Python 拿下,而这将是 Python 的 TIOBE 年度编程语言的四连冠,与此同时,今年 GitHub Octoverse 编程语言趋势中 Python 也上升到第二位,在 Stackoverflow 年度报告中最流行的编程语言也排名第三。Python 似乎人人都会,已经成为了一个必备的技能了,本文将围绕着如何发布自己的 Python 库进行说明。

库 VS 框架

简单做了张图说明了一下库和框架的区别,库就是由你的代码进行调用的,框架则调用你的代码。

XTRqTH

DS_Store_Cleaner

既然搞清楚了库和框架的区别,本文就以我之前写的一个用于清除 .DS_Store 文件的 Python 库来说明发布 Python 库的流程。

首先简单介绍下 DS_Store_Cleaner 这个项目,该项目是用于删除当前目录或指定目录下所有 .DS_Store 文件的工具,那么一个需求就是可以直接使用命令行调用该程序,如 dsclean ~/Desktop 即可删除桌面上的所有 .DS_Store 文件。DS_Store_Cleaner 项目结构如下:

1
2
3
4
5
6
7
8
.
├── DS_Store_Cleaner
│ ├── DS_Store_Cleaner.py # 主程序代码
│ ├── __init__.py # 用于表明这是一个 Python Package
│ └── __main__.py # 用于处理命令行方法
├── LICENSE # 开源证书
├── README.md
└── setup.py # Package 配置信息

也可以采用 requests 作者写的 setup.py (for humans),进行简单地修改。setup.py 是比较关键的信息,但并不复杂,这是我的全部内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import setuptools


setuptools.setup(
name='DS_Store_Cleaner', # 项目名称
version='0.2', # 项目版本
author='VXenomac', # 作者名
author_email='vxenomac@xyzlab.ai', # 作者邮箱
description='删除当前目录或指定目录下所有的 .DS_Store 文件', # 简单描述
url='https://github.com/VXenomac/DS_Store_Cleaner',
packages=setuptools.find_packages(),
classifiers=[
'Development Status :: 3 - Alpha',
'Programming Language :: Python :: 3',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent'
],
entry_points={
'console_scripts': [
'dsclean = DS_Store_Cleaner.__main__:main' # 命令行入口
]
}
)

DS_Store_Cleaner.py 的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import os
from pathlib import Path


def remove_file(path):
"""
根据文件路径删除文件
:param path: 指定需要删除 ,DS_Store 的路径
"""
path = Path(path)
files = [f for f in path.rglob('.DS_Store')]
for file_path in files:
print('正在删除 %s…' % file_path)
os.remove(file_path)
print('一共删除 %d 个 .DS_Store 文件…' % len(files))

__main__.py 的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import sys
from .DS_Store_Cleaner import *


def main():
"""
接受命令行路径,若没有参数则默认传递当前路径
"""
if len(sys.argv) > 1:
remove_file(sys.argv[1])
else:
remove_file(os.getcwd())

if __name__ == '__main__':
main()

至此,一个用于删除 .DS_Store 文件的工具已经完成了,并且可以通过命令行进行调用,在进行发布之前,再介绍一下 PyPi,也就是要发布我们 Python 库的地方。

PyPi

简单说 PyPi(Python Package Index)就是用于托管 Python 第三方库的网站,在上面发布 Python 库需要注册账号,因此发布自己的 Python 库需要注册一个 PyPi 的账号。另外,PyPi 近年来有发生一些被黑客上传了恶意代码,或是用相近的第三方库名称伪造,在使用方面需要甄别,减少遇到恶意事件的情况。

twine 是用于方便上传自己的库至 PyPi 的工具,但我觉得可以直接使用 GitHub Actions,免去自己构建的步骤,更加优雅,因此不再赘述 twine 的使用方式。

GitHub Actions

当我们将代码上传至 GitHub 之后,我们可以选择构建一个 Actions,发布 Python 包的工作流已经被 GitHub 官方所支持了,因此会特别方便,只需要将代码同步至 GitHub,便可触发构建并于 PyPi 发布。如果你对 GitHub 还不熟悉,可以查看之前的文章了解一下。

螢幕截圖 2020-12-06 22.35.26

在点击 Publish Python Package 的 Set up this workflow 之后生成的 GitHub Actions 的配置文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# This workflows will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries

name: Upload Python Package

on:
push:
branches:
- master

jobs:
deploy:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*

:::danger

我们可以看到在 env 下有两个字段,TWINE_USERNAMETWINE_PASSWORD,千万不要直接填写 PyPi 的账号密码,因为这是不安全的

:::

GitHub Actions 的配置文件在仓库公开的时候,是人人可以看的,因此我们需要在当前仓库的设置中添加环境变量来获取较安全的方式:

螢幕截圖 2020-12-06 22.45.52

可以使用 ${{ secrets.变量名 }} 来调用设置内的 Secrets 变量,当然需要有相同的变量名才可以。