发布自己的 python 包到 Pypi

发布自己的 python 包到 Pypi (Distributing Python Modules)

参考: section-build-and-publish

将要发布的包的目录结构如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
╰─➤  tree cocotbext-plotData
cocotbext-plotData
├── cocotbextPlotData
│   ├── __init__.py
│   ├── plotData.py
│   ├── README.org
│   └── sarasa-mono-sc-regular.ttf
└── pyproject.toml

3 directories, 8 files

真正要安装到 site-packages 的是cocotbextPlotData, 这个目录名字不能有特殊符号如 -, 否则 import 的时候会报错
可以看到不需要 setup.py 不需要 setup.cfg, 也不需要 MANIFEST.in, 只需要一个 pyproject.toml 即可

配置文件

👿注意: 以下两种方式只能选其中一种, 否则会卡在pip 安装上

pyproject.toml

这是构建包的配置文件, 内容如下:
关于如何编写 pyproject.toml 可以参考 writing-pyproject-toml
😢 注意: 使用这种方式配置, 然后pip -e 本地安装的话, 在使用LSP 补全的时候会有问题 ( 不认识这个包 ), 原因是
使用 pyproject.toml 来引导pip 以开发模式安装的包在site-packages 里使用了新的链接方式, python 的LSP server 如 ruff 暂时还不支持这种方式

这种方式是生成一个.pth 来记录源包的路径, 然后使用一个finder.py 来查找
旧的方式( 使用 setup.cfg ) 来安装的产生的是下面这种文件:

如果想以开发模式安装又有LSP 补全需求的话, 最好还是使用老的配置文件, 可以参考 setup.cfg setup.py MANIFEST.in

 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
33
34
35
36
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "cocotbextPlotData"
version = "0.1"
description = "plot sim data by dearpygui"
authors = [{name = "skfwe", email = "wcq-062821@163.com"},]
readme = {file = "README.md", content-type = "text/markdown"}
keywords = ["cocotb", "plot"]
dependencies = [
    "dearpygui",
]
requires-python = ">= 3.8"
classifiers = [
  "Development Status :: 4 - Beta",
  "Programming Language :: Python"
]

[project.urls]
Homepage = "https://example.com"
Documentation = "https://readthedocs.org"
Repository = "https://github.com/me/spam.git"
Issues = "https://github.com/me/spam/issues"
Changelog = "https://github.com/me/spam/blob/master/CHANGELOG.md"

[tool.setuptools]
# ...
# By default, include-package-data is true in pyproject.toml, so you do
# NOT have to specify this line.
include-package-data = true

[tool.setuptools.package-data]
# "*" = ["*.org", "*.ttf"]
cocotbextPlotData = ["*.md", "*.ttf"]
  • build-system

    这一项指定编译工具, 这里选择 setuptools, 除了这个还可以选择 Hatchling, Flit, PDM

  • project

    这一项主要指定项目相关的内容
    其中 readme 只支持三种格式(.md, .rst, .txt), 并不支持orgmode 格式

    description 只支持短的, 不支持long description 即在PYPI 主页显示包的时候没有长的说明

  • project.urls

    这一项指定链接, 这样在pypi 页面可以直接跳转到你的个人仓库

  • tool.setuptools

    setuptools 的配置

  • tool.setuptools.package-data

    添加资源文件
    cocotbextPlotData = [".org", “.ttf”]
    的意思是把cocotbextPlotData 文件夹下的所有 .org 和 所有 .ttf 加入到资源文件列表,
    这样在install 的时候会把这些资源文件也一起拷贝到包内
    参考: datafiles

