Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
21 changes: 16 additions & 5 deletions crates/uv-settings/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1737,9 +1737,12 @@ pub struct PipOptions {
/// (i.e., when each file was uploaded to the package index), not the release date of the
/// package version.
///
/// Accepts a superset of [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339.html) (e.g.,
/// `2006-12-02T02:07:43Z`). A full timestamp is required to ensure that the resolver will
/// behave consistently across timezones.
/// Accepts RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`), a "friendly" duration (e.g.,
/// `24 hours`, `1 week`, `30 days`), or an ISO 8601 duration (e.g., `PT24H`, `P7D`, `P30D`).
///
/// Durations do not respect semantics of the local time zone and are always resolved to a fixed
/// number of seconds assuming that a day is 24 hours (e.g., DST transitions are ignored).
/// Calendar units such as months and years are not allowed.
#[option(
default = "None",
value_type = "str",
Expand All @@ -1750,8 +1753,16 @@ pub struct PipOptions {
pub exclude_newer: Option<ExcludeNewerValue>,
/// Limit candidate packages for specific packages to those that were uploaded prior to the given date.
///
/// Accepts package-date pairs in a dictionary format. Set a package to `false` to exempt it
/// from the global [`exclude-newer`](#exclude-newer) constraint entirely.
/// Accepts a dictionary format of `PACKAGE = "DATE"` pairs, where `DATE` is an RFC 3339
/// timestamp (e.g., `2006-12-02T02:07:43Z`), a "friendly" duration (e.g., `24 hours`, `1 week`,
/// `30 days`), or a ISO 8601 duration (e.g., `PT24H`, `P7D`, `P30D`).
///
/// Durations do not respect semantics of the local time zone and are always resolved to a fixed
/// number of seconds assuming that a day is 24 hours (e.g., DST transitions are ignored).
/// Calendar units such as months and years are not allowed.
///
/// Set a package to `false` to exempt it from the global [`exclude-newer`](#exclude-newer)
/// constraint entirely.
#[option(
default = "None",
value_type = "dict",
Expand Down
3 changes: 3 additions & 0 deletions crates/uv/tests/it/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ mod pip_compile_scenarios;
#[cfg(all(feature = "test-python", feature = "test-pypi"))]
mod pip_freeze;

#[cfg(all(feature = "test-python", feature = "test-pypi"))]
mod pip_exclude_newer_relative;

#[cfg(all(feature = "test-python", feature = "test-pypi"))]
mod pip_install;

Expand Down
123 changes: 123 additions & 0 deletions crates/uv/tests/it/pip_exclude_newer_relative.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use anyhow::Result;
use assert_fs::fixture::{FileWriteStr, PathChild};
use uv_static::EnvVars;

use uv_test::uv_snapshot;

/// Install with relative `exclude-newer` values from the `uv pip` CLI.
///
/// Uses idna which has releases at:
/// - 3.6: 2023-11-25
/// - 3.7: 2024-04-11
#[test]
fn pip_install_exclude_newer_relative() {
let context = uv_test::test_context!("3.12");
let current_timestamp = "2024-05-01T00:00:00Z";

// 3 weeks before 2024-05-01 is 2024-04-10, which is before idna 3.7.
uv_snapshot!(context.filters(), context
.pip_install()
.env_remove(EnvVars::UV_EXCLUDE_NEWER)
.env(EnvVars::UV_TEST_CURRENT_TIMESTAMP, current_timestamp)
.arg("--exclude-newer")
.arg("3 weeks")
.arg("idna"), @"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Resolved 1 package in [TIME]
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
+ idna==3.6
");

// A package-specific span can relax the global cutoff for that package.
uv_snapshot!(context.filters(), context
.pip_install()
.env_remove(EnvVars::UV_EXCLUDE_NEWER)
.env(EnvVars::UV_TEST_CURRENT_TIMESTAMP, current_timestamp)
.arg("--exclude-newer")
.arg("3 weeks")
.arg("--exclude-newer-package")
.arg("idna=2 weeks")
.arg("--upgrade")
.arg("idna"), @"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Resolved 1 package in [TIME]
Prepared 1 package in [TIME]
Uninstalled 1 package in [TIME]
Installed 1 package in [TIME]
- idna==3.6
+ idna==3.7
");
}

/// Install with relative `exclude-newer` values from `[tool.uv.pip]`.
///
/// Uses idna which has releases at:
/// - 3.6: 2023-11-25
/// - 3.7: 2024-04-11
#[test]
fn pip_install_exclude_newer_relative_config() -> Result<()> {
let context = uv_test::test_context!("3.12");
let current_timestamp = "2024-05-01T00:00:00Z";
let pyproject_toml = context.temp_dir.child("pyproject.toml");

pyproject_toml.write_str(
r#"
[tool.uv.pip]
exclude-newer = "3 weeks"
"#,
)?;

uv_snapshot!(context.filters(), context
.pip_install()
.env_remove(EnvVars::UV_EXCLUDE_NEWER)
.env(EnvVars::UV_TEST_CURRENT_TIMESTAMP, current_timestamp)
.arg("idna"), @"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Resolved 1 package in [TIME]
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
+ idna==3.6
");

pyproject_toml.write_str(
r#"
[tool.uv.pip]
exclude-newer = "3 weeks"
exclude-newer-package = { idna = "2 weeks" }
"#,
)?;

uv_snapshot!(context.filters(), context
.pip_install()
.env_remove(EnvVars::UV_EXCLUDE_NEWER)
.env(EnvVars::UV_TEST_CURRENT_TIMESTAMP, current_timestamp)
.arg("--upgrade")
.arg("idna"), @"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Resolved 1 package in [TIME]
Prepared 1 package in [TIME]
Uninstalled 1 package in [TIME]
Installed 1 package in [TIME]
- idna==3.6
+ idna==3.7
");

Ok(())
}
4 changes: 2 additions & 2 deletions uv.schema.json

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

Loading