概述
本教程将指导你使用 uv(一个超快的 python 包管理器)来:
- 创建一个标准的 python 包项目
- 配置
pyproject.toml - 构建源码分发包(sdist)和 wheel 包
- 发布到 pypi(python package index)
为什么选择 uv?
- 极快:比 pip 快 10-100 倍
- 一体化:替代 pip、pip-tools、poetry、pyenv、twine 等多个工具
- 现代:原生支持 pyproject.toml 和最新的 python 打包标准
- 可靠:提供锁文件和可重现的环境
环境准备
1. 安装 uv
macos 和 linux:
curl -lssf https://astral.sh/uv/install.sh | sh
windows (powershell):
powershell -executionpolicy bypass -c "irm https://astral.sh/uv/install.ps1 | iex"
通过 pip 安装:
pip install uv
2. 验证安装
uv --version
3. 安装 python(如果需要)
# 安装特定版本的 python uv python install 3.12 # 查看已安装的 python 版本 uv python list
创建项目
方法一:使用 uv init 自动创建
# 创建新项目 uv init my-awesome-package # 进入项目目录 cd my-awesome-package # 项目结构会自动创建
方法二:手动创建项目结构
my-awesome-package/ ├── src/ │ └── my_awesome_package/ │ ├── __init__.py │ └── core.py ├── tests/ │ ├── __init__.py │ └── test_core.py ├── pyproject.toml ├── readme.md ├── license └── .gitignore
创建目录结构:
mkdir -p my-awesome-package/src/my_awesome_package mkdir -p my-awesome-package/tests cd my-awesome-package
pyproject.toml 配置详解
pyproject.toml 是 python 项目的核心配置文件,包含三个主要部分:
完整的 pyproject.toml 示例
[build-system]
requires = ["hatchling >= 1.26"]
build-backend = "hatchling.build"
[project]
name = "my-awesome-package"
version = "0.1.0"
authors = [
{ name = "your name", email = "your.email@example.com" }
]
maintainers = [
{ name = "your name", email = "your.email@example.com" }
]
description = "a short description of your package"
readme = "readme.md"
requires-python = ">=3.9"
license = "mit"
license-files = ["licen[cs]e*"]
keywords = ["example", "package", "tutorial"]
classifiers = [
"development status :: 3 - alpha",
"intended audience :: developers",
"license :: osi approved :: mit license",
"operating system :: os independent",
"programming language :: python :: 3",
"programming language :: python :: 3.9",
"programming language :: python :: 3.10",
"programming language :: python :: 3.11",
"programming language :: python :: 3.12",
"programming language :: python :: 3.13",
]
dependencies = [
"requests>=2.28.0",
"pydantic>=2.0.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0.0",
"pytest-cov>=4.0.0",
"ruff>=0.1.0",
"mypy>=1.0.0",
]
docs = [
"mkdocs>=1.5.0",
"mkdocs-material>=9.0.0",
]
[project.urls]
homepage = "https://github.com/yourusername/my-awesome-package"
documentation = "https://github.com/yourusername/my-awesome-package#readme"
repository = "https://github.com/yourusername/my-awesome-package"
issues = "https://github.com/yourusername/my-awesome-package/issues"
changelog = "https://github.com/yourusername/my-awesome-package/blob/main/changelog.md"
[project.scripts]
my-cli = "my_awesome_package.cli:main"
[project.gui-scripts]
my-gui = "my_awesome_package.gui:main"
[tool.hatch.build.targets.wheel]
packages = ["src/my_awesome_package"]
[tool.ruff]
line-length = 88
target-version = "py39"
[tool.ruff.lint]
select = ["e", "f", "i", "n", "w", "up"]
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-v --cov=my_awesome_package"
[tool.mypy]
python_version = "3.9"
warn_return_any = true
warn_unused_configs = true各部分详解
[build-system] - 构建系统配置
[build-system] requires = ["hatchling >= 1.26"] build-backend = "hatchling.build"
常用的构建后端:
| 构建后端 | requires | build-backend |
|---|---|---|
| hatchling (推荐) | "hatchling >= 1.26" | "hatchling.build" |
| setuptools | "setuptools >= 77.0.3" | "setuptools.build_meta" |
| flit | "flit_core >= 3.12.0, <4" | "flit_core.buildapi" |
| pdm | "pdm-backend >= 2.4.0" | "pdm.backend" |
| uv-build | "uv_build >= 0.11.7, <0.12.0" | "uv_build" |
[project] - 项目元数据
| 字段 | 说明 | 示例 |
|---|---|---|
name | 包名(pypi 上的名称) | "my-awesome-package" |
version | 版本号 | "0.1.0" |
description | 简短描述 | "a short description" |
readme | readme 文件路径 | "readme.md" |
requires-python | python 版本要求 | ">=3.9" |
license | spdx 许可证表达式 | "mit" |
dependencies | 运行时依赖 | ["requests>=2.28.0"] |
[project.urls] - 项目链接
[project.urls] homepage = "https://github.com/user/repo" documentation = "https://readthedocs.org" repository = "https://github.com/user/repo" issues = "https://github.com/user/repo/issues"
[project.scripts] - 命令行工具
[project.scripts] my-cli = "my_package.module:function"
安装后会创建一个 my-cli 命令。
[project.optional-dependencies] - 可选依赖
[project.optional-dependencies] dev = ["pytest", "ruff"] docs = ["mkdocs"]
安装时使用:pip install my-package[dev]
编写包代码
1. 创建init.py
src/my_awesome_package/init.py:
"""my awesome package - a short description.""" __version__ = "0.1.0" __author__ = "your name" __email__ = "your.email@example.com" from .core import hello, add_numbers __all__ = ["hello", "add_numbers", "__version__"]
2. 编写核心模块
src/my_awesome_package/core.py:
"""core functionality of my awesome package."""
def hello(name: str = "world") -> str:
"""say hello to someone.
args:
name: the name to greet. defaults to "world".
returns:
a greeting string.
"""
return f"hello, {name}!"
def add_numbers(a: int | float, b: int | float) -> int | float:
"""add two numbers together.
args:
a: first number.
b: second number.
returns:
the sum of a and b.
"""
return a + b
class calculator:
"""a simple calculator class."""
def __init__(self) -> none:
self.history: list[str] = []
def add(self, a: float, b: float) -> float:
"""add two numbers."""
result = a + b
self.history.append(f"{a} + {b} = {result}")
return result
def subtract(self, a: float, b: float) -> float:
"""subtract b from a."""
result = a - b
self.history.append(f"{a} - {b} = {result}")
return result
def get_history(self) -> list[str]:
"""get calculation history."""
return self.history.copy()
3. 创建 cli 入口(可选)
src/my_awesome_package/cli.py:
"""command-line interface for my awesome package."""
import argparse
import sys
from . import __version__
from .core import hello
def main() -> int:
"""main entry point for the cli."""
parser = argparse.argumentparser(
description="my awesome package cli"
)
parser.add_argument(
"--version",
action="version",
version=f"%(prog)s {__version__}"
)
parser.add_argument(
"--name",
default="world",
help="name to greet"
)
args = parser.parse_args()
print(hello(args.name))
return 0
if __name__ == "__main__":
sys.exit(main())
4. 编写测试
tests/test_core.py:
"""tests for core module."""
import pytest
from my_awesome_package.core import calculator, add_numbers, hello
class testhello:
"""tests for hello function."""
def test_hello_default(self):
assert hello() == "hello, world!"
def test_hello_with_name(self):
assert hello("alice") == "hello, alice!"
def test_hello_empty_string(self):
assert hello("") == "hello, !"
class testaddnumbers:
"""tests for add_numbers function."""
def test_add_positive_integers(self):
assert add_numbers(2, 3) == 5
def test_add_negative_integers(self):
assert add_numbers(-1, -1) == -2
def test_add_floats(self):
assert add_numbers(1.5, 2.5) == 4.0
def test_add_mixed(self):
assert add_numbers(1, 2.5) == 3.5
class testcalculator:
"""tests for calculator class."""
def test_add(self):
calc = calculator()
assert calc.add(2, 3) == 5
def test_subtract(self):
calc = calculator()
assert calc.subtract(5, 3) == 2
def test_history(self):
calc = calculator()
calc.add(1, 2)
calc.subtract(5, 3)
history = calc.get_history()
assert len(history) == 2
assert "1 + 2 = 3" in history[0]
5. 创建 readme.md
# my awesome package [](https://pypi.org/project/my-awesome-package/) [](https://pypi.org/project/my-awesome-package/) a short description of your package. ## installation ```bash pip install my-awesome-package
license
1. 使用 uv 管理依赖
--- ## 构建包 ### 1. 使用 uv 管理依赖 ```bash # 添加运行时依赖 uv add requests pydantic # 添加开发依赖 uv add --dev pytest ruff mypy # 同步依赖(安装所有依赖) uv sync # 只安装特定可选依赖 uv sync --extra dev
2. 运行测试
# 运行测试 uv run pytest # 运行测试并生成覆盖率报告 uv run pytest --cov=my_awesome_package --cov-report=html
3. 代码检查
# 运行 linter uv run ruff check . # 运行 formatter uv run ruff format . # 运行类型检查 uv run mypy src/
4. 构建分发包
# 构建 sdist 和 wheel uv build # 构建时不使用本地源码覆盖(推荐用于发布) uv build --no-sources
构建完成后,会在 dist/ 目录下生成:
dist/ ├── my_awesome_package-0.1.0.tar.gz # 源码分发包 (sdist) └── my_awesome_package-0.1.0-py3-none-any.whl # wheel 包
5. 验证构建
# 检查包的元数据 uv pip show --files dist/my_awesome_package-0.1.0-py3-none-any.whl # 使用 twine 检查(可选) pip install twine twine check dist/*
发布到 pypi
1. 注册 pypi 账号
- 访问 https://pypi.org/account/register/
- 完成注册并验证邮箱
- 启用双因素认证(推荐)
2. 生成 api token
- 登录 pypi
- 访问 https://pypi.org/manage/account/token/
- 点击 “add api token”
- 设置 token 名称和权限范围
- 立即复制并保存 token(只显示一次)
token 格式:pypi-agei...
3. 配置认证信息
方法一:环境变量(推荐)
export uv_publish_token="pypi-your-token-here"
方法二:命令行参数
uv publish --token "pypi-your-token-here"
方法三:使用 .pypirc 文件
创建 ~/.pypirc:
[pypi] username = __token__ password = pypi-your-token-here [testpypi] username = __token__ password = pypi-your-testpypi-token-here
设置文件权限:
chmod 600 ~/.pypirc
4. 发布到 pypi
# 发布所有构建产物 uv publish # 使用 token 发布 uv publish --token "pypi-your-token-here" # 发布到指定索引(需要在 pyproject.toml 中配置) uv publish --index pypi
5. 验证发布
发布成功后,访问 https://pypi.org/project/my-awesome-package/ 查看你的包。
安装测试:
# 在新环境中测试安装 uv run --with my-awesome-package --no-project -- python -c "from my_awesome_package import hello; print(hello())"
使用 testpypi 测试
在正式发布前,建议先在 testpypi 上测试。
1. 注册 testpypi 账号
访问 https://test.pypi.org/account/register/
2. 配置 testpypi 索引
在 pyproject.toml 中添加:
[[tool.uv.index]] name = "testpypi" url = "https://test.pypi.org/simple/" publish-url = "https://test.pypi.org/legacy/" explicit = true
3. 发布到 testpypi
# 生成 testpypi token 后 uv publish --index testpypi --token "pypi-your-testpypi-token"
4. 从 testpypi 安装测试
# 创建测试环境 uv venv test-env source test-env/bin/activate # linux/macos # test-env\scripts\activate # windows # 从 testpypi 安装 uv pip install --index-url https://test.pypi.org/simple/ my-awesome-package # 测试导入 python -c "from my_awesome_package import hello; print(hello())"
版本管理
使用 uv version 管理版本
# 查看当前版本 uv version # 设置精确版本 uv version 1.0.0 # 预览版本变更(不实际修改) uv version 2.0.0 --dry-run # 语义化版本升级 uv version --bump patch # 0.1.0 -> 0.1.1 uv version --bump minor # 0.1.0 -> 0.2.0 uv version --bump major # 0.1.0 -> 1.0.0 # 预发布版本 uv version --bump patch --bump beta # 0.1.0 -> 0.1.1b1 uv version --bump major --bump alpha # 0.1.0 -> 1.0.0a1 # 从预发布升级到稳定版 uv version --bump stable # 1.0.0b2 -> 1.0.0
版本号规范
遵循 语义化版本:
- major.minor.patch (例如:1.2.3)
- major: 不兼容的 api 变更
- minor: 向下兼容的功能性新增
- patch: 向下兼容的问题修正
预发布版本:1.0.0a1, 1.0.0b2, 1.0.0rc1
最佳实践
1. 项目结构
推荐使用 src 布局:
my-package/ ├── src/ │ └── my_package/ │ ├── __init__.py │ ├── core.py │ └── py.typed # pep 561 类型标记 ├── tests/ ├── pyproject.toml ├── readme.md ├── license └── .gitignore
2. 依赖管理
# 明确指定版本范围
dependencies = [
"requests>=2.28.0,<3.0.0",
"pydantic>=2.0.0",
]
# 分离开发依赖
[project.optional-dependencies]
dev = ["pytest>=7.0", "ruff>=0.1.0"]3. ci/cd 自动发布
github actions 示例 (.github/workflows/publish.yml):
name: publish to pypi
on:
release:
types: [published]
jobs:
publish:
runs-on: ubuntu-latest
permissions:
id-token: write # 用于可信发布
steps:
- uses: actions/checkout@v4
- name: install uv
uses: astral-sh/setup-uv@v4
- name: set up python
run: uv python install 3.12
- name: run tests
run: |
uv sync --extra dev
uv run pytest
- name: build package
run: uv build --no-sources
- name: publish to pypi
run: uv publish
env:
uv_publish_token: ${{ secrets.pypi_token }}4. 可信发布(trusted publishing)
pypi 支持无需 token 的可信发布:
- 在 pypi 项目设置中添加 github actions 作为可信发布者
- 配置 github actions 使用
id-token: write权限 - 不需要设置任何 token
- name: publish to pypi run: uv publish # 不需要设置 uv_publish_token
5. 发布检查清单
发布前确认:
- 更新版本号
- 更新 changelog
- 运行测试并确保通过
- 运行 linter 和类型检查
- 更新 readme(如有必要)
- 构建并验证包
- 在 testpypi 上测试
- 确认许可证文件
# 完整的发布流程 uv run pytest && \ uv run ruff check . && \ uv build --no-sources && \ twine check dist/* && \ uv publish
常见问题
q1: 包名已被占用怎么办?
- 使用更有创意的名称
- 添加前缀(如
yourname-packagename) - 检查 pypi 上是否真的需要该名称
q2: 构建失败怎么办?
# 清理构建缓存 rm -rf dist/ build/ *.egg-info # 重新构建 uv build --no-sources # 查看详细错误 uv build --verbose
q3: 如何删除已发布的版本?
pypi 不允许删除已发布的版本,只能:
- 发布新版本
- 使用
yanking标记版本为不推荐
q4: 如何处理二进制扩展?
如果包含 c/c++ 扩展,需要使用 cibuildwheel:
# github actions - name: build wheels uses: pypa/cibuildwheel@v2.16
q5: 发布后如何更新包?
# 更新版本号 uv version --bump patch # 重新构建并发布 uv build --no-sources uv publish
q6: 如何在私有仓库发布?
# pyproject.toml [[tool.uv.index]] name = "private" url = "https://private.pypi.org/simple/" publish-url = "https://private.pypi.org/legacy/"
uv publish --index private
参考资源
- uv 官方文档
- python 打包用户指南
- pyproject.toml 规范
- pypi 帮助
- 语义化版本
- spdx 许可证列表
快速参考命令
# 项目初始化 uv init my-package cd my-package # 依赖管理 uv add requests uv add --dev pytest uv sync # 运行代码 uv run python -m my_package uv run pytest # 版本管理 uv version uv version --bump patch # 构建和发布 uv build --no-sources uv publish # 发布到 testpypi uv publish --index testpypi
以上就是使用uv构建并发布python包到pypi的完整教程的详细内容,更多关于uv构建并发布python包到pypi的资料请关注代码网其它相关文章!
发表评论