setup.cfg setup.py MANIFEST.in

  • setup.py

    固定内容即可

    1
    2
    
    from setuptools import setup
    setup()
    
  • setup.cfg

      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
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    
    # package information
    [metadata]
    name = #当前setup.cfg 所在的目录名
    version = 0.1
    description = XXX modules for cocotb
    keywords = xxx, cocotb
    author = xxx
    author_email = example.com
    # license = MIT
    # url = https://github.com/alexforencich/cocotbext-i2c
    project_urls =
        Bug Tracker = https://example.com
        Source Code = https://example.com
    download_url = https://example.com
    
    long_description = file: README.md
    long-description-content-type = text/markdown
    platforms = any
    classifiers =
        Development Status :: 4 - Beta
        Framework :: cocotb
        Operating System :: OS Independent
        Programming Language :: Python :: 3
        Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
    
    [options]
    packages = find_namespace:
    python_requires = >=3.8
    install_requires =
        cocotb
    
    [options.extras_require]
    test =
        pytest
        cocotb-test
    
    [options.packages.find]
    include = cocotbext.*
    
    # pytest configuration
    [tool:pytest]
    testpaths =
        tests
    addopts =
        --import-mode importlib
    
    # tox configuration
    [tox:tox]
    envlist = py38, py39, py310, py311
    
    [gh-actions]
    python =
        3.8: py38
        3.9: py39
        3.10: py310
        3.11: py311
    
    [testenv]
    setenv =
        COVERAGE=1
    
    deps =
        pytest
        pytest-xdist
        cocotb-test
        coverage
        pytest-cov
    
    commands =
        pytest --cov=cocotbext --cov=tests --cov-branch -n auto
        bash -c 'find . -type f -name "\.coverage" | xargs coverage combine --append'
    
    whitelist_externals =
        bash
    
    # combine if paths are different
    [coverage:paths]
    source =
        cocotbext/
        /*/cocotbext
    
    # do not report dependencies
    [coverage:report]
    omit = 
       .tox/*
    
    # flake8 configuration
    [flake8]
    exclude = .git .tox *.egg build
    max-line-length = 119
    ignore =
        E123 # closing bracket does not match indentation of opening bracket's line
        E126 # continuation line over-indented for hanging indent
        E128 # continuation line under-indented for visual indent
        E221 # multiple spaces before operator
        E226 # missing whitespace around arithmetic operator
        E241 # multiple spaces after ','
        E262 # inline comment should start with '# '
        W503 # line break before binary operator
        W504 # line break after binary operator
    per-file-ignores =
        __init__.py:F401
    
  • MANIFEST.in

    这个文件填写需要打包的资源文件

    1
    2
    3
    
    include LICENSE
    include README.md
    recursive-include tests Makefile test_*.py test_*.v
    

包内容

__init__.py

这个文件可以是空的, 也可以加入一些import 的信息, 方便别人import

1
from .file_name import class_name

plotData.py

主程序

README.org

sarasa-mono-sc-regular.ttf

这是字体文件, 即资源文件, 如果python 包中没有用到, 可以不要

构建包

本地安装

1
2
3
4
cd cocotbext-plotData
pip3 install . --user
或者 
pip3 install . --user --break-system-packages

本地可编辑安装

1
2
3
4
cd cocotbext-plotData
pip3 install -e . --user
或者
pip3 install -e . --user --break-system-packages

这种安装方式相当于在site-packages 内建了个软链接

测试

1
2
python3
>>> from cocotbextPlotData import plotData

发布到 test Pypi

注册pypi

https://test.pypi.org/account/register/ 注册登录
注册完还要生成 recover code 和激活 2FA 才能生成 api token

  • test pypi recover code

    如果忘记密码, 可以使用上面任意一个来重新登录

  • 添加 ~/.pypirc 文件

    里面填入

    1
    2
    3
    
    [testpypi]
      username = __token__
      password = api_token
    

    password 里面填入api token, 这样使用 twine 上传包时就不需要手动输了

安装 twine

1
2
3
pip3 install twine --user
或者
pip3 install twine --user --break-system-packages

这个用于把本地编译好的包上传到 Pypi

安装 build

1
2
3
pip3 install build --user
或者
pip3 install build --user --break-system-packages

这个用于把包编译成二进制

编译二进制

1
2
cd cocotbext-plotData
python3 -m build

这会生成 dist 文件夹

发布

1
2
cd cocotbext-plotData
twine upload --repository testpypi dist/*

在testpypi 网页上可以看到

测试安装

1
2
3
pip3 install --index-url https://test.pypi.org/simple/ cocotbextPlotData
或者
pip3 install --index-url https://test.pypi.org/simple/ cocotbextPlotData --break-system-packages

发布到 PyPi

Pypi 的 Recover code 如下:

修改 ~/.pypirc

添加以下内容

1
2
3
[pypi]
  username = __token__
  password = api_token

编译二进制

1
2
cd cocotbext-plotData
python3 -m build

发布

1
2
cd cocotbext-plotData
twine upload dist/*

这时已经可以在 PyPi 看到包了, 但是还搜不到, 不清楚啥情况, 可能是延时?

测试安装

其中的pip3iu 是我写的一个函数, 相当于 pip3 install xxx -user