使用 uv 创建项目、管理依赖和区分环境
uv 统一管理 Python、虚拟环境和项目依赖
本篇的项目依赖统一使用 uv 管理:
uv python安装和选择 Python。- 项目虚拟环境统一放在项目根目录的
.venv中。 uv add、uv remove安装和删除依赖,并自动维护pyproject.toml、uv.lock。uv run使用项目环境运行命令,不需要手动激活虚拟环境。uv sync根据项目配置重建或同步环境。
以下命令均在 Windows PowerShell 中演示。
在 Conda 虚拟环境中安装 uv
Conda 虚拟环境默认通常不包含 uv。 环境中已经安装 Python 或 pip,不代表 uv 也已安装。如果准备从已有的 Conda 环境使用 uv,需要先激活环境并单独安装。
已有 Conda 环境时,将下面的 fastapi-uv 换成实际环境名称;如果还没有环境,可以先创建:
conda create -n fastapi-uv python=3.11 pip -y
conda activate fastapi-uv在当前 Conda 环境中通过 pip 安装 uv:
python -m pip install uv
uv --version
python -m pip show uvpython -m pip install uv将 uv 安装到当前 Python 对应的 Conda 环境。python -m pip show uv用来确认当前环境是否确实安装了 uv。- 单独执行
uv --version能找到命令,不一定代表 uv 安装在当前 Conda 环境中,因为系统也可能找到全局安装的 uv。
如果更希望通过 conda 安装,也可以在激活环境后使用 conda-forge:
conda install -c conda-forge uv -ypip 和 conda-forge 两种方式选择一种即可,不需要重复安装。不使用 Conda 时,可以跳过本节,采用后面的 uv 独立安装方式。
需要注意:本篇后面的 uv add、uv sync 和 uv run 默认管理项目根目录下的 .venv,不会因为 uv 是从 Conda 环境中启动的,就把项目依赖安装进 Conda 环境。这里 Conda 环境主要用于提供 uv 命令,项目依赖仍由 uv 独立管理。
新建项目
1. 安装 uv 并检查版本
已经安装 uv 时,直接检查:
uv --version如果没有通过上面的 Conda 方式安装 uv,Windows 也可以使用官方 PowerShell 安装脚本独立安装:
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"安装后重新打开终端,再执行 uv --version。已经通过 Conda 安装并确认 uv 可用时,不需要再执行这个独立安装脚本。
2. 使用 uv 安装 Python
以 Python 3.11 为例:
uv python install 3.11
uv python listuv python install 安装由 uv 管理的 Python;uv python list 查看可用版本。
3. 初始化项目并固定 Python 版本
mkdir fastapi-demo
cd fastapi-demo
uv init --no-package
uv python pin 3.11uv init --no-package创建pyproject.toml等项目文件,但不把项目配置成需要构建发布的 Python 包。uv python pin 3.11将项目使用的 Python 版本写入.python-version。- 后续在项目根目录执行
uv add、uv sync或uv run时,uv 会使用项目自己的.venv,无需手动激活。
检查项目实际使用的 Python:
uv run python --version4. 使用 uv 安装 FastAPI
在项目根目录执行:
uv add "fastapi[standard]" "pydantic>=2,<3" uvicorn这条命令会安装依赖,同时更新 pyproject.toml 和 uv.lock。
fastapi[standard]安装 FastAPI 及常用标准扩展,其中包含运行开发服务器所需的 Uvicorn。pydantic>=2,<3明确使用 Pydantic 2,并限制不跨入下一个主版本。
检查安装结果:
uv run python -c "import fastapi, pydantic; print('FastAPI:', fastapi.__version__); print('Pydantic:', pydantic.__version__)"后续启动 FastAPI 开发服务器时,同样通过 uv 运行:
uv run uvicorn main:app --reload常用 uv 包管理命令
| 命令 | 作用 |
|---|---|
uv add requests | 安装运行依赖,并更新项目声明、锁文件和环境 |
uv add "requests==2.32.3" | 安装指定版本 |
uv add --dev pytest | 安装开发依赖,例如测试工具 |
uv remove requests | 删除依赖,并同步更新项目声明、锁文件和环境 |
uv sync | 按 pyproject.toml 和 uv.lock 创建或同步 .venv |
uv lock | 重新解析依赖并更新 uv.lock |
uv run python main.py | 使用项目环境运行 Python |
uv tree | 查看项目依赖树 |
uv pip list | 查看当前项目环境中已安装的包 |
日常项目开发优先使用 uv add 和 uv remove,不要只执行 uv pip install、uv pip uninstall。后两条命令属于 uv 提供的 pip 兼容接口,只修改环境,不会自动维护 pyproject.toml 和 uv.lock,容易造成“本机能运行,但项目声明中没有依赖”的问题。
查看、导出和恢复依赖
查看依赖
uv tree
uv pip listuv tree 更适合查看项目依赖及其间接依赖;uv pip list 类似传统的包列表。
导出 requirements.txt
如果其他工具只接受 requirements.txt,可以从 uv 锁文件导出:
uv export --format requirements-txt --locked --no-dev -o requirements.txt--locked要求使用现有锁文件,避免导出时悄悄改变锁定结果。--no-dev不导出测试、格式化等开发依赖;需要完整开发环境时可以去掉。-o requirements.txt指定输出文件。
uv 项目迁移时,优先携带 pyproject.toml、uv.lock 和 .python-version,不必把 requirements.txt 当作唯一依赖来源。
在新机器上恢复项目
如果项目中有 pyproject.toml 和 uv.lock,进入项目根目录后执行:
uv syncuv 会按 .python-version 选择 Python,创建 .venv 并安装锁定依赖。随后直接使用:
uv run python main.py如果对方只提供了 requirements.txt,可以完全通过 uv 创建环境并安装:
uv venv --python 3.11
uv pip install -r requirements.txt这里的 uv pip install 用于兼容 requirements 文件,不会生成或更新项目的 pyproject.toml 和 uv.lock。
删除软件包和虚拟环境
删除某个软件包
例如删除 requests:
uv remove requestsuv remove 会同时处理依赖声明、锁文件和项目环境。把 requests 换成实际需要删除的包名即可。
如果添加了不再需要的开发依赖,使用 --dev 指定开发依赖组:
uv remove --dev pytest删除项目虚拟环境
项目虚拟环境就是项目根目录下的 .venv 目录。确认不再需要当前环境后,先停止正在使用该环境的程序,再在项目根目录检查并删除:
Get-Location
Resolve-Path .venv
Remove-Item -LiteralPath .venv -Recurse -Force删除前务必确认 Resolve-Path 显示的是当前项目的 .venv,避免误删其他目录。
删除不会移除 pyproject.toml 和 uv.lock。需要恢复环境时重新执行:
uv sync如果只是想修复依赖不一致,通常直接执行 uv sync 即可,不必先删除 .venv。
使用配置文件区分环境
前面的依赖安装、删除和运行均由 uv 完成。下面继续使用 pydantic-settings 读取不同环境的配置。
1. 安装 pydantic-settings
uv add pydantic-settings2. 在项目根目录新建四个配置文件
.env:
APP_ENV=dev
APP_NAME=FastAPI Demo Default
DEBUG=true
DATABASE_URL=mysql+pymysql://root:your_password@127.0.0.1:3306/demo1?charset=utf8mb4.env.dev:
APP_ENV=dev
APP_NAME=FastAPI Demo Dev
DEBUG=true
DATABASE_URL=mysql+pymysql://root:your_password@127.0.0.1:3306/demo1_dev?charset=utf8mb4.env.prod:
APP_ENV=prod
APP_NAME=FastAPI Demo Prod
DEBUG=false
DATABASE_URL=mysql+pymysql://root:your_password@127.0.0.1:3306/demo1_prod?charset=utf8mb4.env.test:
APP_ENV=test
APP_NAME=FastAPI Demo Test
DEBUG=true
DATABASE_URL=mysql+pymysql://root:your_password@127.0.0.1:3306/demo1_test?charset=utf8mb4这些文件可能包含数据库账号、密钥等敏感信息。实际项目应根据内容决定是否提交到版本控制,不要把生产密钥公开提交。
3. core/config.py
import os
from pydantic_settings import BaseSettings, SettingsConfigDict
environment = os.environ["APP_ENV"]
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=(".env", f".env.{environment}"),
env_file_encoding="utf-8",
extra="ignore",
)
# 需要导出的配置项,这里和配置文件对应
app_name: str
app_env: str
debug: bool
database_url: str
settings = Settings()4.注册dev 、prod 、test 命令
- 找到
pyproject.toml中的project.scripts部分,添加以下内容:
[project]
name = "demo1"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.14"
dependencies = [
"fastapi[standard]>=0.162.0",
"pydantic>=2,<3",
"pydantic-settings>=2.16.2",
"uvicorn>=0.54.0",
]
[project.scripts]
dev = "main:dev"
prod = "main:prod"
test = "main:test"
[build-system]
requires = ["setuptools>=75"]
build-backend = "setuptools.build_meta"
[tool.setuptools]
py-modules = ["main"]
[tool.setuptools.packages.find]
where = ["."]
include = ["core*", "modules*"]
namespaces = true5. 修改 main.py
from fastapi import FastAPI
import os
import uvicorn
def create_app() -> FastAPI:
# 延迟加载配置,确保启动函数已设置 APP_ENV
from core.config import settings
app = FastAPI(
title=settings.app_name,
debug=settings.debug,
)
@app.get("/")
def index():
return {
"environment": settings.app_env,
"database_url": settings.database_url,
}
return app
def _start(environment: str, *, reload: bool = False) -> None:
os.environ["APP_ENV"] = environment
uvicorn.run(
"main:create_app",
factory=True,
host="0.0.0.0" if environment == "prod" else "127.0.0.1",
port=8000,
reload=reload,
)
def dev() -> None:
_start("dev", reload=True)
def prod() -> None:
_start("prod")
def test() -> None:
_start("test")6.使用命令
- 运行开发环境:
uv run dev
uv run prod
uv run test