如何将 Python 包发布至 PyPI:从新建文件夹开始

文章目录
  1. 1. 基础动作
  2. 2. 润色一下
    1. 2.1. 本节概览
    2. 2.2. 添加 .gitignore 忽略文件
    3. 2.3. 完善 setup.py 中的项目描述
    4. 2.4. 添加项目 README.md
    5. 2.5. 添加项目依赖
  3. 3. Pytest
    1. 3.1. 本节概览
    2. 3.2. 将 Pytest 添加到开发依赖项
      1. 3.2.1. 依赖声明
    3. 3.3. 编写一个单元测试
    4. 3.4. 执行测试
  4. 4. 源代码发行(Source Distribution)
    1. 4.1. 本节概览
    2. 4.2. 源代码发行
    3. 4.3. 包含资源文件
  5. 5. 发布到 PyPI
    1. 5.1. 本节概览
    2. 5.2. 编译
    3. 5.3. 上传
  6. 6. 进阶操作
    1. 6.1. 本节概览
    2. 6.2. 使用 tox 在不同的 Python 版本中进行测试
    3. 6.3. Travis 自动编译
    4. 6.4. 用 Cookiecutter 自助生成目录结构
    5. 6.5. 其它能做的事情
    6. 6.6. 未来趋势

本文系对 Publishing (Perfect) Python Packages on PyPi - YouTube 内容的文字化归纳。

基础动作

在项目文件夹根目录下创建 src/helloworld.py ,写入如下内容:

1
2
3
4
5
def say_hello(name=None):
if name is None:
return "Hello, World!"
else:
return f"Hello, {name}!"

然后在根目录创建 setup.py

1
2
3
4
5
6
7
8
9
from setuptools import setup

setup(
name='helloworld', # 对应 pip install helloworld
version='0.0.1',
description='Say hello!',
py_modules=["helloworld"], # 对应 helloworld.py
package_dir={'': 'src'},
)

然后在根目录下运行:

1
python setup.py bdist_wheel

本地安装:

1
pip install -e .

其中 -e 表示装到 site-packages 文件夹中。

然后就可以使用这个包啦:

1
2
3
4
5
6
7
8
9
$ python

>>> from helloworld import say_hello

>>> say_hello()
'Hello, World!'

>>> say_hello("Everybody")
'Hello, Everybody!'

润色一下

本节概览

  1. 添加 .gitignore 忽略文件
  2. 添加开源许可证
  3. 完善 setup.py 中的项目描述
  4. 添加项目 README.md
  5. 添加项目依赖

添加 .gitignore 忽略文件

gitignore.io 找,应该是和 GitHub 创建项目的时候的那些忽略文件是一样的。

完善 setup.py 中的项目描述

setup.py Classifiers

添加项目 README.md

在 setup.py 中添加 README

添加项目依赖

添加项目依赖

搞完以后再跑一次 pip install -e .

Pytest

本节概览

  1. 将 Pytest 添加到开发依赖项
  2. 编写一个单元测试
  3. 执行测试

将 Pytest 添加到开发依赖项

开发依赖项

顺手更新一下 README。

更新 README

依赖声明

Install vs Extras requirements.txt

编写一个单元测试

写一个 test_helloworld.py

当前项目目录:

当前项目目录结构

执行测试

1
$ pytest

源代码发行(Source Distribution)

本节概览

  1. 源代码发行
  2. 将资源文件一并打包入待发布的包中(MANIFEST.in 文件)

源代码发行

setup.py 中添加代码仓库等信息:

1
2
3
4
5
6
setup(
...
url="https://github.com/jianggua/demo-py-package",
author="JiangGua",
author_email="[email protected]a.com",
)

进行源代码发行模式的打包:

1
$ python setup.py sdist

dist/xxx.tar.gz 中,好像少了 LICENSE.txttest_helloworld.py 等文件?

包含资源文件

1
2
3
4
5
$ pip install check-manifest

$ check-manifest --create

$ git add MANIFEST.in

发布到 PyPI

本节概览

  1. 编译(或称打包)
  2. 上传

编译

1
2
3
4
$ python setup.py bdist_wheel sdist

$ ls dist/
...

上传

1
2
3
4
# Stick this in 'extras_require'
$ pip install twine

$ twine upload dist/*

进阶操作

本节概览

  1. [使用 tox 在不同的 Python 版本中进行测试](#使用-tox-在不同的 Python 版本中进行测试)
  2. 配置 Travis 自动编译
  3. 用 Cookiecutter 自助生成目录结构
  4. 其它能做的事情
  5. 未来趋势

使用 tox 在不同的 Python 版本中进行测试

在项目根目录创建 tox.ini

1
2
3
4
5
6
[tox]
envlist = py36,py37

[testenv]
deps = pytest
commands = pytest

执行测试:

1
2
$ pip install tox
$ tox

Travis 自动编译

在项目根目录创建 .travis.yml

1
2
3
4
5
6
7
8
9
10
11
12
language: python
sudo: false

python:
- "3.6"
- "3.7-dev"

install:
- pip install tox

script:
- tox -v -e py

用 Cookiecutter 自助生成目录结构

1
2
$ pip install cookiecutter
$ cookiecutter gh:ionelmc/cookiecutter-pylibrary

Cookiecutter 生成的目录结构

其它能做的事情

  • 徽章!
    • Code Coverage (Coveralls, codecov.io)
    • Quality Metrics (Code Climate, Landscape.io)
  • Manage versioning with bumpversion
  • Test on macOS & Windows
  • More Documentation
    • Contributors Section
    • Code of Conduct

未来趋势

  • 将元数据(metadata)从 setup.py 移到 setup.cfg
  • 迁移到 pyproject.toml
    • Poetry (cookiecutter & virtualenv & setup.py)
    • Flit (setup.py)
    • Hatch (cookiecutter & virtualenv & twine)