如果你像我一样,有时写了一个工具库,并想把它分享给其他人,最好的方法就是将其制作成PyPi的包,因为它易于安装和分享。
如果你像我一样,觉得制作包很麻烦,看完这篇文章,你会觉得竟如此简单,主要三个步骤(当然还有一堆可选步骤)。
下面开始吧。
1. 创建包文件
我们以创建podsearch
为例,这是一个在iTunes上进行搜索的工具包,首先创建文件夹和虚拟环境:
$ mkdir podsearch
$ cd podsearch
$ python3 -m venv env
$ . env/bin/activate
创建包的最小结构:
.
├── .gitignore
└── podsearch
└── __init__.py
"""Let's find some podcasts!"""
__version__ = "0.1.0"
def search(name, count=5):
"""Search podcast by name."""
raise NotImplementedError()
2. 发布到测试库
以前制作python包很麻烦,Flit的出现简化了步骤,首先安装Flit:
pip install flit
然后创建包描述:
$ flit init
Module name [podsearch]: # 包名
Author [Anton Zhiyanov]: # 作者
Author email [[email protected]]: # 邮箱
Home page [https://github.com/nalgeon/podsearch-py]: # 作者主页
Choose a license (see http://choosealicense.com/ for more info)
1. MIT - simple and permissive
2. Apache - explicitly grants patent rights
3. GPL - ensures that code based on this is shared with the same terms
4. Skip - choose a license later
Enter 1-4 [1]: 1 # 选择开源协议,不知道怎么选择可以看http://choosealicense.com/
# 也可以编辑pyproject.toml文件增加其他可选项。
Flit创建的pyproject.toml
是包的元数据文件,里面包含发布到PyPI所需要的信息。
注册TestPyPi(测试库)账号和PyPI(正式库) ,两个系统是独立的,所以需要注册两个账号。
编辑~/.pypirc
文件,设置刚才注册的账号信息:
[distutils]
index-servers =
pypi
pypitest
[pypi]
username: nalgeon # 替换成你的PyPI用户名
[pypitest]
repository: https://test.pypi.org/legacy/
username: nalgeon # 替换成你的TestPyPI用户名
然后发布到测试库中:
$ flit publish --repository pypitest
Found 4 files tracked in git
...
Package is at https://test.pypi.org/project/podsearch/
完成!然后可以去TestPyPi(你自己的包地址在上述命令行输出的最后)查看刚发布的包。
3. 发布正式库
修改代码,使其真正支持搜索 podcast :
# ...
SEARCH_URL = "https://itunes.apple.com/search"
@dataclass
class Podcast:
"""Podcast metadata."""
id: str
name: str
author: str
url: str
feed: Optional[str] = None
category: Optional[str] = None
image: Optional[str] = None
def search(name: str, limit: int = 5) -> List[Podcast]:
"""Search podcast by name."""
params = {"term": name, "limit": limit, "media": "podcast"}
response = _get(url=SEARCH_URL, params=params)
return _parse(response)
然后发布到正式PyPI库。当你的代码测试无误后再执行此步骤,不要发布无意义的代码。
flit publish
发布完成后,就可以分享你的包地址了。
为了让你的包更愉快地使用,我推荐再执行以下几个步骤。
a. Readme和更新记录
没人愿意写文档,但是没有使用说明,一般不会有人愿意使用你的包,所以,还是增加README.md
和CHANGELOG.md
吧。
在pyproject.toml
中声明README.md
,这样在发布到PyPI后,会在项目主页展示你的说明信息:
description-file = "README.md"
增加python最低运行版本:
requires-python = ">=3.7"
在__init__.py
中增加版本信息,并通过flit publish
命令发布。
b. 代码检查和测试
格式化代码(black)、测试覆盖率(coverage),代码质量(flake8、pylint、McCabe),静态分析(mypy),这些都可以用tox
统一运行。
$ pip install black coverage flake8 mccabe mypy pylint pytest tox
创建tox的配置文件tox.ini
:
[tox]
isolated_build = True
envlist = py37,py38,py39
[testenv]
deps =
black
coverage
flake8
mccabe
mypy
pylint
pytest
commands =
black podsearch
flake8 podsearch
pylint podsearch
mypy podsearch
coverage erase
coverage run --include=podsearch/* -m pytest -ra
coverage report -m
然后执行:
$ tox -e py39
...
py39 run-test: commands[0] | black podsearch
All done! ✨ 🍰 ✨
...
py39 run-test: commands[2] | pylint podsearch
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)
...
py39 run-test: commands[6] | coverage report -m
TOTAL 100%
...
py39: commands succeeded
congratulations :)
非常完美!单元测试通过!覆盖率100%!
c. 云打包
每个健壮的开源项目在提交commit后都会在云端执行测试,“副作用”就是在readme中生成好看的徽章。
接下来我们使用Github Actions打包,使用Codecov检查测试覆盖率,使用Code Climate检查代码质量。
后两者都需要注册账号,当然也可以通过GitHub账号登陆。
添加Github Actions的配置文件.github/workflows/build.yml
:
# ...
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7, 3.8, 3.9]
env:
USING_COVERAGE: "3.9"
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install black coverage flake8 flit mccabe mypy pylint pytest tox tox-gh-actions
- name: Run tox
run: |
python -m tox
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
if: contains(env.USING_COVERAGE, matrix.python-version)
with:
fail_ci_if_error: true
tox-gh-actions
和USING_COVERAGE
参数确保tox和Github Actions使用相同的python版本,strategy.matrix
这种用法我是跟Hynek Schlawak学的。
最后一步是通过Codecov执行测试覆盖率,Code Climate不需要单独配置,它会自动发现仓库的变更并自动执行。
commit、push,等待几分钟,然后就可以在README.md
中查看徽章了:
[![PyPI Version][pypi-image]][pypi-url]
[![Build Status][build-image]][build-url]
[![Code Coverage][coverage-image]][coverage-url]
[![Code Quality][quality-image]][quality-url]
...
<!-- Badges -->
[pypi-image]: https://img.shields.io/pypi/v/podsearch
[pypi-url]: https://pypi.org/project/podsearch/
[build-image]: https://github.com/nalgeon/podsearch-py/actions/workflows/build.yml/badge.svg
[build-url]: https://github.com/nalgeon/podsearch-py/actions/workflows/build.yml
[coverage-image]: https://codecov.io/gh/nalgeon/podsearch-py/branch/main/graph/badge.svg
[coverage-url]: https://codecov.io/gh/nalgeon/podsearch-py
[quality-image]: https://api.codeclimate.com/v1/badges/3130fa0ba3b7993fbf0a/maintainability
[quality-url]: https://codeclimate.com/github/nalgeon/podsearch-py
是不是很好看?
d. 自动化
tox已经很好了,但是对于开发来说还是有些不方便。单独运行pylint、coverage命令会更快,但却十分麻烦,所以我们把这个无聊的过程自动化。
对于频繁使用的命令,可以使用Makefile
创建简短的别名:
.DEFAULT_GOAL := help
.PHONY: coverage deps help lint push test
coverage: ## Run tests with coverage
coverage erase
coverage run --include=podsearch/* -m pytest -ra
coverage report -m
deps: ## Install dependencies
pip install black coverage flake8 mccabe mypy pylint pytest tox
lint: ## Lint and static-check
flake8 podsearch
pylint podsearch
mypy podsearch
push: ## Push code with tags
git push && git push --tags
test: ## Run tests
pytest -ra
以下是所有命令:
$ make help
Usage: make [task]
task help
------ ----
coverage Run tests with coverage
deps Install dependencies
lint Lint and static-check
push Push code with tags
test Run tests
help Show help message
根据DRY原则(don't repeat yourself,不要做重复的事),使用make
命令替换build.yml
中的原始命令:
- name: Install dependencies
run: |
make deps
- name: Run tox
run: |
make tox
e. 云发布
在Github Actions中配置flit publish
的工作流:
name: publish
on:
release:
types: [created]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.9"
- name: Install dependencies
run: |
make deps
- name: Publish to PyPi
env:
FLIT_USERNAME: ${{ secrets.PYPI_USERNAME }}
FLIT_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
make publish
PYPI_USERNAME
和 PYPI_PASSWORD
在Github仓库中设置(Settings > Secrets > New repository secret)为你的PyPi用户名和密码,当然还有更好的方式——API token。
现在,当你发布新版本后,GitHub会自动发布包到Pypi上。
整洁的代码、清晰的文档、单元测试、自动打包,一个包含完美流程的包诞生了,现在可以分享给你的小伙伴了~
原文地址:https://antonz.org/python-packaging/
翻译:花墨
(本文发布已获得原作者许可)
本文由 花墨 创作,采用 知识共享署名4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。