Skip to content

Commit 4bb5eb5

Browse files
committed
Add exclude-newer-package
1 parent a9ea756 commit 4bb5eb5

File tree

27 files changed

+966
-64
lines changed

27 files changed

+966
-64
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/uv-bench/benches/uv.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,9 @@ mod resolver {
158158
let index = InMemoryIndex::default();
159159
let index_locations = IndexLocations::default();
160160
let installed_packages = EmptyInstalledPackages;
161-
let options = OptionsBuilder::new().exclude_newer(exclude_newer).build();
161+
let options = OptionsBuilder::new()
162+
.exclude_newer(exclude_newer.clone())
163+
.build();
162164
let sources = SourceStrategy::default();
163165
let dependency_metadata = DependencyMetadata::default();
164166
let conflicts = Conflicts::empty();

crates/uv-cli/src/lib.rs

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ use uv_pep508::{MarkerTree, Requirement};
1919
use uv_pypi_types::VerbatimParsedUrl;
2020
use uv_python::{PythonDownloads, PythonPreference, PythonVersion};
2121
use uv_redacted::DisplaySafeUrl;
22-
use uv_resolver::{AnnotationStyle, ExcludeNewer, ForkStrategy, PrereleaseMode, ResolutionMode};
22+
use uv_resolver::{
23+
AnnotationStyle, ExcludeNewer, ExcludeNewerTimestamp, ForkStrategy, PrereleaseMode,
24+
ResolutionMode,
25+
};
2326
use uv_static::EnvVars;
2427
use uv_torch::TorchMode;
2528
use uv_workspace::pyproject_mut::AddBoundsKind;
@@ -29,6 +32,31 @@ pub mod compat;
2932
pub mod options;
3033
pub mod version;
3134

35+
/// A package-specific exclude-newer entry in the format `PACKAGE=DATE`.
36+
#[derive(Debug, Clone, PartialEq, Eq)]
37+
pub struct ExcludeNewerPackageEntry {
38+
pub package: PackageName,
39+
pub timestamp: ExcludeNewerTimestamp,
40+
}
41+
42+
impl FromStr for ExcludeNewerPackageEntry {
43+
type Err = String;
44+
45+
fn from_str(s: &str) -> Result<Self, Self::Err> {
46+
let Some((package, date)) = s.split_once('=') else {
47+
return Err(format!(
48+
"Invalid exclude-newer-package: {s} (expected `PACKAGE=DATE`)"
49+
));
50+
};
51+
52+
let package = PackageName::from_str(package)
53+
.map_err(|err| format!("Invalid package name `{package}`: {err}"))?;
54+
let timestamp = ExcludeNewerTimestamp::from_str(date)?;
55+
56+
Ok(Self { package, timestamp })
57+
}
58+
}
59+
3260
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
3361
pub enum VersionFormat {
3462
/// Display the version as plain text.
@@ -2646,6 +2674,15 @@ pub struct VenvArgs {
26462674
#[arg(long, env = EnvVars::UV_EXCLUDE_NEWER)]
26472675
pub exclude_newer: Option<ExcludeNewer>,
26482676

2677+
/// Limit candidate packages for a specific package to those that were uploaded prior to the given date.
2678+
///
2679+
/// Accepts package-date pairs in the format `PACKAGE=DATE`, where `DATE` is an RFC 3339 timestamp
2680+
/// (e.g., `2006-12-02T02:07:43Z`) or local date (e.g., `2006-12-02`) in your system's configured time zone.
2681+
///
2682+
/// Can be provided multiple times for different packages.
2683+
#[arg(long)]
2684+
pub exclude_newer_package: Option<Vec<ExcludeNewerPackageEntry>>,
2685+
26492686
/// The method to use when installing packages from the global cache.
26502687
///
26512688
/// This option is only used for installing seed packages.
@@ -4628,6 +4665,15 @@ pub struct ToolUpgradeArgs {
46284665
#[arg(long, env = EnvVars::UV_EXCLUDE_NEWER, help_heading = "Resolver options")]
46294666
pub exclude_newer: Option<ExcludeNewer>,
46304667

4668+
/// Limit candidate packages for specific packages to those that were uploaded prior to the given date.
4669+
///
4670+
/// Accepts package-date pairs in the format `PACKAGE=DATE`, where `DATE` is an RFC 3339 timestamp
4671+
/// (e.g., `2006-12-02T02:07:43Z`) or local date (e.g., `2006-12-02`) in your system's configured time zone.
4672+
///
4673+
/// Can be provided multiple times for different packages.
4674+
#[arg(long, help_heading = "Resolver options")]
4675+
pub exclude_newer_package: Option<Vec<ExcludeNewerPackageEntry>>,
4676+
46314677
/// The method to use when installing packages from the global cache.
46324678
///
46334679
/// Defaults to `clone` (also known as Copy-on-Write) on macOS, and `hardlink` on Linux and
@@ -5369,6 +5415,15 @@ pub struct InstallerArgs {
53695415
#[arg(long, env = EnvVars::UV_EXCLUDE_NEWER, help_heading = "Resolver options")]
53705416
pub exclude_newer: Option<ExcludeNewer>,
53715417

5418+
/// Limit candidate packages for specific packages to those that were uploaded prior to the given date.
5419+
///
5420+
/// Accepts package-date pairs in the format `PACKAGE=DATE`, where `DATE` is an RFC 3339 timestamp
5421+
/// (e.g., `2006-12-02T02:07:43Z`) or local date (e.g., `2006-12-02`) in your system's configured time zone.
5422+
///
5423+
/// Can be provided multiple times for different packages.
5424+
#[arg(long, help_heading = "Resolver options")]
5425+
pub exclude_newer_package: Option<Vec<ExcludeNewerPackageEntry>>,
5426+
53725427
/// The method to use when installing packages from the global cache.
53735428
///
53745429
/// Defaults to `clone` (also known as Copy-on-Write) on macOS, and `hardlink` on Linux and
@@ -5562,6 +5617,15 @@ pub struct ResolverArgs {
55625617
#[arg(long, env = EnvVars::UV_EXCLUDE_NEWER, help_heading = "Resolver options")]
55635618
pub exclude_newer: Option<ExcludeNewer>,
55645619

5620+
/// Limit candidate packages for a specific package to those that were uploaded prior to the given date.
5621+
///
5622+
/// Accepts package-date pairs in the format `PACKAGE=DATE`, where `DATE` is an RFC 3339 timestamp
5623+
/// (e.g., `2006-12-02T02:07:43Z`) or local date (e.g., `2006-12-02`) in your system's configured time zone.
5624+
///
5625+
/// Can be provided multiple times for different packages.
5626+
#[arg(long, help_heading = "Resolver options")]
5627+
pub exclude_newer_package: Option<Vec<ExcludeNewerPackageEntry>>,
5628+
55655629
/// The method to use when installing packages from the global cache.
55665630
///
55675631
/// This option is only used when building source distributions.
@@ -5751,6 +5815,15 @@ pub struct ResolverInstallerArgs {
57515815
#[arg(long, env = EnvVars::UV_EXCLUDE_NEWER, help_heading = "Resolver options")]
57525816
pub exclude_newer: Option<ExcludeNewer>,
57535817

5818+
/// Limit candidate packages for specific packages to those that were uploaded prior to the given date.
5819+
///
5820+
/// Accepts package-date pairs in the format `PACKAGE=DATE`, where `DATE` is an RFC 3339 timestamp
5821+
/// (e.g., `2006-12-02T02:07:43Z`) or local date (e.g., `2006-12-02`) in your system's configured time zone.
5822+
///
5823+
/// Can be provided multiple times for different packages.
5824+
#[arg(long, help_heading = "Resolver options")]
5825+
pub exclude_newer_package: Option<Vec<ExcludeNewerPackageEntry>>,
5826+
57545827
/// The method to use when installing packages from the global cache.
57555828
///
57565829
/// Defaults to `clone` (also known as Copy-on-Write) on macOS, and `hardlink` on Linux and

crates/uv-cli/src/options.rs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,36 @@ use anstream::eprintln;
22

33
use uv_cache::Refresh;
44
use uv_configuration::ConfigSettings;
5-
use uv_resolver::PrereleaseMode;
5+
use uv_resolver::{ExcludeNewer, PrereleaseMode};
66
use uv_settings::{Combine, PipOptions, ResolverInstallerOptions, ResolverOptions};
77
use uv_warnings::owo_colors::OwoColorize;
88

99
use crate::{
10-
BuildOptionsArgs, FetchArgs, IndexArgs, InstallerArgs, Maybe, RefreshArgs, ResolverArgs,
11-
ResolverInstallerArgs,
10+
BuildOptionsArgs, ExcludeNewerPackageEntry, FetchArgs, IndexArgs, InstallerArgs, Maybe,
11+
RefreshArgs, ResolverArgs, ResolverInstallerArgs,
1212
};
1313

14+
/// Merge an existing `ExcludeNewer` configuration with package-specific entries.
15+
fn merge_exclude_newer(
16+
base: Option<ExcludeNewer>,
17+
package_entries: Option<Vec<ExcludeNewerPackageEntry>>,
18+
) -> Option<ExcludeNewer> {
19+
let package_entries = package_entries.unwrap_or_default();
20+
21+
if base.is_none() && package_entries.is_empty() {
22+
return None;
23+
}
24+
25+
let mut result = base.unwrap_or_default();
26+
27+
// Add package-specific entries
28+
for entry in package_entries {
29+
result.package.insert(entry.package, entry.timestamp);
30+
}
31+
32+
Some(result)
33+
}
34+
1435
/// Given a boolean flag pair (like `--upgrade` and `--no-upgrade`), resolve the value of the flag.
1536
pub fn flag(yes: bool, no: bool, name: &str) -> Option<bool> {
1637
match (yes, no) {
@@ -68,6 +89,7 @@ impl From<ResolverArgs> for PipOptions {
6889
exclude_newer,
6990
link_mode,
7091
no_sources,
92+
exclude_newer_package,
7193
} = args;
7294

7395
Self {
@@ -86,7 +108,7 @@ impl From<ResolverArgs> for PipOptions {
86108
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
87109
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
88110
no_build_isolation_package: Some(no_build_isolation_package),
89-
exclude_newer,
111+
exclude_newer: merge_exclude_newer(exclude_newer, exclude_newer_package),
90112
link_mode,
91113
no_sources: if no_sources { Some(true) } else { None },
92114
..PipOptions::from(index_args)
@@ -111,6 +133,7 @@ impl From<InstallerArgs> for PipOptions {
111133
compile_bytecode,
112134
no_compile_bytecode,
113135
no_sources,
136+
exclude_newer_package: _,
114137
} = args;
115138

116139
Self {
@@ -155,6 +178,7 @@ impl From<ResolverInstallerArgs> for PipOptions {
155178
compile_bytecode,
156179
no_compile_bytecode,
157180
no_sources,
181+
exclude_newer_package,
158182
} = args;
159183

160184
Self {
@@ -175,7 +199,7 @@ impl From<ResolverInstallerArgs> for PipOptions {
175199
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
176200
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
177201
no_build_isolation_package: Some(no_build_isolation_package),
178-
exclude_newer,
202+
exclude_newer: merge_exclude_newer(exclude_newer, exclude_newer_package),
179203
link_mode,
180204
compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode"),
181205
no_sources: if no_sources { Some(true) } else { None },
@@ -266,6 +290,7 @@ pub fn resolver_options(
266290
exclude_newer,
267291
link_mode,
268292
no_sources,
293+
exclude_newer_package,
269294
} = resolver_args;
270295

271296
let BuildOptionsArgs {
@@ -323,7 +348,7 @@ pub fn resolver_options(
323348
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
324349
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
325350
no_build_isolation_package: Some(no_build_isolation_package),
326-
exclude_newer,
351+
exclude_newer: merge_exclude_newer(exclude_newer, exclude_newer_package),
327352
link_mode,
328353
no_build: flag(no_build, build, "build"),
329354
no_build_package: Some(no_build_package),
@@ -357,6 +382,7 @@ pub fn resolver_installer_options(
357382
no_build_isolation_package,
358383
build_isolation,
359384
exclude_newer,
385+
exclude_newer_package,
360386
link_mode,
361387
compile_bytecode,
362388
no_compile_bytecode,
@@ -434,7 +460,8 @@ pub fn resolver_installer_options(
434460
} else {
435461
Some(no_build_isolation_package)
436462
},
437-
exclude_newer,
463+
exclude_newer: merge_exclude_newer(exclude_newer, exclude_newer_package),
464+
exclude_newer_package: None,
438465
link_mode,
439466
compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode"),
440467
no_build: flag(no_build, build, "build"),

crates/uv-dispatch/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ impl BuildContext for BuildDispatch<'_> {
224224
let resolver = Resolver::new(
225225
Manifest::simple(requirements.to_vec()).with_constraints(self.constraints.clone()),
226226
OptionsBuilder::new()
227-
.exclude_newer(self.exclude_newer)
227+
.exclude_newer(self.exclude_newer.clone())
228228
.index_strategy(self.index_strategy)
229229
.build_options(self.build_options.clone())
230230
.flexibility(Flexibility::Fixed)

0 commit comments

Comments
 (0)