Skip to content

Commit

Permalink
Detect infinite recursion in uv run.
Browse files Browse the repository at this point in the history
Handle potential infinite recursion if `uv run` recursively invokes `uv
run`. This can happen if the shebang line of a script includes `uv run`.

Handled by adding a new environment variable `UV_RUN_RECURSION_DEPTH`, which
contains a counter of the number of times that uv run has been transitively
invoked. If unset, it defaults to zero, and each time uv run starts a
subprocess it increments the counter.

Closes astral-sh#11220.
  • Loading branch information
ssanderson committed Feb 8, 2025
1 parent cf366a5 commit 94d8a1c
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 0 deletions.
5 changes: 5 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2932,6 +2932,11 @@ pub struct RunArgs {
/// By default, environment modifications are omitted, but enabled under `--verbose`.
#[arg(long, env = EnvVars::UV_SHOW_RESOLUTION, value_parser = clap::builder::BoolishValueParser::new(), hide = true)]
pub show_resolution: bool,

/// Number of times this process has been recursively invoked by uv
/// run. Used to detect and error on potential infinite recursion.
#[arg(long, hide = true, env = EnvVars::UV_RUN_RECURSION_DEPTH)]
pub recursion_depth: Option<u32>,
}

#[derive(Args)]
Expand Down
5 changes: 5 additions & 0 deletions crates/uv-static/src/env_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -614,4 +614,9 @@ impl EnvVars {

/// Enables fetching files stored in Git LFS when installing a package from a Git repository.
pub const UV_GIT_LFS: &'static str = "UV_GIT_LFS";

/// Used to prevent infinite recursion when uv run transitively invokes
/// itself.
#[attr_hidden]
pub const UV_RUN_RECURSION_DEPTH: &'static str = "UV_RUN_RECURSION_DEPTH";
}
18 changes: 18 additions & 0 deletions crates/uv/src/commands/project/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,17 @@ pub(crate) async fn run(
env_file: Vec<PathBuf>,
no_env_file: bool,
preview: PreviewMode,
recursion_depth: u32,
) -> anyhow::Result<ExitStatus> {
// Check if max recursion depth was exceeded. This most commonly happens
// for scripts with a shebang line like `#!/usr/bin/env -S uv run`, so try
// to provide guidance for that case.
if recursion_depth > MAX_RECURSION_DEPTH {
bail!(
"uv run recursion depth exceeded. If your script shebang invokes `uv run`, you may need to add --script.",
);
}

// These cases seem quite complex because (in theory) they should change the "current package".
// Let's ban them entirely for now.
let mut requirements_from_stdin: bool = false;
Expand Down Expand Up @@ -1080,6 +1090,12 @@ pub(crate) async fn run(
)?;
process.env(EnvVars::PATH, new_path);

// Increment recursion depth counter.
process.env(
EnvVars::UV_RUN_RECURSION_DEPTH,
format!("{}", recursion_depth + 1),
);

// Ensure `VIRTUAL_ENV` is set.
if interpreter.is_virtualenv() {
process.env(EnvVars::VIRTUAL_ENV, interpreter.sys_prefix().as_os_str());
Expand Down Expand Up @@ -1483,3 +1499,5 @@ fn is_python_zipapp(target: &Path) -> bool {
}
false
}

const MAX_RECURSION_DEPTH: u32 = 100;
1 change: 1 addition & 0 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1512,6 +1512,7 @@ async fn run_project(
args.env_file,
args.no_env_file,
globals.preview,
args.recursion_depth,
))
.await
}
Expand Down
3 changes: 3 additions & 0 deletions crates/uv/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ pub(crate) struct RunSettings {
pub(crate) settings: ResolverInstallerSettings,
pub(crate) env_file: Vec<PathBuf>,
pub(crate) no_env_file: bool,
pub(crate) recursion_depth: u32,
}

impl RunSettings {
Expand Down Expand Up @@ -344,6 +345,7 @@ impl RunSettings {
show_resolution,
env_file,
no_env_file,
recursion_depth,
} = args;

let install_mirrors = filesystem
Expand Down Expand Up @@ -403,6 +405,7 @@ impl RunSettings {
env_file,
no_env_file,
install_mirrors,
recursion_depth: recursion_depth.unwrap_or(0),
}
}
}
Expand Down

0 comments on commit 94d8a1c

Please sign in to comment.