介绍
首先, uv 工具是使用 rust 开发出来的, 速度要比传统的 pip
, pipx
等一众包管理工具要快不少. 另外, 除了包管理之外, uv 还提供了脚手架的功能, 使用体验和前端开发使用过的 vue-cli
很相似, 可以帮助我们自动初始化项目, 创建好一个空的包含必要文件结构的文件夹. 此外, uv 可以同时管理多个不同 Python 版本, 方便快速切换使用. 当我们向项目中添加依赖时, 还会自动维护相关的包列表, 对于后续的打包和分发, 就相当友好. 更多功能和具体介绍参考官网文档:
https://docs.astral.sh/uv/
本文将在 WSL Ubuntu 环境中以一个简单的 Flask 项目作为示范, 使用 uv 对项目的整个生命周期进行管理.
安装 uv
可以使用官网提供的安装脚本, 对于已经安装了 Python 的环境, 可以直接用 pip install uv
安装. 需要留意, 对于 pip 方式安装, 需要确保系统环境变量 PATH
中包含了 pip 包安装后的 bin 文件夹路径. 否则直接直接执行 uv 命令时会出现找不到的情况.
# 检查已安装的 uv 版本
lpwm@Beijing:~/python-project$ uv --version
uv 0.5.9# 检查支持的所有 Python 版本
lpwm@Beijing:~/python-project$ uv python list
cpython-3.13.1+freethreaded-linux-x86_64-gnu <download available>
cpython-3.13.1-linux-x86_64-gnu <download available>
cpython-3.12.8-linux-x86_64-gnu <download available>
cpython-3.12.3-linux-x86_64-gnu /usr/bin/python3.12
cpython-3.12.3-linux-x86_64-gnu /usr/bin/python3 -> python3.12
cpython-3.12.3-linux-x86_64-gnu /bin/python3.12
cpython-3.12.3-linux-x86_64-gnu /bin/python3 -> python3.12
cpython-3.11.11-linux-x86_64-gnu /home/lpwm/.local/share/uv/python/cpython-3.11.11-linux-x86_64-gnu/bin/python3.11
cpython-3.10.16-linux-x86_64-gnu /home/lpwm/.local/share/uv/python/cpython-3.10.16-linux-x86_64-gnu/bin/python3.10
cpython-3.9.21-linux-x86_64-gnu /home/lpwm/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/bin/python3.9
cpython-3.8.20-linux-x86_64-gnu <download available>
cpython-3.7.9-linux-x86_64-gnu <download available>
pypy-3.10.14-linux-x86_64-gnu <download available>
pypy-3.9.19-linux-x86_64-gnu <download available>
pypy-3.8.16-linux-x86_64-gnu <download available>
pypy-3.7.13-linux-x86_64-gnu <download available>
初始化新项目
这里特意使用一个当前系统中不存在的较老的 cpython-3.8.20
作为运行环境, 创建项目时指定 Python 版本 uv 会自动下载.
lpwm@Beijing:~/python-project$ uv init my-flask --python cpython-3.8.20
Initialized project `my-flask` at `/home/lpwm/python-project/my-flask`
会在当前路径中创建和项目同名的文件夹, 包含下面内容:
my-project/
├── .git
├── .gitignore
├── .python-version
├── README.md
├── hello.py
└── pyproject.toml
.python-version
记录了当前项目所使用的 Python 版本信息, 只定义到小数点后一位 3.8
, 因为 Python 版本的小数点后第二位只用于安全补丁更新后递增, 支持语法特性上不会进行变动.
pyproject.toml
是项目的配置文件, 初始内容如下, 像描述性的属性可以随便修改, dependencies
部分实现的就是传统的 requirements.txt
功能, 可以手搓, 当然我们接着用 uv 的命令添加依赖, 会自动更新这一部分内容.
[project]
name = "my-flask"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.8"
dependencies = []
uv 文档上说的是它会使用系统中存在的 .pip
配置文件获取 index-url
镜像地址的配置, 但是实际体验下来, 好像有些命令不太灵, 所以稳妥的办法还是将镜像地址写到项目的这个配置文件中靠谱. 在配置的最后添加下面内容:
[pip]
index-url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"
[tool.uv]
index-url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"
管理依赖
使用 uv add
命令向项目中添加依赖
lpwm@Beijing:~/python-project/my-flask$ uv add flask
Using CPython 3.8.20
Creating virtual environment at: .venv
Resolved 17 packages in 492ms
Prepared 9 packages in 103ms
Installed 9 packages in 7ms+ blinker==1.8.2+ click==8.1.8+ flask==3.0.3+ importlib-metadata==8.5.0+ itsdangerous==2.2.0+ jinja2==3.1.6+ markupsafe==2.1.5+ werkzeug==3.0.6+ zipp==3.20.2
此时再检查 pyproject.toml
内容, dependencies
部分自动加上了包的信息.
[project]
name = "my-flask"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.8"
dependencies = ["flask>=3.0.3",
]
[pip]
index-url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"
[tool.uv]
index-url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"
这里默认安装的 Flask 版本太高了, 我想要使用特定的一个老版本 2.3.3
, 所以需要先移除现在安装的包, 再指定版本添加依赖.
lpwm@Beijing:~/python-project/my-flask$ uv remove flask
Resolved 1 package in 1ms
Uninstalled 9 packages in 3ms- blinker==1.8.2- click==8.1.8- flask==3.0.3- importlib-metadata==8.5.0- itsdangerous==2.2.0- jinja2==3.1.6- markupsafe==2.1.5- werkzeug==3.0.6- zipp==3.20.2
lpwm@Beijing:~/python-project/my-flask$ uv add flask==2.3.3
Resolved 16 packages in 76ms
Prepared 1 package in 60ms
Installed 9 packages in 7ms+ blinker==1.8.2+ click==8.1.8+ flask==2.3.3+ importlib-metadata==8.5.0+ itsdangerous==2.2.0+ jinja2==3.1.6+ markupsafe==2.1.5+ werkzeug==3.0.6+ zipp==3.20.2
再次检查 pyproject.toml
内容, dependencies
部分同步更新. 优雅, 太优雅了!
dependencies = ["flask==2.3.3",
]
使用命令 uv tree
可以检查当前项目依赖的树结构
lpwm@Beijing:~/python-project/my-flask$ uv tree
Resolved 16 packages in 0.52ms
my-flask v0.1.0
└── flask v2.3.3├── blinker v1.8.2├── click v8.1.8├── importlib-metadata v8.5.0│ └── zipp v3.20.2├── itsdangerous v2.2.0├── jinja2 v3.1.6│ └── markupsafe v2.1.5└── werkzeug v3.0.6└── markupsafe v2.1.5
这个时候如果尝试直接运行 Flask 的命令, 会提示找不到, 因为我们还没有创建对应的虚拟环境. 其实这一步也不需要手动做, 在要执行的命令前面添加 uv run
就会自动创建 .venv
并且调用命令, 而无需切换到虚拟环境中.
lpwm@Beijing:~/python-project/my-flask$ uv run flask --version
Using CPython 3.8.20
Creating virtual environment at: .venv
Installed 9 packages in 6ms
Python 3.8.20
Flask 2.3.3
Werkzeug 3.0.6
当然, 我们也可以手动激活命令行到 .venv
的上下文环境.
lpwm@Beijing:~/python-project/my-flask$ source .venv/bin/activate
(my-flask) lpwm@Beijing:~/python-project/my-flask$ flask --version
Python 3.8.20
Flask 2.3.3
Werkzeug 3.0.6
(my-flask) lpwm@Beijing:~/python-project/my-flask$ deactivate
lpwm@Beijing:~/python-project/my-flask$
Flask 搓个 Demo 页面
在项目根路径下创建 app.py
, 做个简单的首页.
from flask import Flaskapp = Flask(__name__)@app.get('/')
def index():return '<h1>Hello uv</h1>'
原神 启动!
lpwm@Beijing:~/python-project/my-flask$ uv run flask run* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.* Running on http://127.0.0.1:5000
Press CTRL+C to quit
127.0.0.1 - - [30/Mar/2025 22:48:43] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [30/Mar/2025 22:48:43] "GET /favicon.ico HTTP/1.1" 404 -
浏览器可以正常访问:
OK, 现在假装项目就开发完成了, 接着进入打包发布环节.
项目打包
首先删除项目默认创建的那个 hello.py
文件, 打包时要求根路径下只能有一个入口文件. 否则执行 uv build
会报错:
lpwm@Beijing:~/python-project/my-flask$ uv build
Building source distribution...
running egg_info
**** 此处省略 n 行 ****
Successfully built dist/my_flask-0.1.0.tar.gz
Successfully built dist/my_flask-0.1.0-py3-none-any.whl
打包完成后, 会在项目中多出 dist
文件夹, 其中包含了 .tar.gz
和 .whl
两种格式. 如果需要发布到 PyPI 仓库的项目, 直接运行 uv publish
就可以, 本文不做演示. 下面我们来讨论使用 .whl
格式分发后的安装过程.
Wheel 方式分发安装
将上面打包好的 dist/my_flask-0.1.0-py3-none-any.whl
复制到一个新的文件夹中, 模拟分发后的环境.
# 另新建一个项目文件夹, 把 .whl 文件复制过去
lpwm@Beijing:~/python-project$ mkdir dep-flask
lpwm@Beijing:~/python-project$ cp my-flask/dist/my_flask-0.1.0-py3-none-any.whl ./dep-flask/
lpwm@Beijing:~/python-project$ cd dep-flask# 使用 uv 仅创建一个虚拟环境
lpwm@Beijing:~/python-project/dep-flask$ ls
my_flask-0.1.0-py3-none-any.whl
lpwm@Beijing:~/python-project/dep-flask$ uv venv --python 3.8
Using CPython 3.8.20
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate# 激活虚拟环境后安装 whl 文件, 注意 pip 命令前添加 uv
lpwm@Beijing:~/python-project/dep-flask$ source .venv/bin/activate
(dep-flask) lpwm@Beijing:~/python-project/dep-flask$ uv pip install my_flask-0.1.0-py3-none-any.whl
Resolved 10 packages in 495ms
Prepared 1 package in 3ms
Installed 10 packages in 7ms+ blinker==1.8.2+ click==8.1.8+ flask==2.3.3+ importlib-metadata==8.5.0+ itsdangerous==2.2.0+ jinja2==3.1.6+ markupsafe==2.1.5+ my-flask==0.1.0 (from file:///home/lpwm/python-project/dep-flask/my_flask-0.1.0-py3-none-any.whl)+ werkzeug==3.0.6+ zipp==3.20.2# 检查已安装的依赖, 其中包含了我们的项目 my-flask 以及关联的依赖们
(dep-flask) lpwm@Beijing:~/python-project/dep-flask$ uv pip list
Package Version
------------------ -------
blinker 1.8.2
click 8.1.8
flask 2.3.3
importlib-metadata 8.5.0
itsdangerous 2.2.0
jinja2 3.1.6
markupsafe 2.1.5
my-flask 0.1.0
werkzeug 3.0.6
zipp 3.20.2
后记
需要注意, 以上只是 .whl
格式文件安装的操作步骤演示, 我们使用 Flask 作为依赖框架开发的 WEB 项目在实际部署中其实并不太适用于这种方式, 编写 Dockerfile
使用容器方式部署才是正解.
先挖个坑, 后面再记录 uv 管理的项目使用 Docker 部署的实战过程.
https://docs.astral.sh/uv/guides/integration/docker/