创建时间: 2026-08-28最后更新: 2026-08-28

1. 职责

pyproject.toml 是现代 Python 项目的中心配置文件。uv 会用它识别项目根目录和依赖,构建工具会从中读取打包信息,FastAPI CLI、Ruff、pytest 等工具也可以在各自的配置区读取设置。

这并不意味着所有内容都属于同一种规范。一个 pyproject.toml 通常会包含下面几类配置:

区域谁定义用途
[build-system]Python 构建规范声明用什么工具构建项目
[project]Python 项目元数据规范声明名称、版本、Python 版本和依赖等信息
[dependency-groups]Python 依赖组规范声明测试、格式化等开发依赖组
[tool.*]每个工具自己定义保存 uv、FastAPI、Ruff、pytest 等工具配置

本章重点介绍项目元数据、构建系统和工具配置。[dependency-groups] 会在下一章结合依赖管理详细说明。

例如,当前 fastapi-langchain 项目的核心内容如下:

pyproject.toml
01
[project]
02
name = "fastapi-langchain"
03
version = "0.1.0"
04
description = "FastAPI service for LangChain and LangGraph examples"
05
readme = "README.md"
06
authors = [
07
{ name = "yangbo", email = "yangbo@example.com" },
08
]
09
requires-python = ">=3.13"
10
dependencies = [
11
"fastapi[standard]>=0.141.1",
12
"langchain>=1.3.16",
13
"langchain-openai>=1.6.0",
14
"pydantic-settings>=2.10.1",
15
]
16
17
[tool.fastapi]
18
entrypoint = "app.main:app"

[project] 遵循标准项目元数据规范。uv 可以直接用它管理项目;pip 安装源码项目时,则会通过构建后端获取这些元数据。[tool.fastapi] 由 FastAPI CLI 解释,Python 本身不会因为看到它就启动服务。

项目运行时的模型温度、数据库连接池大小和 CORS 来源可以放进独立的应用配置,但不建议全部塞进 pyproject.toml。pyproject.toml 更适合描述「这个项目是什么、依赖什么、开发工具如何工作」。

2. 项目字段

[project] 中最常见的字段如下:

字段含义当前项目示例
name项目或分发包名称fastapi-langchain
version项目版本0.1.0
description一句话说明FastAPI 与 LangChain 案例服务
readme项目说明文件README.md
requires-python支持的 Python 版本范围>=3.13
dependencies运行项目所需依赖FastAPI、LangChain 等
authors作者信息姓名和邮箱
licenseSPDX 许可证表达式MIT、Apache-2.0
license-files随分发包包含的许可证文件LICENSE*
keywords用于检索的关键词fastapi、langchain
classifiersPyPI 分类信息Python 版本、开发状态等

name 是项目标识,不一定与 Python 导入名完全相同。项目名常使用连字符:

project-name.toml
1
[project]
2
name = "fastapi-langchain"

Python 包目录通常使用下划线,或者像当前项目一样直接使用 app:

import-example.py
1
from app.factory import create_app

requires-python 不是「推荐版本」,而是项目兼容范围。uv 会根据它选择可用依赖;没有显式配置目标版本时,Ruff 也可以据此推断应允许哪些 Python 语法:

python-version.toml
1
[project]
2
requires-python = ">=3.13"

如果项目代码使用了 Python 3.13 才具备的能力,就不应写成 >=3.10。反过来,如果服务需要在 Python 3.11 环境部署,也不能只因为开发电脑装了 3.13 就把最低版本抬高。

version 描述项目发布版本,并应符合 PEP 440,例如 0.1.0、1.0.0rc1。内部 Web 应用即使不发布到 PyPI,也可以使用版本号标识迭代阶段,并把它传给 FastAPI 的 OpenAPI 信息。版本号不等于 Git 提交号,也不等于依赖锁文件版本。

3. 元数据

作者、许可证、链接等字段不会改变 FastAPI 的运行逻辑,但它们让项目更容易识别和维护:

