跳到内容

运行脚本

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

注意

如果您不熟悉 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!

此外,您的脚本可以直接从标准输入(stdin)读取

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

或者,如果您的 shell 支持 here-document

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

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

$ # Note: the `--no-project` flag must be provided _before_ the script name.
$ 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.pythonlang.cn/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')
]

重要

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

uv 也遵循 Python 版本要求

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

# Use some syntax added in Python 3.12
type Point = tuple[float, float]
print(Point)

注意

必须提供 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])))
$ # Use the default Python version, may differ on your machine
$ uv run example.py
3.12.6
$ # Use a specific Python version
$ 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

Run Result

同样,它也适用于带依赖项的情况

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

Run Result

下一步

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

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