Skip to content

Commit b7e412a

Browse files
committed
Add exclude-newer-package
1 parent 1d20530 commit b7e412a

File tree

28 files changed

+1081
-92
lines changed

28 files changed

+1081
-92
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: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ mod resolver {
9999
use uv_pypi_types::{Conflicts, ResolverMarkerEnvironment};
100100
use uv_python::Interpreter;
101101
use uv_resolver::{
102-
FlatIndex, InMemoryIndex, Manifest, OptionsBuilder, PythonRequirement, Resolver,
103-
ResolverEnvironment, ResolverOutput,
102+
ExcludeNewer, FlatIndex, InMemoryIndex, Manifest, OptionsBuilder, PythonRequirement,
103+
Resolver, ResolverEnvironment, ResolverOutput,
104104
};
105105
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy};
106106
use uv_workspace::WorkspaceCache;
@@ -144,21 +144,23 @@ mod resolver {
144144
let build_options = BuildOptions::default();
145145
let concurrency = Concurrency::default();
146146
let config_settings = ConfigSettings::default();
147-
let exclude_newer = Some(
147+
let exclude_newer = Some(ExcludeNewer::global(
148148
jiff::civil::date(2024, 9, 1)
149149
.to_zoned(jiff::tz::TimeZone::UTC)
150150
.unwrap()
151151
.timestamp()
152152
.into(),
153-
);
153+
));
154154
let build_constraints = Constraints::default();
155155
let flat_index = FlatIndex::default();
156156
let hashes = HashStrategy::default();
157157
let state = SharedState::default();
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: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ 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, ExcludeNewerTimestamp, ForkStrategy, PrereleaseMode, ResolutionMode,
24+
};
2325
use uv_static::EnvVars;
2426
use uv_torch::TorchMode;
2527
use uv_workspace::pyproject_mut::AddBoundsKind;
@@ -29,6 +31,34 @@ pub mod compat;
2931
pub mod options;
3032
pub mod version;
3133

34+
/// A package-specific exclude-newer entry.
35+
#[derive(Debug, Clone, PartialEq, Eq)]
36+
pub struct ExcludeNewerPackageEntry {
37+
pub package: PackageName,
38+
pub timestamp: ExcludeNewerTimestamp,
39+
}
40+
41+
impl FromStr for ExcludeNewerPackageEntry {
42+
type Err = String;
43+
44+
/// Parses a [`ExcludeNewerPackageEntry`] from a string in the format `PACKAGE=DATE`.
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` value `{s}`: expected format `PACKAGE=DATE`"
49+
));
50+
};
51+
52+
let package = PackageName::from_str(package).map_err(|err| {
53+
format!("Invalid `exclude-newer-package` package name `{package}`: {err}")
54+
})?;
55+
let timestamp = ExcludeNewerTimestamp::from_str(date)
56+
.map_err(|err| format!("Invalid `exclude-newer-package` timestamp `{date}`: {err}"))?;
57+
58+
Ok(Self { package, timestamp })
59+
}
60+
}
61+
3262
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
3363
pub enum VersionFormat {
3464
/// Display the version as plain text.
@@ -2644,7 +2674,16 @@ pub struct VenvArgs {
26442674
/// Accepts both RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`) and local dates in the same
26452675
/// format (e.g., `2006-12-02`) in your system's configured time zone.
26462676
#[arg(long, env = EnvVars::UV_EXCLUDE_NEWER)]
2647-
pub exclude_newer: Option<ExcludeNewer>,
2677+
pub exclude_newer: Option<ExcludeNewerTimestamp>,
2678+
2679+
/// Limit candidate packages for a specific package to those that were uploaded prior to the given date.
2680+
///
2681+
/// Accepts package-date pairs in the format `PACKAGE=DATE`, where `DATE` is an RFC 3339 timestamp
2682+
/// (e.g., `2006-12-02T02:07:43Z`) or local date (e.g., `2006-12-02`) in your system's configured time zone.
2683+
///
2684+
/// Can be provided multiple times for different packages.
2685+
#[arg(long)]
2686+
pub exclude_newer_package: Option<Vec<ExcludeNewerPackageEntry>>,
26482687