metadata.toml
01
[project]
02
name = "fastapi-langchain"
03
version = "0.1.0"
04
description = "FastAPI service for LangChain and LangGraph examples"
05
readme = "README.md"
06
requires-python = ">=3.13"
07
license = "MIT"
08
license-files = ["LICENSE*"]
09
keywords = ["fastapi", "langchain", "langgraph"]
10
authors = [
11
{ name = "yangbo", email = "yangbo@example.com" },
12
]
13
14
[project.urls]
15
Repository = "https://github.com/yangbo5207/fastapi-langchain"
16
Documentation = "https://langchainpy.usehook.cn"

readme 指向的文件必须存在。项目准备构建成分发包时,错误路径会导致构建失败。

license 使用 SPDX 表达式描述授权方式。license-files 用 glob 模式声明需要放进分发包的许可证文件;准备构建项目时,应确保至少有一个实际文件与模式匹配。旧项目中可能还能看到许可证表或 License :: classifier,新项目不建议继续使用这些已弃用的写法。

authors 是由内联表组成的数组。每位作者可以提供 name、email 或同时提供两者,但至少要填写其中一项。

classifiers 是一组固定分类字符串,主要用于发布到 PyPI。普通内部 FastAPI 服务没有必要为了看起来完整而堆很多分类字段。配置应服务于实际用途,而不是追求字段数量。

有些工具支持从 Git 标签或其他文件动态计算版本。标准 [project] 通过 dynamic 声明哪些字段由构建后端提供:

dynamic-version.toml
1
[project]
2
name = "fastapi-langchain"
3
dynamic = ["version"]

如果没有配置对应构建工具,就不要使用 dynamic。初学项目直接写明 version = "0.1.0" 更容易理解。

4. 构建系统

[build-system] 告诉 Python 工具:要把当前项目构建或安装成一个包,应先安装什么构建后端,再调用哪个后端。

使用 uv 的构建后端时,可以写成:

build-system.toml
1
[build-system]
2
requires = ["uv_build>=0.12.5,<0.13"]
3
build-backend = "uv_build"

这两个字段的职责不同:

  • requires 是执行构建所需的依赖;
  • build-backend 是实际接收构建调用的 Python 对象。

什么是 wheel

wheel 是 Python 的一种已构建分发格式,文件扩展名是 .whl。它通常包含可以直接安装的 Python 代码、包元数据和资源文件。安装 wheel 时,安装工具主要负责解包并把文件放到正确位置,不需要再调用当前项目的构建后端从源码生成安装产物,因此通常比安装源码分发包更快、更稳定。

一个纯 Python wheel 的文件名可能是:

wheel-name.text
1
fastapi_langchain-0.1.0-py3-none-any.whl

文件名中的标签描述兼容范围:py3 表示支持 Python 3,none 表示不依赖特定 Python ABI,any 表示不绑定操作系统或 CPU 平台。包含 C、Rust 等本地扩展的 wheel 会带有更具体的 Python、ABI 和平台标签,不能在任意环境中通用。

wheel 与源码分发包的区别在于:wheel 已经完成构建,而源码分发包通常是 .tar.gz,安装时还需要在目标环境中调用构建后端。执行下面的命令时,构建工具一般会把两种产物放进 dist/:

build-package.bash
1
uv build

wheel 也不是独立可执行程序或 Docker 镜像。它通常不包含 Python 解释器、操作系统依赖和所有第三方依赖;项目的第三方依赖仍记录在元数据中,由 uv、pip 等安装工具继续解析和安装。

Web 服务能够通过 uv run fastapi dev 启动,不代表它一定需要构建成 wheel。如果刻意采用不安装自身包的扁平应用结构,可以使用 uv init --no-package 创建项目,此时可以不声明 [build-system]。从 uv 0.12 开始,直接执行 uv init 默认创建可打包应用,并生成 src 布局和构建系统;不要把这两种项目模式混为一谈。

下面这些场景通常需要明确构建系统:

  • 项目需要安装自身包,并采用标准 src 布局;
  • 要发布 wheel 或源码包;
  • 要使用 [project.scripts] 生成可安装命令;
  • 其他项目需要把它作为依赖安装。

uv_build 适合构建纯 Python 包。如果项目包含需要编译的本地扩展,应根据所用语言选择 Maturin、scikit-build-core 等相应的构建后端。

