1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//! uv — fast Python package manager.
use std::path::Path;
use std::process::Command;
/// Detected via `uv.lock`.
pub(crate) fn detect(dir: &Path) -> bool {
dir.join("uv.lock").exists()
}
/// `uv sync [--frozen]`
pub(crate) fn install_cmd(frozen: bool) -> Command {
let mut c = super::program::command("uv");
c.arg("sync");
if frozen {
c.arg("--frozen");
}
c
}
/// `uvx <args...>` — uv's `npx`-equivalent (i.e. `uv tool run`).
///
/// Runs a tool from `PyPI` in an ephemeral environment without
/// installing it permanently into the project venv. This is the
/// right primitive for the arbitrary-command exec fallback —
/// `uv run` is for the project's own Python scripts /
/// `pyproject.toml#project.scripts.<name>` entries, not for
/// `npx`-style "fetch and run any binary."
pub(crate) fn exec_cmd(args: &[String]) -> Command {
let mut c = super::program::command("uvx");
c.args(args);
c
}
#[cfg(test)]
mod tests {
use super::exec_cmd;
#[test]
fn exec_uses_uvx_passthrough() {
// `runner --pm uv run ruff check` should build
// `uvx ruff check` — uvx is the `uv tool run` shorthand and
// is the npx-equivalent. `uv run` (the previous
// implementation) only finds binaries already installed in
// the project venv, which is a different code path.
let args = [String::from("ruff"), String::from("check")];
let built: Vec<_> = exec_cmd(&args)
.get_args()
.map(|arg| arg.to_string_lossy().into_owned())
.collect();
assert_eq!(built, ["ruff", "check"]);
}
}