26492688
/// The method to use when installing packages from the global cache.
26502689
///
@@ -4626,7 +4665,16 @@ pub struct ToolUpgradeArgs {
46264665
/// Accepts both RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`) and local dates in the same
46274666
/// format (e.g., `2006-12-02`) in your system's configured time zone.
46284667
#[arg(long, env = EnvVars::UV_EXCLUDE_NEWER, help_heading = "Resolver options")]
4629-
pub exclude_newer: Option<ExcludeNewer>,
4668+
pub exclude_newer: Option<ExcludeNewerTimestamp>,
4669+
4670+
/// Limit candidate packages for specific packages to those that were uploaded prior to the given date.
4671+
///
4672+
/// Accepts package-date pairs in the format `PACKAGE=DATE`, where `DATE` is an RFC 3339 timestamp
4673+
/// (e.g., `2006-12-02T02:07:43Z`) or local date (e.g., `2006-12-02`) in your system's configured time zone.
4674+
///
4675+
/// Can be provided multiple times for different packages.
4676+
#[arg(long, help_heading = "Resolver options")]
4677+
pub exclude_newer_package: Option<Vec<ExcludeNewerPackageEntry>>,
46304678

46314679
/// The method to use when installing packages from the global cache.
46324680
///
@@ -5367,7 +5415,16 @@ pub struct InstallerArgs {
53675415
/// Accepts both RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`) and local dates in the same
53685416
/// format (e.g., `2006-12-02`) in your system's configured time zone.
53695417
#[arg(long, env = EnvVars::UV_EXCLUDE_NEWER, help_heading = "Resolver options")]
5370-
pub exclude_newer: Option<ExcludeNewer>,
5418+
pub exclude_newer: Option<ExcludeNewerTimestamp>,
5419+
5420+
/// Limit candidate packages for specific packages to those that were uploaded prior to the given date.
5421+
///
5422+
/// Accepts package-date pairs in the format `PACKAGE=DATE`, where `DATE` is an RFC 3339 timestamp
5423+
/// (e.g., `2006-12-02T02:07:43Z`) or local date (e.g., `2006-12-02`) in your system's configured time zone.
5424+
///
5425+
/// Can be provided multiple times for different packages.
5426+
#[arg(long, help_heading = "Resolver options")]
5427+
pub exclude_newer_package: Option<Vec<ExcludeNewerPackageEntry>>,
53715428

53725429
/// The method to use when installing packages from the global cache.
53735430
///
@@ -5560,7 +5617,16 @@ pub struct ResolverArgs {
55605617
/// Accepts both RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`) and local dates in the same
55615618
/// format (e.g., `2006-12-02`) in your system's configured time zone.
55625619
#[arg(long, env = EnvVars::UV_EXCLUDE_NEWER, help_heading = "Resolver options")]
5563-
pub exclude_newer: Option<ExcludeNewer>,
5620+
pub exclude_newer: Option<ExcludeNewerTimestamp>,
5621+
5622+
/// Limit candidate packages for a specific package to those that were uploaded prior to the given date.
5623+
///
5624+
/// Accepts package-date pairs in the format `PACKAGE=DATE`, where `DATE` is an RFC 3339 timestamp
5625+
/// (e.g., `2006-12-02T02:07:43Z`) or local date (e.g., `2006-12-02`) in your system's configured time zone.
5626+
///
5627+
/// Can be provided multiple times for different packages.
5628+
#[arg(long, help_heading = "Resolver options")]
5629+
pub exclude_newer_package: Option<Vec<ExcludeNewerPackageEntry>>,
55645630

55655631
/// The method to use when installing packages from the global cache.
55665632
///
@@ -5749,7 +5815,16 @@ pub struct ResolverInstallerArgs {
57495815
/// Accepts both RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`) and local dates in the same
57505816
/// format (e.g., `2006-12-02`) in your system's configured time zone.
57515817
#[arg(long, env = EnvVars::UV_EXCLUDE_NEWER, help_heading = "Resolver options")]
5752-
pub exclude_newer: Option<ExcludeNewer>,
5818+
pub exclude_newer: Option<ExcludeNewerTimestamp>,
5819+
5820+
/// Limit candidate packages for specific packages to those that were uploaded prior to the given date.
5821+
///
5822+
/// Accepts package-date pairs in the format `PACKAGE=DATE`, where `DATE` is an RFC 3339 timestamp
5823+
/// (e.g., `2006-12-02T02:07:43Z`) or local date (e.g., `2006-12-02`) in your system's configured time zone.
5824+
///
5825+
/// Can be provided multiple times for different packages.
5826+
#[arg(long, help_heading = "Resolver options")]
5827+
pub exclude_newer_package: Option<Vec<ExcludeNewerPackageEntry>>,
57535828

57545829
/// The method to use when installing packages from the global cache.
57555830
///
@@ -5838,7 +5913,7 @@ pub struct FetchArgs {
58385913
/// Accepts both RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`) and local dates in the same
58395914
/// format (e.g., `2006-12-02`) in your system's configured time zone.
58405915
#[arg(long, env = EnvVars::UV_EXCLUDE_NEWER, help_heading = "Resolver options")]
5841-
pub exclude_newer: Option<ExcludeNewer>,
5916+
pub exclude_newer: Option<ExcludeNewerTimestamp>,
58425917
}
58435918

58445919
#[derive(Args)]

crates/uv-cli/src/options.rs

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,38 @@ use anstream::eprintln;
22

33
use uv_cache::Refresh;
44
use uv_configuration::ConfigSettings;
5-
use uv_resolver::PrereleaseMode;
5+
use uv_normalize::PackageName;
6+
use uv_resolver::{ExcludeNewer, ExcludeNewerTimestamp, PrereleaseMode};
67
use uv_settings::{Combine, PipOptions, ResolverInstallerOptions, ResolverOptions};
78
use uv_warnings::owo_colors::OwoColorize;
89

910
use crate::{
10-
BuildOptionsArgs, FetchArgs, IndexArgs, InstallerArgs, Maybe, RefreshArgs, ResolverArgs,
11-
ResolverInstallerArgs,
11+
BuildOptionsArgs, ExcludeNewerPackageEntry, FetchArgs, IndexArgs, InstallerArgs, Maybe,
12+
RefreshArgs, ResolverArgs, ResolverInstallerArgs,
1213
};
1314

15+
/// Merge an existing `ExcludeNewer` configuration with package-specific entries.
16+
fn merge_exclude_newer(
17+
base: Option<ExcludeNewer>,
18+
package_entries: Option<Vec<ExcludeNewerPackageEntry>>,
19+
) -> Option<ExcludeNewer> {
20+
let package_entries = package_entries.unwrap_or_default();
21+
22+
if base.is_none() && package_entries.is_empty() {
23+
return None;
24+
}
25+
26+
let result = base.unwrap_or_default();
27+
28+
// Add package-specific entries
29+
let package_map: Vec<(PackageName, ExcludeNewerTimestamp)> = package_entries
30+
.into_iter()
31+
.map(|entry| (entry.package, entry.timestamp))
32+
.collect();
33+
34+
ExcludeNewer::from_args(result.global, package_map)
35+
}
36+
1437
/// Given a boolean flag pair (like `--upgrade` and `--no-upgrade`), resolve the value of the flag.
1538
pub fn flag(yes: bool, no: bool, name: &str) -> Option<bool> {
1639
match (yes, no) {
@@ -68,6 +91,7 @@ impl From<ResolverArgs> for PipOptions {
6891
exclude_newer,
6992
link_mode,
7093
no_sources,
94+
exclude_newer_package,
7195
} = args;
7296

7397
Self {
@@ -87,6 +111,12 @@ impl From<ResolverArgs> for PipOptions {
87111
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
88112
no_build_isolation_package: Some(no_build_isolation_package),
89113
exclude_newer,
114+
exclude_newer_package: exclude_newer_package.map(|entries| {
115+
entries
116+
.into_iter()
117+
.map(|e| (e.package, e.timestamp))
118+
.collect()
119+
}),
90120
link_mode,
91121
no_sources: if no_sources { Some(true) } else { None },
92122
..PipOptions::from(index_args)
@@ -111,6 +141,7 @@ impl From<InstallerArgs> for PipOptions {
111141
compile_bytecode,
112142
no_compile_bytecode,
113143
no_sources,
144+
exclude_newer_package: _,
114145
} = args;
115146

116147
Self {
@@ -155,6 +186,7 @@ impl From<ResolverInstallerArgs> for PipOptions {
155186
compile_bytecode,
156187
no_compile_bytecode,
157188
no_sources,
189+
exclude_newer_package,
158190
} = args;
159191

160192
Self {
@@ -175,7 +207,11 @@ impl From<ResolverInstallerArgs> for PipOptions {
175207
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
176208
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
177209
no_build_isolation_package: Some(no_build_isolation_package),
178-
exclude_newer,
210+
exclude_newer: merge_exclude_newer(
211+
exclude_newer.map(ExcludeNewer::global),
212+
exclude_newer_package,
213+
)
214+
.and_then(|en| en.global),
179215
link_mode,
180216
compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode"),
181217
no_sources: if no_sources { Some(true) } else { None },
@@ -266,6 +302,7 @@ pub fn resolver_options(
266302
exclude_newer,
267303
link_mode,
268304
no_sources,
305+
exclude_newer_package,
269306
} = resolver_args;
270307

271308
let BuildOptionsArgs {
@@ -323,7 +360,10 @@ pub fn resolver_options(
323360
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
324361
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
325362
no_build_isolation_package: Some(no_build_isolation_package),
326-
exclude_newer,
363+
exclude_newer: merge_exclude_newer(
364+
exclude_newer.map(ExcludeNewer::global),
365+
exclude_newer_package,
366+
),
327367
link_mode,
328368
no_build: flag(no_build, build, "build"),
329369
no_build_package: Some(no_build_package),
@@ -357,6 +397,7 @@ pub fn resolver_installer_options(
357397
no_build_isolation_package,
358398
build_isolation,
359399
exclude_newer,
400+
exclude_newer_package,
360401
link_mode,
361402
compile_bytecode,
362403
no_compile_bytecode,
@@ -435,6 +476,12 @@ pub fn resolver_installer_options(
435476
Some(no_build_isolation_package)
436477
},
437478
exclude_newer,
479+
exclude_newer_package: exclude_newer_package.map(|entries| {
480+
entries
481+
.into_iter()
482+
.map(|e| (e.package, e.timestamp))
483+
.collect()
484+
}),
438485
link_mode,
439486
compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode"),
440487
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)