不要把 [build-system] 误解成 Docker 或生产部署配置。它负责 Python 包构建,不负责启动 Uvicorn、创建数据库或设置云平台环境变量。

5. 入口

Python 标准项目元数据可以通过 [project.scripts] 声明安装后可用的命令入口。使用这个区域前,项目必须声明构建系统,并采用可以被构建后端安装的包结构:

project-scripts.toml
1
[build-system]
2
requires = ["uv_build>=0.12.5,<0.13"]
3
build-backend = "uv_build"
4
5
[project.scripts]
6
inspect-api = "app.cli:inspect_application"

右侧的 app.cli:inspect_application 表示:导入 app/cli.py,调用其中的 inspect_application 函数。对应代码可以是:

app/cli.py
1
from app.main import app
2
3
4
def inspect_application() -> None:
5
print(f"{app.title} {app.version}")

然后可以执行:

run-script.bash
1
uv run inspect-api

这里必须注意:[project.scripts] 不是 package.json 的 scripts。它不能直接写成下面这样:

invalid-script.toml
1
# 错误理解:右侧不是任意 Shell 命令
2
[project.scripts]
3
dev = "fastapi dev"

右侧必须是 模块路径:可调用对象。[project.scripts] 适合暴露项目自身提供的 CLI,而不是单纯给 Shell 命令起别名。如果只想缩短开发命令,可以使用 Makefile、Just 或其他任务运行器。

FastAPI CLI 自己还支持另一种入口配置:

fastapi-entrypoint.toml
1
[tool.fastapi]
2
entrypoint = "app.main:app"

它不是新建一个 uv run dev 命令,而是告诉 fastapi dev 和 fastapi run 应该去哪里找到 FastAPI 应用:

fastapi-cli.bash
1
uv run fastapi dev

当前项目采用的正是这种方式。

6. 工具区

[tool.*] 是 pyproject.toml 最灵活的区域。点号后面的名称代表工具自己的命名空间:

tool-sections.toml
1
[tool.fastapi]
2
entrypoint = "app.main:app"
3
4
[tool.ruff]
5
line-length = 100
6
7
[tool.pytest.ini_options]
8
testpaths = ["tests"]

这里的层级关系是:

tool-result.py
1
{
2
"tool": {
3
"fastapi": {"entrypoint": "app.main:app"},
4
"ruff": {"line-length": 100},
5
"pytest": {
6
"ini_options": {"testpaths": ["tests"]},
7
},
8
},
9
}

不同工具的字段不能互换。line-length 是 Ruff 理解的字段,放进 [tool.fastapi] 不会让 FastAPI 自动限制代码行宽。

工具一般只读取自己的区域,因此同一个文件可以集中保存多个工具的设置。集中配置的好处是项目入口清晰;如果配置非常庞大,部分工具也支持独立的 TOML 文件,例如 Ruff 可以使用 ruff.toml。

选择集中还是拆分时,可以采用简单原则:常用且较短的配置放在 pyproject.toml;规模很大、需要独立继承或由不同团队维护的配置再拆出去。

配置边界

pyproject.toml 可以承载很多内容,但仍有明确边界:

内容是否适合原因
Python 版本和依赖适合属于项目定义
FastAPI 应用入口适合FastAPI CLI 官方工具配置
Ruff、pytest 配置适合开发工具配置
默认模型名称视情况更适合独立应用配置,除非仅用于开发工具
数据库密码不适合会进入版本控制
DeepSeek API Key不适合属于敏感运行环境配置
用户每次请求的温度不适合属于动态业务输入

还要区分 pyproject.toml 与 uv.lock:前者声明允许的依赖范围,后者记录 uv 实际解析出的精确版本。开发者会维护前者,uv 负责生成后者。

当项目出现问题时,可以先判断是哪一层:

  • uv sync 失败,优先检查项目依赖与 uv 配置;
  • fastapi dev 找不到应用,检查 [tool.fastapi];
  • Ruff 规则不生效,检查 [tool.ruff];
  • 服务启动后模型配置错误,检查应用 Settings 和环境变量,而不是盲目修改 [project]。
正在验证登录状态
请稍候,验证完成后将继续显示文章内容