如何发布一个Python包

发布流程

如何将一个Python包发布到PyPI呢?官方文档在这里

打包

  • 方式一:使用轮子(wheels)发布

    这种方式发布的是编译好的包,因此安装会更快,是更推荐的做法。

    # 1. 先安装wheel
    $ pip install wheel
    
    # 2.1 如果源码是纯Python而且同时支持2和3,则
    $ python setup.py bdist_wheel --universal
    
    # 2.2 如果只支持特定版本或平台,则使用对应版本执行
    $ python setup.py bdist_wheel
  • 方式二:直接使用源码发布

    这种方式发布的包在安装的时候需要临时编译,所以比较慢,但是安装包会比较小

    $ python setup.py sdist

准备PyPI账号

首先要有个账号:可以在这里注册。

然后写入~/.pypirc

[distutils]
index-servers=pypi

[pypi]
repository = https://upload.pypi.org/legacy/
username = <username>
;password = <password>

上传

推荐使用twine上传。

# 安装twine
$ pip install twine

# 上传打包好的文件
$ twine upload dist/*

scope

不像 NPM 那么强大,PyPI 并不支持 scope,所以给包取名字的时候,我们需要自己考虑命名冲突的问题。

PEP 420 允许给包名指定命名空间(Implicit Namespace Packages)。即使这样,也并没有限定账号,所以其实这个命名空间还是大家都可以用的,只是方便管理。这时,我们可以给自己的包名加上账号前缀作为命名空间,毕竟一般来说,不会有人在自己的包名前面加上别人的账号当前缀吧。

发布有命名空间的包步骤如下:

  1. 包名添加前缀,如 gera2ld-pyserve

    值得注意的是,PEP 503 中提出了包名规范化,会把包名中的特殊字符都替换成 - 再处理,而且在最新的 pip 中,包名中出现其他特殊字符会导致安装失败,所以我们使用 - 来连接前缀。

  2. 文件路径结果增加一层命名空间层,如下:

    ▾ gera2ld/
      # 注:这里不能有 __init__.py
      ▾ pyserve/
          __init__.py

    注意命名空间目录下不能有 __init__.py。只有这样,才能多个包共用一个 namespace 互不影响。

  3. 修改 setup.py,手动指定子包的位置:

    setup(
        name='gera2ld-pyserve',
        packages=['gera2ld.pyserve'],
    )

    因为 gera2ld 目录下没有 __init__.pysetuptools.find_packages 无法自动找到子包,所以需要手动指定 packages 。

  4. 接下来就可以按上文所述的步骤正常发布和使用了。

第三方包管理工具

pip 是 Python 自带的包管理工具,它存在一些问题,早已被 Node.js、Rust 等语言的包管理工具超越,所以也涌现了一些新的包管理工具,学习了其他语言的长处,不过他们底层都是基于 pip 的,所以仍然会有一些限制。

Poetry 就是一个很好的第三方包管理工具,它可以自动创建 virtualenv,支持像 NPM 那样使用当前安装的环境执行脚本,发布版本。它使用了和 NPM 类似的配置方式,所有的项目配置都存在 pyproject.toml 中。

通过 Poetry,我们可以更简单地发布一个包:

  1. 更新 pyproject.toml,升级版本号 version

    $ poetry version 1.0.0
    $ git add .
    $ git commit -m v1.0.0
    $ git tag -m v1.0.0 v1.0.0
  2. 代码编译和打包:

    $ poetry build
  3. 发布:

    $ poetry publish

© 2020