跳转至

运行脚本

Python 脚本是用于独立执行的文件,例如使用 python <script>.py 命令。使用 uv 执行脚本可确保在无需手动管理环境的情况下管理脚本依赖项。

Tip

如果你不熟悉 Python 环境:每个 Python 安装都有一个可安装软件包的环境。通常,建议创建 虚拟 环境 来隔离每个脚本所需的软件包。uv 会自动为你管理虚拟环境,并且更倾向于使用 声明式方式 来管理依赖项。

运行无依赖的脚本

如果你的脚本没有依赖项,可以使用 uv run 来执行它:

example.py
print("Hello world")
$ uv run example.py
Hello world

同样,如果你的脚本依赖标准库中的某个模块,也无需额外操作:

example.py
import os

print(os.path.expanduser("~"))
$ uv run example.py
/Users/astral

可以给脚本提供参数:

example.py
import sys

print(" ".join(sys.argv[1:]))
$ uv run example.py test
test

$ uv run example.py hello world!
hello world!

此外,你的脚本可以直接从标准输入读取:

$ echo 'print("hello world!")' | uv run -

或者,如果你的 shell 支持Here 文档

uv run - <<EOF
print("hello world!")
EOF

请注意,如果你在一个 项目 中使用 uv run,即包含 pyproject.toml 文件的目录,它会在运行脚本之前安装当前项目。如果你的脚本不依赖该项目,可以使用 --no-project 标志来跳过此步骤:

$ # 注意:`--no-project` 标志必须在脚本名称之前提供。
$ uv run --no-project example.py

有关在项目中工作的更多详细信息,请参阅项目指南

运行带有依赖项的脚本

当你的脚本需要其他软件包时,必须将它们安装到脚本运行的环境中。uv 倾向于按需创建这些环境,而不是使用手动管理依赖项的长期虚拟环境。这就需要明确声明脚本所需的依赖项。通常,建议使用项目内联元数据来声明依赖项,但 uv 也支持每次调用时请求依赖项。

例如,以下脚本需要 rich

example.py
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
For example: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:01

如果需要特定版本,可以在请求的依赖项中添加约束条件:

$ uv run --with 'rich>12,<13' example.py

可以通过重复使用 --with 选项来请求多个依赖项。

请注意,如果在 项目 中使用 uv run,这些依赖项将作为项目依赖项的 补充 包含在内。若要避免这种行为,请使用 --no-project 标志。

创建 Python 脚本

Python 最近为内联脚本元数据添加了一种标准格式。它允许选择 Python 版本并定义依赖项。使用 uv init --script 来初始化带有内联元数据的脚本:

$ uv init --script example.py --python 3.12

声明脚本依赖项

内联元数据格式允许在脚本本身中声明脚本的依赖项。

uv 支持为您添加和更新内联脚本元数据。使用 uv add --script 来声明脚本的依赖项:

$ uv add --script example.py'requests<3' 'rich'

这将在脚本顶部添加一个 script 部分,使用 TOML 声明依赖项:

example.py
# /// 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'),
│   ('4', 'Deprecation of Standard Modules'),
│   ('5', 'Guidelines for Language Evolution'),
│   ('6', 'Bug Fix Releases'),
│   ('7', 'Style Guide for C Code'),
│   ('8', 'Style Guide for Python Code'),
│   ('9', 'Sample Plaintext PEP Template'),
│   ('10', 'Voting Guidelines')
]

Important

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

uv 也会遵循 Python 版本要求:

example.py
# /// script

# requires-python = ">=3.12"

# dependencies = []

# ///


# 使用 Python 3.12 中新增的一些语法
Point类型 = tuple[float, float]
print(Point类型)

Tip

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

uv run 会查找并使用所需的 Python 版本。如果未安装该 Python 版本,将会下载 —— 更多详细信息,请参阅 Python 版本 文档。

使用 shebang 创建可执行文件

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

例如,创建一个名为 greet 的文件,内容如下:

greet
#!/usr/bin/env -S uv run --script

print("Hello, world!")

确保你的脚本可执行,例如使用 chmod +x greet,然后运行脚本:

$ ./greet
Hello, world!

在此场景下也支持声明依赖项,例如:

example
#!/usr/bin/env -S uv run --script

# /// script

# requires-python = ">=3.12"

# dependencies = ["httpx"]

# ///
import httpx

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

使用替代的包索引

如果你希望使用替代的包索引来解析依赖项,可以使用 --index 选项指定索引:

$ uv add --index "https://example.com/simple" --script example.py 'requests<3' 'rich'

这会将包数据包含在内联元数据中:

# [[tool.uv.index]]

# url = "https://example.com/simple"

如果你需要通过身份验证才能访问包索引,请参阅包索引文档。

锁定依赖项

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 export --script 这样的命令仍将按预期运行,但不会创建锁定文件。

提高可重复性

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

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

example.py
# /// script

# dependencies = [

#   "requests",

# ]

# [tool.uv]

# exclude-newer = "2023-10-16T00:00:00Z"

# ///

import requests

print(requests.__version__)

使用不同的 Python 版本

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

example.py
import sys

print(".".join(map(str, sys.version_info[:3])))
$ # 使用默认的 Python 版本,在您的机器上可能会有所不同
$ uv run example.py
3.12.6
$ # 使用特定的 Python 版本
$ uv run --python 3.10 example.py
3.10.15

有关请求 Python 版本的更多详细信息,请参阅 Python 版本请求 文档。

使用 GUI 脚本

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

example.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()
PS> uv run example.pyw

运行结果

同样,它对依赖项也适用:

example_pyqt.pyw
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_())
PS> uv run --with PyQt5 example_pyqt.pyw

运行结果

后续步骤

要了解更多关于 uv run 的信息,请参阅 命令参考

或者,继续阅读以了解如何使用 uv 运行和安装工具