跳到主内容
快讯直播
AI智模界
教程

uv + ty:Python 依赖与类型检查实战

这篇能做出什么

做完这篇,你会得到一个可以反复套用的项目骨架:

  • 别人 clone 仓库后,只需一条 uv sync,虚拟环境、依赖版本就全部对齐,不用再问“你装的是哪个版本”。
  • 提交代码前跑一条 uv run ty check,能在“把字符串当数字传进函数”这类问题上直接报错,而不是等上线后才发现。
  • 在编辑器里写代码,保存后立刻看到红线,不用来回切终端。
  • CI 里跑的是同一套命令,本地过不了就合不进去。

下面按“装工具 → 建项目 → 加依赖 → 接类型检查 → 接编辑器 → 接 CI”的顺序走一遍。

前置条件清单

  • 一台能装软件的电脑,macOS、Linux、Windows 都行。
  • 会用终端敲命令,知道 cd、ls。
  • 装了 git;要做 CI 的话需要一个 GitHub 仓库。
  • 一个编辑器,文里用 VS Code 举例,其他编辑器思路相同。
  • 不需要预装 Python:uv 可以自己下载并管理解释器版本。

第一步:安装 uv 并验证

macOS / Linux:

```bash

curl -LsSf https://astral.sh/uv/install.sh | sh

```

Windows PowerShell:

```powershell

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

```

也可以用 Homebrew、pipx 等包管理器安装,具体方式以 uv 官方文档当前说明为准。装完开一个新终端验证:

```bash

uv --version

uv python list

```

第二条会列出本机已有的和可下载的 Python 版本。如果你机器上一个 Python 都没有,这一步会提示可用的版本,后续 uv 会自动下载。

第二步:初始化项目

```bash

uv init --lib demo-ty

cd demo-ty

uv python pin 3.12

```

几点说明:

  • --lib 生成 src 布局的库项目(src/demo_ty/),适合演示类型检查。如果你做的是脚本、服务,用 uv init demo-ty 即可,后面命令完全一样。
  • uv python pin 3.12 会把版本写进 .python-version 文件。这个文件要提交到 git,CI 和同事的机器都会读它,避免“我这儿是 3.13 你那儿是 3.9”的扯皮。版本号按项目需要填,以官方支持列表为准。
  • 初始化后目录里会有 pyproject.toml、.python-version、README.md、src/ 等文件。

第三步:添加依赖,看清发生了三件事

```bash

uv add httpx

uv add --dev pytest ty

```

第一条加运行时依赖,第二条加开发期工具(测试框架和类型检查器)。执行完,pyproject.toml 会长成这样(版本约束由 uv 自动补全,下面只示意结构):

```toml

[project]

name = "demo-ty"

version = "0.1.0"

requires-python = ">=3.12"

dependencies = [

"httpx",

]

[dependency-groups]

dev = [

"pytest",

"ty",

]

```

同时它还做了两件事:

1. 生成或更新 uv.lock,把每个包(包括间接依赖)的精确版本和哈希记下来。

2. 创建 .venv/ 虚拟环境并装好包。

uv.lock 必须提交到 git,.venv/ 不要提交。.gitignore 里加两行:

```gitignore

.venv/

__pycache__/

```

日常操作还有这些:

```bash

uv add "httpx>=0.27" # 带约束地加依赖

uv remove httpx # 删依赖,同时更新 lock

uv sync # 按 lock 把环境对齐(同事 clone 后跑这条)

uv sync --no-dev # 只装运行时依赖,用于生产镜像

uv tree # 看依赖树,排查“谁把这个包带进来的”

```

旧版本 uv 把开发依赖写在 [tool.uv] 下面,新版本用 PEP 735 的 [dependency-groups]。以 uv add --dev 实际生成的内容为准,不要手写覆盖。

第四步:用 uv run 跑任何东西

```bash

uv run python -c "import httpx; print(httpx.__version__)"

uv run pytest -q

```

uv run 会先检查环境是否和 lock 一致,不一致就先同步,然后才执行命令。所以不需要手动 source .venv/bin/activate;激活当然也可以,但在 CI 和脚本里,uv run 更省事。

如果环境已经同步过,想省掉检查开销,可以加 --no-sync:

```bash

uv run --no-sync pytest -q

```

第五步:接入 ty,先让它报一次错

在 src/demo_ty/pricing.py 写一段故意有问题的代码:

```python

def total(prices: list[float]) -> float:

return sum(prices)

if __name__ == "__main__":

print(total(["19.9", "29.9"]))

```

运行:

```bash

uv run ty check

```

它会指出:total 需要的是 list[float],你给的是字符串列表,参数类型不匹配。输出里会带上规则名,方便你按规则去查文档或做配置。

改成正确写法:

```python

if __name__ == "__main__":

print(total([19.9, 29.9]))

```

再跑一次,通过。这就是整条流水线里最核心的一条命令,后面 CI 里也是它。

想缩小检查范围可以指定路径:

```bash

uv run ty check src

```

ty 还在快速迭代,可配置项、规则名会变。写配置前,先跑一次看它报的规则名,再对照官方文档,不要照抄网上过期的片段。

