[中文正文内容]

简而言之 你可以将 uv 添加到 Python 脚本的 shebang 行中,使其成为一个自包含的可执行文件。

我正在做一个 Go 项目,以更好地学习这门语言。这是一个由 postgres 数据库支持的简单 API。 当我需要测试一个 endpoint 时,我更喜欢在 ipython REPL 中使用 httpx Python 包,而不是发出 curl 请求。 能够检查响应,并使用 dicts 而不是编写 JSON 来轻松打包 payloads 是一件很棒的事情。 总之,我决定编写一个脚本来 upsert 一些用户数据,这样我就可以测试我的 /users endpoint。 我的 jam_users.py 脚本如下所示:

import httpx
import IPython
from loguru import logger

users = [
  dict(name="The Dude", email="the.dude@abides.com", password="thedudeabides"),
  dict(name="Walter Sobchak", email="walter@sobchak-security.com", password="vietnamvet"),
  dict(name="Donnie", email="donniesurfs@yahoo.com", password="iamthewalrus"),
  dict(name="Maude", email="mauddie@avant-guard.com", password="goodmanandthorough"),
]

r = httpx.get("http://localhost:4000/v1/users")
r.raise_for_status()

for user in r.json()["users"]:
  logger.info(f"Deleting: {user['name']}")
  r = httpx.delete(f"http://localhost:4000/v1/users/{user['id']}")
  r.raise_for_status()

for user in users:
  r = httpx.post("http://localhost:4000/v1/users", json=user)
  r.raise_for_status()
  logger.info(f"Created: {r.json()}")

IPython.embed()

这非常简单。 它会清除任何现有用户,然后插入这些测试用户。 之后,我会进入一个 ipython repl 来完成我需要的测试。 我所要做的就是运行:

python jam_users.py

但是,如果我想按原样运行脚本,我需要选择以下方法之一:

在我看来,这些都不是很好的选择。 这些方法还依赖于安装了与这些软件包兼容的系统 Python。 这不是什么大问题,但仍然需要考虑。

我最近一直在使用 uv,并且我越来越喜欢它作为包管理器的实用性、作为 pip 替代品的效率以及隔离 Python 可执行文件的能力。 我还没有使用太多的是 Python 脚本中的特殊 # /// script 标签。

当我第一次读到这个功能时,我非常怀疑。 我不太喜欢将语法嵌入到注释中。 然而,这似乎是一个完美的应用程序。 因此,我更新了我的脚本,在脚本头中包含了依赖项,如下所示:

# /// script
# dependencies = ["ipython", "httpx", "loguru"]
# ///
import httpx
import IPython
from loguru import logger

...

添加此内容后,我现在可以使用 uv 非常轻松地运行脚本:

uv run jam_users.py

太棒了! 现在,uv 将为脚本创建一个隔离的 virtual environment,下载依赖项并安装它们,然后在该 venv 的上下文中运行我的脚本! 我不必自己管理 virtual environment,也不必担心用我迟早会忘记删除的软件包来弄乱我的系统 Python。

不过,常规 Python 脚本的一个好处是,你可以使用 shebang 行使其可执行:

#!/usr/bin/env python
...

现在,如果我使脚本可执行(chmod +x jam_users.py),我可以将其直接作为可执行脚本调用! 但是,这不会利用 uv 脚本 header,因为 Python 本身只会忽略该注释。

所以,我进行了一些挖掘,发现你实际上可以将 uv 命令的调用嵌入到 shebang 行中,如下所示:

#!/usr/bin/env -S uv run --script
# /// script
# dependencies = ["ipython", "httpx", "loguru"]
# ///
import httpx
import IPython
from loguru import logger

...

这有效,因为 -S 标志告诉系统在将所有内容传递给系统的 env 之前,将其后面的所有内容拆分为单独的参数。

现在(当然,在 chmod +x jam_users.py 之后),我可以直接执行我的脚本:

./jam_users.py

就是这样! 更好的是,我可以在任何安装了 uv 的(Unix)系统上运行此脚本,而无需进行任何依赖项或 virtual environment 管理。

现在,这个脚本本身非常简单,只不过是一个玩具示例。 但是,在过去,我编写过相当复杂的脚本,需要将其交给其他用户运行。 当然,这总是伴随着关于如何_准备_他们的系统才能运行脚本的冗长解释。 这种方法可以立即轻松地解决该问题(只要他们安装了 uv)。

试一试,让我知道你的想法。

感谢阅读!