2021 年发布 Python 软件包的正确姿势!

共 14032字,需浏览 29分钟

 ·

2021-04-24 21:17

如果您像我一样,偶尔编写一个有用的python实用小程序,并希望与您的同事共享。做到这一点的最好方法是制作一个Python软件包:它易于安装,并且可以避免进行拷贝操作。
您可能会认为创建软件包很麻烦。其实现在已经不会这样了。我将通过此分步指南进行说明。只需执行三个主要步骤(以及一系列可选步骤),并辅以几个GitHub链接即可。
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 (https://flit.readthedocs.io/en/latest/)小程序可以简化所有操作。让我们安装它:
pip install flit
并创建软件包描述:
$ flit init
Module name [podsearch]:
Author [Anton Zhiyanov]:
Author email [m@antonz.org]:
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

Written pyproject.toml; edit that file to add optional extra info.

pyproject.toml

Flit已创建pyproject.toml - 项目元数据文件。它已经具有将程序包发布到公共存储库-PyPI所需的一切。
注册TestPyPi(测试存储库)和PyPI(主要存储库)。它们是完全独立的,因此您将需要两个帐户。
~/ .pypirc中设置对存储库的访问权限:
[distutils]
index-servers =
  pypi
  pypitest

[pypi]
username: nalgeon  # replace with your PyPI username

[pypitest]
repository: https://test.pypi.org/legacy/
username: nalgeon  # replace with your TestPyPI username
并将软件包发布到测试存储库:
$ flit publish --repository pypitest
Found 4 files tracked in git
...
Package is at https://test.pypi.org/project/podsearch/
完毕!该软件包可在TestPyPi上获得。
3.公开软件包
让我们改进代码,以便它能够实际搜索播客:

# ...

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和变更日志changelog
没人喜欢写文档。但是,如果没有文档,人们不太可能会想要安装您的软件包,因此我们需要添加README.mdCHANGELOG.md
  • README.md
  • CHANGELOG.md
将README添加到pyproject.toml,以便PyPI在软件包页面上显示它:
description-file = "README.md"
还要指定受支持的最低Python版本:
requires-python = ">=3.7"
更新__init__.py中的版本,并通过flit publish发布软件包:

B.Linterstests
我们来考虑一下格式设置(black),测试覆盖率(coverage),代码质量(flake8pylintmccabe)和静态分析(mypy)。我们将通过tox处理一切。
$ pip install black coverage flake8 mccabe mypy pylint pytest tox
tox.ini中创建tox配置:
[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.ini
并运行所有检查:
$ 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 :)
linters检测通过,测试也通过了,覆盖率是100%。
C.云构建
每个可靠的开源项目在每次提交后都会进行云测试,因此我们也将这样做。一个很好的附加效果是自述文件中有漂亮的徽章。
让我们使用GitHub Actions构建项目,使用Codecov检查测试覆盖率,并使用Code Climate检查代码质量。
您将必须注册CodecovCode 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: $

            - 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
build.yml
就像我们前面一样,GitHub通过tox进行测试。tox-gh-actions软件包和USING_COVERAGE设置可确保tox使用与strategy.matrix所需的 GitHub Actions 相同的 Python 版本。
最后一步将测试覆盖率发送给CodecovCode Climate不需要单独的步骤-它会自动发现存储库更改。
现在,一分钟内提交,推送并享受结果。并且让每个人也喜欢向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很好,但对于开发来说不是很方便。运行单个命令(例如pylintcoverage等)的速度更快。但是它们非常冗长,因此我们将一些无意义的操作进行自动化处理。
让我们为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
Makefile
这是我们的任务:

$
 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
为了使代码更简洁,请使用make调用替换原始的build.yml步骤:
- name: Install dependencies
  run: |
      make deps

- name: Run tox
  run: |
      make tox
E.云发布
GitHub有能力为我们运行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

publish.yml
在存储库设置(Settings > Secrets > New repository secret)中设置了PYPI_USERNAMEPYPI_PASSWORD。使用您的PyPi用户名和密码,甚至更好的-API令牌。
现在,一旦创建新版本,GitHub将自动发布该软件包。
您的软件包已准备就绪!它具有人们梦寐以求的一切:干净的代码,清晰的文档,测试和云构建。是时候告诉你的同事和朋友了。
这些设置将使您的 Python 软件包变得AWESOME:
  • pyproject.toml
  • tox.ini
  • Makefile
  • build.yml
  • publish.yml

更多阅读



5 分钟快速上手 pytest 测试框架


5分钟掌握 Python 随机爬山算法


5分钟快速掌握 Adam 优化算法

特别推荐




点击下方阅读原文加入社区会员

浏览 25
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报