第六步:把配置写进 pyproject.toml

在 pyproject.toml 末尾追加:

```toml

[tool.ty.environment]

python-version = "3.12"

python = ".venv"

```

environment.python 告诉 ty 去哪个虚拟环境找第三方库的类型信息。多数情况下 ty 自己能找到项目根下的 .venv,显式写死更稳,尤其是虚拟环境不在默认位置、或 CI 里路径不同时。

如果个别规则误报太多,可以按规则名调整等级:

```toml

[tool.ty.rules]

规则名以 ty check 输出里显示的为准

unresolved-import = "ignore"

```

原则是:只关掉确实误报的规则,不要图省事把整类检查关掉,否则类型检查就白接了。

第七步:编辑器集成

在 VS Code 扩展市场搜索 ty(Astral 官方发布),安装后打开项目根目录,扩展会启动 ty 的语言服务(底层命令是 ty server),实现保存即诊断、跳转定义、悬浮提示。

同时确认一下解释器选对了:命令面板执行 “Python: Select Interpreter”,选项目里的 .venv。如果编辑器里一切正常、终端里却报错,或者反过来,九成是解释器或工作目录没对上。

如果你原来装着 Pylance,两个类型检查器会重复报错。过渡期可以先把其中一个的诊断关掉,只留一个当权威来源。具体开关位置以扩展说明为准。

第八步:接进 CI

先确认 .python-version 和 uv.lock 都已提交。然后建 .github/workflows/ci.yml:

```yaml

name: ci

on:

push:

pull_request:

jobs:

check:

runs-on: ubuntu-latest

steps:

  • uses: actions/checkout@v4
  • name: Install uv

uses: astral-sh/setup-uv@v5

with:

enable-cache: true

  • name: Sync dependencies

run: uv sync --locked

  • name: Type check

run: uv run --no-sync ty check

  • name: Test

run: uv run --no-sync pytest -q

```

几个关键点:

  • astral-sh/setup-uv 的版本 tag 以该仓库 README 当前说明为准,升级时同步改。
  • uv sync --locked 表示“lock 文件必须已经是最新的”,如果有人改了 pyproject.toml 却忘了 uv lock,这一步会直接失败,而不是在 CI 里悄悄改版本。
  • setup-uv 会读取 .python-version 并安装对应解释器,所以 CI 与本地的 Python 版本自然一致。
  • 不需要额外写“激活虚拟环境”的步骤。

本地提交前,把这几条串成一条命令习惯:

```bash

uv run ty check && uv run pytest -q

```

想更省心可以加 pre-commit(同样用 uv add --dev pre-commit 装),把这条命令挂到提交钩子上。

常见问题与排错

1. 命令行里 import 报错,但代码明明写了依赖

大概率是绕过 uv 用了 pip install。uv sync 默认会把 .venv 严格对齐到 lock 文件,手工装进去的包可能被清掉。统一用 uv add,改完记得提交新的 uv.lock。

2. CI 上 uv sync --locked 失败

本地改了 pyproject.toml 没重新生成 lock。本地跑一次 uv lock,把 uv.lock 一起提交。

3. 编辑器不标红,命令行 ty check 正常

编辑器选错了解释器,或工作区根目录不是项目根目录。检查 .venv 是否存在、解释器是否指向它,然后重开窗口。

4. ty 报第三方库找不到类型

有些库不带类型标注,需要额外装类型存根包(常见命名是 types-xxx,以该库文档为准)。如果只是内部模块路径问题,检查 [tool.ty.environment] 里的 python 路径是否指向正确的虚拟环境。

5. ty 的配置项照抄后不生效

ty 处于快速迭代期,配置结构会调整。先看 ty check 的输出和 ty --help,再对照官方文档,不要用两年前的博客片段。

6. 多人改依赖,uv.lock 冲突

不要手工合并 lock 文件。让一方先合入,另一方拉最新代码后重新跑 uv lock,再提交。

下一步建议

  • 加 ruff:同一家出品的格式化与 lint 工具,uv add --dev ruff,然后 uv run ruff check、uv run ruff format,和 ty 组成“格式 + 静态检查”双保险。
  • 过渡期双检:ty 还在演进,关键项目可以先让 mypy 或 pyright 并行跑一段时间,比对两者的报错差异,再决定以谁为准。
  • 多版本矩阵:如果项目要兼容多个 Python 版本,在 CI 里用矩阵跑不同解释器版本,依赖解析仍然统一走 uv sync --locked。
  • 定期升级依赖:uv lock --upgrade 配合测试跑一遍,比攒半年再升要轻松得多。
  • 把命令固化下来:写进 Makefile、justfile 或 pre-commit 钩子,让“检查”变成默认动作,而不是靠自觉。

整条链路建好后,日常只剩三个动作:uv add 加依赖、uv run 跑命令、uv lock 提交锁定。类型检查从编辑器的红线一路延伸到 CI 的门禁,中间不需要任何手工同步。

AI 生成本文由 AI 基于公开信息自动生成,仅供参考。