Skip to content

Commit ed44c3c

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

File tree

28 files changed

+1031
-92
lines changed

28 files changed

+1031
-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: 83 additions & 7 deletions
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, 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,34 @@ pub mod compat;
2932
pub mod options;
3033
pub mod version;
3134

35+
/// A package-specific exclude-newer entry.
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+
/// Parses a [`ExcludeNewerPackageEntry`] from a string in the format `PACKAGE=DATE`.
46+
fn from_str(s: &str) -> Result<Self, Self::Err> {
47+
let Some((package, date)) = s.split_once('=') else {
48+
return Err(format!(
49+
"Invalid `exclude-newer-package` value `{s}`: expected format `PACKAGE=DATE`"
50+
));
51+
};
52+
53+
let package = PackageName::from_str(package).map_err(|err| {
54+
format!("Invalid `exclude-newer-package` package name `{package}`: {err}")
55+
})?;
56+
let timestamp = ExcludeNewerTimestamp::from_str(date)
57+
.map_err(|err| format!("Invalid `exclude-newer-package` timestamp `{date}`: {err}"))?;
58+
59+
Ok(Self { package, timestamp })
60+
}
61+
}
62+
3263
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
3364
pub enum VersionFormat {
3465
/// Display the version as plain text.
@@ -2644,7 +2675,16 @@ pub struct VenvArgs {
26442675
/// Accepts both RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`) and local dates in the same
26452676
/// format (e.g., `2006-12-02`) in your system's configured time zone.
26462677
#[arg(long, env = EnvVars::UV_EXCLUDE_NEWER)]
2647-
pub exclude_newer: Option<ExcludeNewer>,
2678+
pub exclude_newer: Option<ExcludeNewerTimestamp>,
2679+
2680+
/// Limit candidate packages for a specific package to those that were uploaded prior to the given date.
2681+
///
2682+
/// Accepts package-date pairs in the format `PACKAGE=DATE`, where `DATE` is an RFC 3339 timestamp
2683+
/// (e.g., `2006-12-02T02:07:43Z`) or local date (e.g., `2006-12-02`) in your system's configured time zone.
2684+
///
2685+
/// Can be provided multiple times for different packages.
2686+
#[arg(long)]
2687+
pub exclude_newer_package: Option<Vec<ExcludeNewerPackageEntry>>,
26482688

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

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

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

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

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

58445920
#[derive(Args)]

crates/uv-cli/src/options.rs

Lines changed: 46 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_resolver::{ExcludeNewer, ExcludeNewerTimestamp, PrereleaseMode};
6+
use uv_normalize::PackageName;
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,8 @@ 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(exclude_newer.map(ExcludeNewer::global), exclude_newer_package)
211+
.and_then(|en| en.global),
179212
link_mode,
180213
compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode"),
181214
no_sources: if no_sources { Some(true) } else { None },
@@ -266,6 +299,7 @@ pub fn resolver_options(
266299
exclude_newer,
267300
link_mode,
268301
no_sources,
302+
exclude_newer_package,
269303
} = resolver_args;
270304

271305
let BuildOptionsArgs {
@@ -323,7 +357,7 @@ pub fn resolver_options(
323357
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
324358
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
325359
no_build_isolation_package: Some(no_build_isolation_package),
326-
exclude_newer,
360+
exclude_newer: merge_exclude_newer(exclude_newer.map(ExcludeNewer::global), exclude_newer_package),
327361
link_mode,
328362
no_build: flag(no_build, build, "build"),
329363
no_build_package: Some(no_build_package),
@@ -357,6 +391,7 @@ pub fn resolver_installer_options(
357391
no_build_isolation_package,
358392
build_isolation,
359393
exclude_newer,
394+
exclude_newer_package,
360395
link_mode,
361396
compile_bytecode,
362397
no_compile_bytecode,
@@ -435,6 +470,12 @@ pub fn resolver_installer_options(
435470
Some(no_build_isolation_package)
436471
},
437472
exclude_newer,
473+
exclude_newer_package: exclude_newer_package.map(|entries| {
474+
entries
475+
.into_iter()
476+
.map(|e| (e.package, e.timestamp))
477+
.collect()
478+
}),
438479
link_mode,
439480
compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode"),
440481
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)