979 字
5 分钟
使用 uv 运行需要依赖的 Python 脚本
2025-07-28

依赖脚本运行#

当脚本需要其他包时,这些包必须安装到脚本运行的环境中。uv偏好按需创建环境,而不是使用手动管理依赖的长期虚拟环境。

例如,以下脚本需要rich包:

import time
from rich.progress import track

for i in track(range(20), description="For example:"):
    time.sleep(0.05)

不指定依赖直接执行会失败:

$ uv run --no-project example.py
Traceback (most recent call last):
  File "/Users/astral/example.py", line 2, in <module>
    from rich.progress import track
ModuleNotFoundError: No module named 'rich'

使用--with选项请求依赖:

uv run --with rich example.py

如需特定版本,可以添加约束:

uv run --with "rich>=10.0" example.py

多个依赖可以重复使用--with选项。

创建Python脚本#

Python最近添加了内联脚本元数据(inline script metadata)的标准格式,允许选择Python版本和定义依赖。使用uv init --script初始化带内联元数据的脚本:

uv init --script hello.py

声明脚本依赖#

内联元数据格式允许在脚本本身中声明依赖。uv支持为你添加和更新内联脚本元数据。使用uv add --script声明脚本依赖:

uv add --script example.py requests rich

这会在脚本顶部添加使用TOML格式的script部分:

# /// script
# dependencies = [
#     "requests<3",
#     "rich",
# ]
# ///

import requests
from rich.pretty import pprint

resp = requests.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10])

uv会自动创建包含运行脚本所需依赖的环境:

$ uv run example.py
[
│   ('1', 'PEP Purpose and Guidelines'),
│   ('2', 'Procedure for Adding New Modules'),
│   ('3', 'Guidelines for Handling Bug Reports'),
│   ...
]

重要:使用内联脚本元数据时,即使在项目中使用uv run,项目的依赖也会被忽略,无需--no-project标志。

uv也尊重Python版本要求:

# /// script
# requires-python = ">=3.12"
# dependencies = []
# ///

# 使用Python 3.12新增的语法
type Point = tuple[float, float]
print(Point)

注意:即使为空,也必须提供dependencies字段。

uv run会搜索并使用所需的Python版本。如果未安装,Python版本会自动下载。

使用shebang创建可执行文件#

可以添加shebang使脚本无需使用uv run即可执行——这样便于运行PATH中或当前文件夹的脚本。

例如,创建名为greet的文件:

#!/usr/bin/env uv run
print("Hello world")

确保脚本可执行(如chmod +x greet),然后运行:

./greet

这种情况下也支持依赖声明:

#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.12"
# dependencies = ["httpx"]
# ///

import httpx
print(httpx.get("https://example.com"))

使用替代包索引#

如果要使用替代包索引(package index)解析依赖,可以使用--index选项:

uv run --with httpx --index https://pypi.example.com/simple/ script.py

如果需要认证访问包索引,请参考包索引文档。

锁定依赖#

uv支持使用uv.lock文件格式为PEP 723脚本锁定依赖。与项目不同,脚本必须使用uv lock显式锁定:

uv lock --script example.py

运行uv lock --script会在脚本旁边创建.lock文件(如example.py.lock)。

锁定后,后续的uv run --scriptuv add --scriptuv export --scriptuv tree --script操作会重用锁定的依赖,必要时更新锁文件。

提高可重现性#

除了锁定依赖,uv还支持在内联脚本元数据的tool.uv部分使用exclude-newer字段,限制uv只考虑特定日期前发布的发行版。这有助于提高脚本在后续运行时的可重现性。

日期必须指定为RFC 3339时间戳(如2006-12-02T02:07:43Z):

# /// script
# dependencies = [
#     "requests",
# ]
# [tool.uv]
# exclude-newer = "2023-10-16T00:00:00Z"
# ///

import requests
print(requests.__version__)

使用不同Python版本#

uv允许在每次脚本调用时请求任意Python版本:

uv run --python 3.11 script.py
uv run --python python3.12 script.py

使用GUI脚本#

在Windows上,uv会使用pythonw运行以.pyw扩展名结尾的脚本:

from tkinter import Tk, ttk

root = Tk()
root.title("uv")
frm = ttk.Frame(root, padding=10)
frm.grid()
ttk.Label(frm, text="Hello World").grid(column=0, row=0)
root.mainloop()

带依赖的GUI脚本同样可以工作:

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QGridLayout

app = QApplication(sys.argv)
widget = QWidget()
grid = QGridLayout()
text_label = QLabel()
text_label.setText("Hello World!")
grid.addWidget(text_label)
widget.setLayout(grid)
widget.setGeometry(100, 100, 200, 50)
widget.setWindowTitle("uv")
widget.show()
sys.exit(app.exec_())
使用 uv 运行需要依赖的 Python 脚本
https://blog.lpkt.cn/posts/py-uv-with-deps/
作者
lollipopkit
发布于
2025-07-28
许可协议
CC BY-NC-SA 4.0