Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crates/uv-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ rustc-hash = { workspace = true }
serde = { workspace = true }
url = { workspace = true }

[target.'cfg(unix)'.dependencies]
uv-unix = { workspace = true }

[dev-dependencies]
insta = { workspace = true }

Expand Down
4 changes: 2 additions & 2 deletions crates/uv-cli/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,7 @@ pub struct RunSettings {
pub max_recursion_depth: u32,
pub malware_settings: MalwareCheckSettings,
#[cfg(unix)]
pub run_rlimit_nofile: Option<u32>,
pub run_resource_limits: Vec<uv_unix::ResourceLimit>,
}

impl RunSettings {
Expand Down Expand Up @@ -879,7 +879,7 @@ impl RunSettings {
max_recursion_depth: max_recursion_depth.unwrap_or(Self::DEFAULT_MAX_RECURSION_DEPTH),
malware_settings,
#[cfg(unix)]
run_rlimit_nofile: environment.run_rlimit_nofile,
run_resource_limits: environment.run_resource_limits,
})
}
}
Expand Down
33 changes: 25 additions & 8 deletions crates/uv-project-commands/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ pub async fn run(
preview: Preview,
max_recursion_depth: u32,
malware_settings: MalwareCheckSettings,
#[cfg(unix)] run_rlimit_nofile: Option<u32>,
#[cfg(unix)] run_resource_limits: Vec<uv_unix::ResourceLimit>,
) -> 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
Expand Down Expand Up @@ -1262,13 +1262,30 @@ pub async fn run(
}

#[cfg(unix)]
if let Some(limit) = run_rlimit_nofile {
uv_unix::set_open_file_limit(limit).with_context(|| {
format!(
"Failed to apply `{}` value `{limit}`",
EnvVars::UV_RUN_RLIMIT_NOFILE
)
})?;
if !run_resource_limits.is_empty() {
let resource_limits = run_resource_limits
.into_iter()
.map(|limit| {
limit.prepare().with_context(|| {
format!(
"Failed to apply `{}` value `{}`",
limit.environment_variable(),
limit.value()
)
})
})
.collect::<Result<Vec<_>, _>>()?;
// SAFETY: Validation and allocation happen before the fork. The callback only
// invokes resource-limit system calls and constructs errors from OS error codes.
#[expect(unsafe_code)]
unsafe {
process.pre_exec(move || {
for limit in &resource_limits {
limit.apply()?;
Comment on lines +1282 to +1284

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] Retain configured limits in spawn-error diagnostics

prepare() checks representability and the inherited hard limit, but setrlimit can still fail—for example, macOS rejects RLIMIT_NOFILE above kern.maxfilesperproc even with an unlimited hard limit. Returning only errno here routes that failure through the generic Failed to spawn context, losing the environment variable and requested value that the previous implementation reported. Retain the configured names and values in the parent and include them in spawn-error context, while keeping the callback allocation-free.

}
Ok(())
});
}
}

// Spawn and wait for completion
Expand Down
3 changes: 3 additions & 0 deletions crates/uv-settings/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ toml = { workspace = true }
tracing = { workspace = true }
url = { workspace = true }

[target.'cfg(unix)'.dependencies]
uv-unix = { workspace = true }

[package.metadata.cargo-shear]
ignored = ["clap"]

Expand Down
21 changes: 16 additions & 5 deletions crates/uv-settings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ pub struct EnvironmentOptions {
pub malware_check: EnvFlag,
pub malware_check_url: Option<DisplaySafeUrl>,
#[cfg(unix)]
pub run_rlimit_nofile: Option<u32>,
pub run_resource_limits: Vec<uv_unix::ResourceLimit>,
}

impl EnvironmentOptions {
Expand Down Expand Up @@ -836,6 +836,20 @@ impl EnvironmentOptions {
EnvFlag::new(EnvVars::UV_NATIVE_TLS)?
};

#[cfg(unix)]
let run_resource_limits = uv_unix::SUPPORTED_RESOURCE_LIMITS
.iter()
.filter_map(|&(environment_variable, resource)| {
parse_integer_environment_variable(environment_variable, None)
.map(|value| {
value.map(|value| {
uv_unix::ResourceLimit::new(environment_variable, resource, value)
})
})
.transpose()
})
.collect::<Result<Vec<_>, _>>()?;

Ok(Self {
ruff_path: parse_path_environment_variable(EnvVars::RUFF),
ty_path: parse_path_environment_variable(EnvVars::TY),
Expand Down Expand Up @@ -961,10 +975,7 @@ impl EnvironmentOptions {
})
.transpose()?,
#[cfg(unix)]
run_rlimit_nofile: parse_integer_environment_variable(
EnvVars::UV_RUN_RLIMIT_NOFILE,
None,
)?,
run_resource_limits,
})
}
}
Expand Down
28 changes: 26 additions & 2 deletions crates/uv-static/src/env_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1403,14 +1403,38 @@ impl EnvVars {
#[attr_added_in("0.5.19")]
pub const UV_GIT_LFS: &'static str = "UV_GIT_LFS";

/// Sets the soft virtual-address-space limit, in bytes, for commands executed by `uv run`.
///
/// Only supported on Unix platforms that provide `RLIMIT_AS`.
#[attr_added_in("next release")]
pub const UV_RUN_RLIMIT_AS: &'static str = "UV_RUN_RLIMIT_AS";

/// Sets the soft core-file size limit, in bytes, for commands executed by `uv run`.
#[attr_added_in("next release")]
pub const UV_RUN_RLIMIT_CORE: &'static str = "UV_RUN_RLIMIT_CORE";

/// Sets the soft CPU-time limit, in seconds, for commands executed by `uv run`.
#[attr_added_in("next release")]
pub const UV_RUN_RLIMIT_CPU: &'static str = "UV_RUN_RLIMIT_CPU";
Comment on lines +1418 to +1422

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] Document the Unix restriction for all new limits

The descriptions for UV_RUN_RLIMIT_CPU, UV_RUN_RLIMIT_CORE, and UV_RUN_RLIMIT_FSIZE omit the Unix-only restriction stated for the other limits. These descriptions populate the environment-variable reference, but parsing and application are gated by #[cfg(unix)], so the variables silently do nothing on Windows. Add the same platform note to these three entries.


/// Sets the soft file-size limit, in bytes, for commands executed by `uv run`.
#[attr_added_in("next release")]
pub const UV_RUN_RLIMIT_FSIZE: &'static str = "UV_RUN_RLIMIT_FSIZE";

/// Sets the soft open-file descriptor limit for commands executed by `uv run`.
///
/// The limit is applied after uv prepares the environment and immediately before the command
/// is spawned. The hard open-file descriptor limit remains unchanged. If the limit cannot be
/// The limit is applied in the child process after uv prepares the environment, before the
/// command is executed. The hard open-file descriptor limit remains unchanged. If the limit cannot be
/// applied, uv exits with an error without running the command. Only supported on Unix.
#[attr_added_in("0.12.3")]
pub const UV_RUN_RLIMIT_NOFILE: &'static str = "UV_RUN_RLIMIT_NOFILE";

/// Sets the soft process-count limit for commands executed by `uv run`.
///
/// Only supported on Unix platforms that provide `RLIMIT_NPROC`.
#[attr_added_in("next release")]
pub const UV_RUN_RLIMIT_NPROC: &'static str = "UV_RUN_RLIMIT_NPROC";

/// Number of times that `uv run` has been recursively invoked. Used to guard against infinite
/// recursion, e.g., when `uv run` is used in a script shebang.
#[attr_hidden]
Expand Down
5 changes: 5 additions & 0 deletions crates/uv-unix/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,10 @@ test = false
workspace = true

[target.'cfg(unix)'.dependencies]
uv-static = { workspace = true }

nix = { workspace = true }
thiserror = { workspace = true }

[target.'cfg(target_vendor = "apple")'.dependencies]
rustix = { workspace = true, features = ["process"] }
5 changes: 4 additions & 1 deletion crates/uv-unix/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@

mod resource_limits;

pub use resource_limits::{OpenFileLimitError, adjust_open_file_limit, set_open_file_limit};
pub use resource_limits::{
OpenFileLimitError, PreparedResourceLimit, ResourceLimit, ResourceLimitError, RunResource,
SUPPORTED_RESOURCE_LIMITS, adjust_open_file_limit,
};
Loading
Loading