From 3569e1f987e664375ac0665027baf26afd1cf76f Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Mon, 16 Sep 2024 19:07:06 -0400 Subject: [PATCH] Rename to metadata-overrides --- Cargo.lock | 1 - crates/bench/benches/uv.rs | 6 +- crates/distribution-types/src/lib.rs | 4 +- .../src/metadata_override.rs | 76 +++++++++ .../distribution-types/src/static_metadata.rs | 32 ---- crates/pypi-types/Cargo.toml | 1 - crates/pypi-types/src/metadata.rs | 19 +-- crates/uv-cli/src/options.rs | 4 +- crates/uv-dispatch/src/lib.rs | 12 +- .../src/distribution_database.rs | 4 +- crates/uv-resolver/src/lock/mod.rs | 37 ++-- ...r__lock__tests__hash_optional_missing.snap | 2 +- ...r__lock__tests__hash_optional_present.snap | 2 +- ...r__lock__tests__hash_required_present.snap | 2 +- ...missing_dependency_source_unambiguous.snap | 2 +- ...dependency_source_version_unambiguous.snap | 2 +- ...issing_dependency_version_unambiguous.snap | 2 +- ...lock__tests__source_direct_has_subdir.snap | 2 +- ..._lock__tests__source_direct_no_subdir.snap | 2 +- ...solver__lock__tests__source_directory.snap | 2 +- ...esolver__lock__tests__source_editable.snap | 2 +- crates/uv-settings/Cargo.toml | 2 +- crates/uv-settings/src/settings.rs | 52 +++--- crates/uv-types/src/traits.rs | 6 +- crates/uv-workspace/Cargo.toml | 4 - crates/uv/src/commands/build.rs | 4 +- crates/uv/src/commands/pip/compile.rs | 6 +- crates/uv/src/commands/pip/install.rs | 6 +- crates/uv/src/commands/pip/sync.rs | 6 +- crates/uv/src/commands/project/add.rs | 2 +- crates/uv/src/commands/project/lock.rs | 14 +- crates/uv/src/commands/project/mod.rs | 16 +- crates/uv/src/commands/project/sync.rs | 4 +- crates/uv/src/commands/venv.rs | 10 +- crates/uv/src/lib.rs | 12 +- crates/uv/src/settings.rs | 42 ++--- crates/uv/tests/lock.rs | 40 ++++- crates/uv/tests/show_settings.rs | 70 ++++++++ docs/concepts/projects.md | 50 +++++- docs/concepts/resolution.md | 81 +++++++-- docs/reference/settings.md | 160 +++++++++--------- uv.schema.json | 44 ++--- 42 files changed, 539 insertions(+), 308 deletions(-) create mode 100644 crates/distribution-types/src/metadata_override.rs delete mode 100644 crates/distribution-types/src/static_metadata.rs diff --git a/Cargo.lock b/Cargo.lock index ee28b31035928..37b3ada25853e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2814,7 +2814,6 @@ dependencies = [ "pep508_rs", "regex", "rkyv", - "schemars", "serde", "thiserror", "toml", diff --git a/crates/bench/benches/uv.rs b/crates/bench/benches/uv.rs index e142ea394a08a..831151dc5421e 100644 --- a/crates/bench/benches/uv.rs +++ b/crates/bench/benches/uv.rs @@ -83,7 +83,7 @@ mod resolver { use anyhow::Result; - use distribution_types::{IndexCapabilities, IndexLocations, StaticMetadata}; + use distribution_types::{IndexCapabilities, IndexLocations, MetadataOverrides}; use install_wheel_rs::linker::LinkMode; use pep440_rs::Version; use pep508_rs::{MarkerEnvironment, MarkerEnvironmentBuilder}; @@ -161,7 +161,7 @@ mod resolver { let installed_packages = EmptyInstalledPackages; let options = OptionsBuilder::new().exclude_newer(exclude_newer).build(); let sources = SourceStrategy::default(); - let static_metadata = StaticMetadata::default(); + let metadata_override = MetadataOverrides::default(); let python_requirement = if universal { PythonRequirement::from_requires_python( @@ -179,7 +179,7 @@ mod resolver { interpreter, &index_locations, &flat_index, - &static_metadata, + &metadata_override, &index, &git, &capabilities, diff --git a/crates/distribution-types/src/lib.rs b/crates/distribution-types/src/lib.rs index 3a86fd9080cb0..9c26bed76088e 100644 --- a/crates/distribution-types/src/lib.rs +++ b/crates/distribution-types/src/lib.rs @@ -58,11 +58,11 @@ pub use crate::hash::*; pub use crate::id::*; pub use crate::index_url::*; pub use crate::installed::*; +pub use crate::metadata_override::*; pub use crate::prioritized_distribution::*; pub use crate::resolution::*; pub use crate::resolved::*; pub use crate::specified_requirement::*; -pub use crate::static_metadata::*; pub use crate::traits::*; mod annotation; @@ -76,11 +76,11 @@ mod hash; mod id; mod index_url; mod installed; +mod metadata_override; mod prioritized_distribution; mod resolution; mod resolved; mod specified_requirement; -mod static_metadata; mod traits; #[derive(Debug, Clone)] diff --git a/crates/distribution-types/src/metadata_override.rs b/crates/distribution-types/src/metadata_override.rs new file mode 100644 index 0000000000000..cc2b8751d07d9 --- /dev/null +++ b/crates/distribution-types/src/metadata_override.rs @@ -0,0 +1,76 @@ +use pep440_rs::{Version, VersionSpecifiers}; +use pep508_rs::Requirement; +use pypi_types::{Metadata23, VerbatimParsedUrl}; +use rustc_hash::FxHashMap; +use serde::{Deserialize, Serialize}; +use uv_normalize::{ExtraName, PackageName}; + +/// Pre-defined [`MetadataOverride`] entries, indexed by [`PackageName`] and [`Version`]. +#[derive(Debug, Clone, Default)] +pub struct MetadataOverrides(FxHashMap>); + +impl MetadataOverrides { + /// Index a set of [`MetadataOverride`] entries by [`PackageName`] and [`Version`]. + pub fn from_entries(entries: impl IntoIterator) -> Self { + let mut map = Self::default(); + for entry in entries { + map.0.entry(entry.name.clone()).or_default().push(entry); + } + map + } + + /// Retrieve a [`MetadataOverride`] entry by [`PackageName`] and [`Version`]. + pub fn get(&self, package: &PackageName, version: &Version) -> Option { + let versions = self.0.get(package)?; + + // Search for an exact, then a global match. + let metadata = versions + .iter() + .find(|v| v.version.as_ref() == Some(version)) + .or_else(|| versions.iter().find(|v| v.version.is_none()))?; + + Some(Metadata23 { + name: metadata.name.clone(), + version: version.clone(), + requires_dist: metadata.requires_dist.clone(), + requires_python: metadata.requires_python.clone(), + provides_extras: metadata.provides_extras.clone(), + }) + } + + /// Retrieve all [`MetadataOverride`] entries. + pub fn values(&self) -> impl Iterator { + self.0.values().flatten() + } +} + +/// A subset of the Python Package Metadata 2.3 standard as specified in +/// . +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +#[serde(rename_all = "kebab-case")] +pub struct MetadataOverride { + // Mandatory fields + pub name: PackageName, + #[cfg_attr( + feature = "schemars", + schemars( + with = "String", + description = "PEP 440-style package version, e.g., `1.2.3`" + ) + )] + pub version: Option, + // Optional fields + #[serde(default)] + pub requires_dist: Vec>, + #[cfg_attr( + feature = "schemars", + schemars( + with = "Option", + description = "PEP 508-style Python requirement, e.g., `>=3.10`" + ) + )] + pub requires_python: Option, + #[serde(default)] + pub provides_extras: Vec, +} diff --git a/crates/distribution-types/src/static_metadata.rs b/crates/distribution-types/src/static_metadata.rs deleted file mode 100644 index eac484b0ce4d9..0000000000000 --- a/crates/distribution-types/src/static_metadata.rs +++ /dev/null @@ -1,32 +0,0 @@ -use pep440_rs::Version; -use pypi_types::Metadata23; -use rustc_hash::FxHashMap; -use uv_normalize::PackageName; - -/// Pre-defined [`Metadata23`] entries, indexed by [`PackageName`] and [`Version`]. -#[derive(Debug, Clone, Default)] -pub struct StaticMetadata(FxHashMap>); - -impl StaticMetadata { - /// Index a set of [`Metadata23`] entries by [`PackageName`] and [`Version`]. - pub fn from_entries(entries: impl IntoIterator) -> Self { - let mut map = Self::default(); - for entry in entries { - map.0 - .entry(entry.name.clone()) - .or_default() - .insert(entry.version.clone(), entry); - } - map - } - - /// Retrieve a [`Metadata23`] entry by [`PackageName`] and [`Version`]. - pub fn get(&self, package: &PackageName, version: &Version) -> Option<&Metadata23> { - self.0.get(package).and_then(|map| map.get(version)) - } - - /// Retrieve all [`Metadata23`] entries. - pub fn values(&self) -> impl Iterator { - self.0.values().flat_map(|map| map.values()) - } -} diff --git a/crates/pypi-types/Cargo.toml b/crates/pypi-types/Cargo.toml index 41462084c011b..3f27358d2320a 100644 --- a/crates/pypi-types/Cargo.toml +++ b/crates/pypi-types/Cargo.toml @@ -26,7 +26,6 @@ jiff = { workspace = true, features = ["serde"] } mailparse = { workspace = true } regex = { workspace = true } rkyv = { workspace = true } -schemars = { workspace = true, optional = true } serde = { workspace = true } thiserror = { workspace = true } toml = { workspace = true } diff --git a/crates/pypi-types/src/metadata.rs b/crates/pypi-types/src/metadata.rs index 8dd5bf53653c8..f81b4fe1dee6e 100644 --- a/crates/pypi-types/src/metadata.rs +++ b/crates/pypi-types/src/metadata.rs @@ -26,32 +26,15 @@ use crate::{LenientVersionSpecifiers, VerbatimParsedUrl}; /// fields that are relevant to dependency resolution. /// /// At present, we support up to version 2.3 of the metadata specification. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "kebab-case")] pub struct Metadata23 { // Mandatory fields pub name: PackageName, - #[cfg_attr( - feature = "schemars", - schemars( - with = "String", - description = "PEP 440-style package version, e.g., `1.2.3`" - ) - )] pub version: Version, // Optional fields - #[serde(default)] pub requires_dist: Vec>, - #[cfg_attr( - feature = "schemars", - schemars( - with = "Option", - description = "PEP 508-style Python requirement, e.g., `>=3.10`" - ) - )] pub requires_python: Option, - #[serde(default)] pub provides_extras: Vec, } diff --git a/crates/uv-cli/src/options.rs b/crates/uv-cli/src/options.rs index 19470a0bbe59a..8153cf1f7e1ac 100644 --- a/crates/uv-cli/src/options.rs +++ b/crates/uv-cli/src/options.rs @@ -271,7 +271,7 @@ pub fn resolver_options( } else { prerelease }, - static_metadata: None, + metadata_override: None, config_settings: config_setting .map(|config_settings| config_settings.into_iter().collect::()), no_build_isolation: flag(no_build_isolation, build_isolation), @@ -365,7 +365,7 @@ pub fn resolver_installer_options( } else { prerelease }, - static_metadata: None, + metadata_override: None, config_settings: config_setting .map(|config_settings| config_settings.into_iter().collect::()), no_build_isolation: flag(no_build_isolation, build_isolation), diff --git a/crates/uv-dispatch/src/lib.rs b/crates/uv-dispatch/src/lib.rs index 3541590301dc6..b38b433abc2a1 100644 --- a/crates/uv-dispatch/src/lib.rs +++ b/crates/uv-dispatch/src/lib.rs @@ -12,7 +12,7 @@ use rustc_hash::FxHashMap; use tracing::{debug, instrument}; use distribution_types::{ - CachedDist, IndexCapabilities, IndexLocations, Name, Resolution, SourceDist, StaticMetadata, + CachedDist, IndexCapabilities, IndexLocations, MetadataOverrides, Name, Resolution, SourceDist, VersionOrUrlRef, }; use pypi_types::Requirement; @@ -46,7 +46,7 @@ pub struct BuildDispatch<'a> { index: &'a InMemoryIndex, git: &'a GitResolver, capabilities: &'a IndexCapabilities, - static_metadata: &'a StaticMetadata, + metadata_override: &'a MetadataOverrides, in_flight: &'a InFlight, build_isolation: BuildIsolation<'a>, link_mode: install_wheel_rs::linker::LinkMode, @@ -68,7 +68,7 @@ impl<'a> BuildDispatch<'a> { interpreter: &'a Interpreter, index_locations: &'a IndexLocations, flat_index: &'a FlatIndex, - static_metadata: &'a StaticMetadata, + metadata_override: &'a MetadataOverrides, index: &'a InMemoryIndex, git: &'a GitResolver, capabilities: &'a IndexCapabilities, @@ -93,7 +93,7 @@ impl<'a> BuildDispatch<'a> { index, git, capabilities, - static_metadata, + metadata_override, in_flight, index_strategy, config_settings, @@ -140,8 +140,8 @@ impl<'a> BuildContext for BuildDispatch<'a> { self.capabilities } - fn static_metadata(&self) -> &StaticMetadata { - self.static_metadata + fn metadata_override(&self) -> &MetadataOverrides { + self.metadata_override } fn build_options(&self) -> &BuildOptions { diff --git a/crates/uv-distribution/src/distribution_database.rs b/crates/uv-distribution/src/distribution_database.rs index 8c2102902ba95..5c3e3ff778bdd 100644 --- a/crates/uv-distribution/src/distribution_database.rs +++ b/crates/uv-distribution/src/distribution_database.rs @@ -363,7 +363,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> { // If the metadata was provided by the user directly, prefer it. if let Some(metadata) = self .build_context - .static_metadata() + .metadata_override() .get(dist.name(), dist.version()) { return Ok(ArchiveMetadata::from_metadata23(metadata.clone())); @@ -429,7 +429,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> { if let Some(version) = dist.version() { if let Some(metadata) = self .build_context - .static_metadata() + .metadata_override() .get(dist.name(), version) { return Ok(ArchiveMetadata::from_metadata23(metadata.clone())); diff --git a/crates/uv-resolver/src/lock/mod.rs b/crates/uv-resolver/src/lock/mod.rs index e7372743acccc..e775767d8f9d8 100644 --- a/crates/uv-resolver/src/lock/mod.rs +++ b/crates/uv-resolver/src/lock/mod.rs @@ -19,15 +19,15 @@ use distribution_filename::{DistExtension, ExtensionError, SourceDistExtension, use distribution_types::{ BuiltDist, DirectUrlBuiltDist, DirectUrlSourceDist, DirectorySourceDist, Dist, DistributionMetadata, FileLocation, FlatIndexLocation, GitSourceDist, HashPolicy, - IndexLocations, IndexUrl, Name, PathBuiltDist, PathSourceDist, RegistryBuiltDist, - RegistryBuiltWheel, RegistrySourceDist, RemoteSource, Resolution, ResolvedDist, StaticMetadata, - ToUrlError, UrlString, + IndexLocations, IndexUrl, MetadataOverride, MetadataOverrides, Name, PathBuiltDist, + PathSourceDist, RegistryBuiltDist, RegistryBuiltWheel, RegistrySourceDist, RemoteSource, + Resolution, ResolvedDist, ToUrlError, UrlString, }; use pep440_rs::Version; use pep508_rs::{split_scheme, MarkerEnvironment, MarkerTree, VerbatimUrl, VerbatimUrlError}; use platform_tags::{TagCompatibility, TagPriority, Tags}; use pypi_types::{ - redact_git_credentials, HashDigest, Metadata23, ParsedArchiveUrl, ParsedGitUrl, Requirement, + redact_git_credentials, HashDigest, ParsedArchiveUrl, ParsedGitUrl, Requirement, RequirementSource, ResolverMarkerEnvironment, }; use uv_configuration::{BuildOptions, DevSpecification, ExtrasSpecification, InstallOptions}; @@ -795,12 +795,14 @@ impl Lock { manifest_table.insert("overrides", value(overrides)); } - if !self.manifest.static_metadata.is_empty() { + if !self.manifest.metadata_override.is_empty() { let mut tables = ArrayOfTables::new(); - for metadata in &self.manifest.static_metadata { + for metadata in &self.manifest.metadata_override { let mut table = Table::new(); table.insert("name", value(metadata.name.to_string())); - table.insert("version", value(metadata.version.to_string())); + if let Some(version) = metadata.version.as_ref() { + table.insert("version", value(version.to_string())); + } if !metadata.requires_dist.is_empty() { table.insert( "requires-dist", @@ -824,7 +826,7 @@ impl Lock { } tables.push(table); } - manifest_table.insert("static-metadata", Item::ArrayOfTables(tables)); + manifest_table.insert("metadata-override", Item::ArrayOfTables(tables)); } if !manifest_table.is_empty() { @@ -913,7 +915,7 @@ impl Lock { requirements: &[Requirement], constraints: &[Requirement], overrides: &[Requirement], - static_metadata: &StaticMetadata, + metadata_override: &MetadataOverrides, indexes: Option<&IndexLocations>, build_options: &BuildOptions, tags: &Tags, @@ -1030,8 +1032,8 @@ impl Lock { // Validate that the lockfile was generated with the same static metadata. { - let expected = static_metadata.values().cloned().collect::>(); - let actual = &self.manifest.static_metadata; + let expected = metadata_override.values().cloned().collect::>(); + let actual = &self.manifest.metadata_override; if expected != *actual { return Ok(SatisfiesResult::MismatchedStaticMetadata(expected, actual)); } @@ -1292,7 +1294,10 @@ pub enum SatisfiesResult<'lock> { /// The lockfile uses a different set of overrides. MismatchedOverrides(BTreeSet, BTreeSet), /// The lockfile uses different static metadata. - MismatchedStaticMetadata(BTreeSet, &'lock BTreeSet), + MismatchedStaticMetadata( + BTreeSet, + &'lock BTreeSet, + ), /// The lockfile is missing a workspace member. MissingRoot(PackageName), /// The lockfile referenced a remote index that was not provided @@ -1348,7 +1353,7 @@ pub struct ResolverManifest { overrides: BTreeSet, /// The static metadata provided to the resolver. #[serde(default)] - static_metadata: BTreeSet, + metadata_override: BTreeSet, } impl ResolverManifest { @@ -1359,14 +1364,14 @@ impl ResolverManifest { requirements: impl IntoIterator, constraints: impl IntoIterator, overrides: impl IntoIterator, - static_metadata: impl IntoIterator, + metadata_override: impl IntoIterator, ) -> Self { Self { members: members.into_iter().collect(), requirements: requirements.into_iter().collect(), constraints: constraints.into_iter().collect(), overrides: overrides.into_iter().collect(), - static_metadata: static_metadata.into_iter().collect(), + metadata_override: metadata_override.into_iter().collect(), } } @@ -1389,7 +1394,7 @@ impl ResolverManifest { .into_iter() .map(|requirement| requirement.relative_to(workspace.install_path())) .collect::, _>>()?, - static_metadata: self.static_metadata, + metadata_override: self.metadata_override, }) } } diff --git a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__hash_optional_missing.snap b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__hash_optional_missing.snap index a013286c46324..46e5ac2065fa2 100644 --- a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__hash_optional_missing.snap +++ b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__hash_optional_missing.snap @@ -105,7 +105,7 @@ Ok( requirements: {}, constraints: {}, overrides: {}, - static_metadata: {}, + metadata_override: {}, }, }, ) diff --git a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__hash_optional_present.snap b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__hash_optional_present.snap index 456b898c4c0d2..3696acdecce2d 100644 --- a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__hash_optional_present.snap +++ b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__hash_optional_present.snap @@ -112,7 +112,7 @@ Ok( requirements: {}, constraints: {}, overrides: {}, - static_metadata: {}, + metadata_override: {}, }, }, ) diff --git a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__hash_required_present.snap b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__hash_required_present.snap index eca2002001d59..aa7a8f56515c4 100644 --- a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__hash_required_present.snap +++ b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__hash_required_present.snap @@ -104,7 +104,7 @@ Ok( requirements: {}, constraints: {}, overrides: {}, - static_metadata: {}, + metadata_override: {}, }, }, ) diff --git a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__missing_dependency_source_unambiguous.snap b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__missing_dependency_source_unambiguous.snap index 4ab54c3fd9374..e333b421adfaa 100644 --- a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__missing_dependency_source_unambiguous.snap +++ b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__missing_dependency_source_unambiguous.snap @@ -176,7 +176,7 @@ Ok( requirements: {}, constraints: {}, overrides: {}, - static_metadata: {}, + metadata_override: {}, }, }, ) diff --git a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__missing_dependency_source_version_unambiguous.snap b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__missing_dependency_source_version_unambiguous.snap index 4ab54c3fd9374..e333b421adfaa 100644 --- a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__missing_dependency_source_version_unambiguous.snap +++ b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__missing_dependency_source_version_unambiguous.snap @@ -176,7 +176,7 @@ Ok( requirements: {}, constraints: {}, overrides: {}, - static_metadata: {}, + metadata_override: {}, }, }, ) diff --git a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__missing_dependency_version_unambiguous.snap b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__missing_dependency_version_unambiguous.snap index 4ab54c3fd9374..e333b421adfaa 100644 --- a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__missing_dependency_version_unambiguous.snap +++ b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__missing_dependency_version_unambiguous.snap @@ -176,7 +176,7 @@ Ok( requirements: {}, constraints: {}, overrides: {}, - static_metadata: {}, + metadata_override: {}, }, }, ) diff --git a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__source_direct_has_subdir.snap b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__source_direct_has_subdir.snap index 88c0789fb8c93..44bc6d302a921 100644 --- a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__source_direct_has_subdir.snap +++ b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__source_direct_has_subdir.snap @@ -85,7 +85,7 @@ Ok( requirements: {}, constraints: {}, overrides: {}, - static_metadata: {}, + metadata_override: {}, }, }, ) diff --git a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__source_direct_no_subdir.snap b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__source_direct_no_subdir.snap index 3d484f60819c2..caf8d4e5c5eaa 100644 --- a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__source_direct_no_subdir.snap +++ b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__source_direct_no_subdir.snap @@ -81,7 +81,7 @@ Ok( requirements: {}, constraints: {}, overrides: {}, - static_metadata: {}, + metadata_override: {}, }, }, ) diff --git a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__source_directory.snap b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__source_directory.snap index 4ad77ef9cdfef..f56c270275c00 100644 --- a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__source_directory.snap +++ b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__source_directory.snap @@ -71,7 +71,7 @@ Ok( requirements: {}, constraints: {}, overrides: {}, - static_metadata: {}, + metadata_override: {}, }, }, ) diff --git a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__source_editable.snap b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__source_editable.snap index ed0bd2b158f26..fc44efc76467f 100644 --- a/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__source_editable.snap +++ b/crates/uv-resolver/src/lock/snapshots/uv_resolver__lock__tests__source_editable.snap @@ -71,7 +71,7 @@ Ok( requirements: {}, constraints: {}, overrides: {}, - static_metadata: {}, + metadata_override: {}, }, }, ) diff --git a/crates/uv-settings/Cargo.toml b/crates/uv-settings/Cargo.toml index cd4667c5c3ccd..6be4b49b758f0 100644 --- a/crates/uv-settings/Cargo.toml +++ b/crates/uv-settings/Cargo.toml @@ -16,7 +16,7 @@ workspace = true distribution-types = { workspace = true, features = ["schemars"] } install-wheel-rs = { workspace = true, features = ["schemars", "clap"] } pep508_rs = { workspace = true } -pypi-types = { workspace = true, features = ["schemars"] } +pypi-types = { workspace = true } uv-cache-info = { workspace = true, features = ["schemars"] } uv-configuration = { workspace = true, features = ["schemars", "clap"] } uv-fs = { workspace = true } diff --git a/crates/uv-settings/src/settings.rs b/crates/uv-settings/src/settings.rs index 1207d578bb437..1e2632221d73d 100644 --- a/crates/uv-settings/src/settings.rs +++ b/crates/uv-settings/src/settings.rs @@ -2,10 +2,10 @@ use std::{fmt::Debug, num::NonZeroUsize, path::PathBuf}; use serde::{Deserialize, Serialize}; -use distribution_types::{FlatIndexLocation, IndexUrl}; +use distribution_types::{FlatIndexLocation, IndexUrl, MetadataOverride}; use install_wheel_rs::linker::LinkMode; use pep508_rs::Requirement; -use pypi_types::{Metadata23, SupportedEnvironments, VerbatimParsedUrl}; +use pypi_types::{SupportedEnvironments, VerbatimParsedUrl}; use uv_cache_info::CacheKey; use uv_configuration::{ ConfigSettings, IndexStrategy, KeyringProviderType, PackageNameSpecifier, TargetTriple, @@ -288,7 +288,7 @@ pub struct ResolverOptions { pub allow_insecure_host: Option>, pub resolution: Option, pub prerelease: Option, - pub static_metadata: Option>, + pub metadata_override: Option>, pub config_settings: Option, pub exclude_newer: Option, pub link_mode: Option, @@ -441,26 +441,29 @@ pub struct ResolverInstallerOptions { possible_values = true )] pub prerelease: Option, - /// Pre-defined static metadata for dependencies of the project (direct or transitive). + /// Pre-defined static metadata for dependencies of the project (direct or transitive). When + /// provided, enables the resolver to use the specified metadata instead of querying the + /// registry or building the relevant package from source. /// /// Metadata should be provided in adherence with the [Metadata 2.3](https://packaging.python.org/en/latest/specifications/core-metadata/) /// standard, though only the following fields are respected: /// - /// - `name` - /// - `version` - /// - `requires-dist` - /// - `requires-python` - /// - `provides-extras` + /// - `name`: The name of the package. + /// - (Optional) `version`: The version of the package. If omitted, the metadata will be applied + /// to all versions of the package. + /// - (Optional) `requires-dist`: The dependencies of the package (e.g., `werkzeug>=0.14`). + /// - (Optional) `requires-python`: The Python version required by the package (e.g., `>=3.10`). + /// - (Optional) `provides-extras`: The extras provided by the package. #[option( default = r#"[]"#, value_type = "list[dict]", example = r#" - static-metadata = [ + metadata-override = [ { name = "flask", version = "1.0.0", requires-dist = ["werkzeug"], requires-python = ">=3.6" }, ] "# )] - pub static_metadata: Option>, + pub metadata_override: Option>, /// Settings to pass to the [PEP 517](https://peps.python.org/pep-0517/) build backend, /// specified as `KEY=VALUE` pairs. #[option( @@ -970,26 +973,29 @@ pub struct PipOptions { possible_values = true )] pub prerelease: Option, - /// Pre-defined static metadata for dependencies of the project (direct or transitive). + /// Pre-defined static metadata for dependencies of the project (direct or transitive). When + /// provided, enables the resolver to use the specified metadata instead of querying the + /// registry or building the relevant package from source. /// /// Metadata should be provided in adherence with the [Metadata 2.3](https://packaging.python.org/en/latest/specifications/core-metadata/) /// standard, though only the following fields are respected: /// - /// - `name` - /// - `version` - /// - `requires-dist` - /// - `requires-python` - /// - `provides-extras` + /// - `name`: The name of the package. + /// - (Optional) `version`: The version of the package. If omitted, the metadata will be applied + /// to all versions of the package. + /// - (Optional) `requires-dist`: The dependencies of the package (e.g., `werkzeug>=0.14`). + /// - (Optional) `requires-python`: The Python version required by the package (e.g., `>=3.10`). + /// - (Optional) `provides-extras`: The extras provided by the package. #[option( default = r#"[]"#, value_type = "list[dict]", example = r#" - static-metadata = [ + metadata-override = [ { name = "flask", version = "1.0.0", requires-dist = ["werkzeug"], requires-python = ">=3.6" }, ] "# )] - pub static_metadata: Option>, + pub metadata_override: Option>, /// Write the requirements generated by `uv pip compile` to the given `requirements.txt` file. /// /// If the file already exists, the existing versions will be preferred when resolving @@ -1331,7 +1337,7 @@ impl From for ResolverOptions { allow_insecure_host: value.allow_insecure_host, resolution: value.resolution, prerelease: value.prerelease, - static_metadata: value.static_metadata, + metadata_override: value.metadata_override, config_settings: value.config_settings, exclude_newer: value.exclude_newer, link_mode: value.link_mode, @@ -1393,7 +1399,7 @@ pub struct ToolOptions { pub allow_insecure_host: Option>, pub resolution: Option, pub prerelease: Option, - pub static_metadata: Option>, + pub metadata_override: Option>, pub config_settings: Option, pub no_build_isolation: Option, pub no_build_isolation_package: Option>, @@ -1419,7 +1425,7 @@ impl From for ToolOptions { allow_insecure_host: value.allow_insecure_host, resolution: value.resolution, prerelease: value.prerelease, - static_metadata: value.static_metadata, + metadata_override: value.metadata_override, config_settings: value.config_settings, no_build_isolation: value.no_build_isolation, no_build_isolation_package: value.no_build_isolation_package, @@ -1447,7 +1453,7 @@ impl From for ResolverInstallerOptions { allow_insecure_host: value.allow_insecure_host, resolution: value.resolution, prerelease: value.prerelease, - static_metadata: value.static_metadata, + metadata_override: value.metadata_override, config_settings: value.config_settings, no_build_isolation: value.no_build_isolation, no_build_isolation_package: value.no_build_isolation_package, diff --git a/crates/uv-types/src/traits.rs b/crates/uv-types/src/traits.rs index 3ce3696da7dd2..d0b4ee8714b31 100644 --- a/crates/uv-types/src/traits.rs +++ b/crates/uv-types/src/traits.rs @@ -4,8 +4,8 @@ use std::path::{Path, PathBuf}; use anyhow::Result; use distribution_types::{ - CachedDist, IndexCapabilities, IndexLocations, InstalledDist, Resolution, SourceDist, - StaticMetadata, + CachedDist, IndexCapabilities, IndexLocations, InstalledDist, MetadataOverrides, Resolution, + SourceDist, }; use pep508_rs::PackageName; use pypi_types::Requirement; @@ -64,7 +64,7 @@ pub trait BuildContext { fn capabilities(&self) -> &IndexCapabilities; /// Return a reference to any pre-defined static metadata. - fn static_metadata(&self) -> &StaticMetadata; + fn metadata_override(&self) -> &MetadataOverrides; /// Whether source distribution building or pre-built wheels is disabled. /// diff --git a/crates/uv-workspace/Cargo.toml b/crates/uv-workspace/Cargo.toml index 4627587d92ae5..5ddaabbf59959 100644 --- a/crates/uv-workspace/Cargo.toml +++ b/crates/uv-workspace/Cargo.toml @@ -47,7 +47,3 @@ tempfile = { workspace = true } [package.metadata.cargo-shear] ignored = ["uv-options-metadata"] - -[features] -default = [] -schemars = ["dep:schemars", "pypi-types/schemars"] diff --git a/crates/uv/src/commands/build.rs b/crates/uv/src/commands/build.rs index ddf432a25f0e9..ef9447b281fd2 100644 --- a/crates/uv/src/commands/build.rs +++ b/crates/uv/src/commands/build.rs @@ -115,7 +115,7 @@ async fn build_impl( allow_insecure_host, resolution: _, prerelease: _, - static_metadata, + metadata_override, config_setting, no_build_isolation, no_build_isolation_package, @@ -300,7 +300,7 @@ async fn build_impl( &interpreter, index_locations, &flat_index, - static_metadata, + metadata_override, &state.index, &state.git, &state.capabilities, diff --git a/crates/uv/src/commands/pip/compile.rs b/crates/uv/src/commands/pip/compile.rs index aaca88c9fa6a4..b74dba8cd4524 100644 --- a/crates/uv/src/commands/pip/compile.rs +++ b/crates/uv/src/commands/pip/compile.rs @@ -8,7 +8,7 @@ use owo_colors::OwoColorize; use tracing::debug; use distribution_types::{ - IndexCapabilities, IndexLocations, NameRequirementSpecification, StaticMetadata, + IndexCapabilities, IndexLocations, MetadataOverrides, NameRequirementSpecification, UnresolvedRequirementSpecification, Verbatim, }; use install_wheel_rs::linker::LinkMode; @@ -75,7 +75,7 @@ pub(crate) async fn pip_compile( include_index_annotation: bool, index_locations: IndexLocations, index_strategy: IndexStrategy, - static_metadata: StaticMetadata, + metadata_override: MetadataOverrides, keyring_provider: KeyringProviderType, allow_insecure_host: Vec, config_settings: ConfigSettings, @@ -336,7 +336,7 @@ pub(crate) async fn pip_compile( &interpreter, &index_locations, &flat_index, - &static_metadata, + &metadata_override, &source_index, &git, &capabilities, diff --git a/crates/uv/src/commands/pip/install.rs b/crates/uv/src/commands/pip/install.rs index c04e7b7e296f6..384bbdd7c150c 100644 --- a/crates/uv/src/commands/pip/install.rs +++ b/crates/uv/src/commands/pip/install.rs @@ -6,7 +6,7 @@ use owo_colors::OwoColorize; use tracing::{debug, enabled, Level}; use distribution_types::{ - IndexLocations, NameRequirementSpecification, Resolution, StaticMetadata, + IndexLocations, MetadataOverrides, NameRequirementSpecification, Resolution, UnresolvedRequirementSpecification, }; use install_wheel_rs::linker::LinkMode; @@ -55,7 +55,7 @@ pub(crate) async fn pip_install( upgrade: Upgrade, index_locations: IndexLocations, index_strategy: IndexStrategy, - static_metadata: StaticMetadata, + metadata_override: MetadataOverrides, keyring_provider: KeyringProviderType, allow_insecure_host: Vec, reinstall: Reinstall, @@ -339,7 +339,7 @@ pub(crate) async fn pip_install( interpreter, &index_locations, &flat_index, - &static_metadata, + &metadata_override, &state.index, &state.git, &state.capabilities, diff --git a/crates/uv/src/commands/pip/sync.rs b/crates/uv/src/commands/pip/sync.rs index 5d4debcc327b5..4a1ae5ec6c232 100644 --- a/crates/uv/src/commands/pip/sync.rs +++ b/crates/uv/src/commands/pip/sync.rs @@ -5,7 +5,7 @@ use anyhow::Result; use owo_colors::OwoColorize; use tracing::debug; -use distribution_types::{IndexLocations, Resolution, StaticMetadata}; +use distribution_types::{IndexLocations, MetadataOverrides, Resolution}; use install_wheel_rs::linker::LinkMode; use pep508_rs::PackageName; use uv_auth::store_credentials_from_url; @@ -47,7 +47,7 @@ pub(crate) async fn pip_sync( hash_checking: Option, index_locations: IndexLocations, index_strategy: IndexStrategy, - static_metadata: StaticMetadata, + metadata_override: MetadataOverrides, keyring_provider: KeyringProviderType, allow_insecure_host: Vec, allow_empty_requirements: bool, @@ -288,7 +288,7 @@ pub(crate) async fn pip_sync( interpreter, &index_locations, &flat_index, - &static_metadata, + &metadata_override, &state.index, &state.git, &state.capabilities, diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs index 482d0fa78690c..0f49807d966d8 100644 --- a/crates/uv/src/commands/project/add.rs +++ b/crates/uv/src/commands/project/add.rs @@ -300,7 +300,7 @@ pub(crate) async fn add( target.interpreter(), &settings.index_locations, &flat_index, - &settings.static_metadata, + &settings.metadata_override, &state.index, &state.git, &state.capabilities, diff --git a/crates/uv/src/commands/project/lock.rs b/crates/uv/src/commands/project/lock.rs index 815fd5fe88095..fb9911c9aa361 100644 --- a/crates/uv/src/commands/project/lock.rs +++ b/crates/uv/src/commands/project/lock.rs @@ -9,7 +9,7 @@ use rustc_hash::{FxBuildHasher, FxHashMap}; use tracing::debug; use distribution_types::{ - IndexLocations, NameRequirementSpecification, StaticMetadata, + IndexLocations, MetadataOverrides, NameRequirementSpecification, UnresolvedRequirementSpecification, }; use pep440_rs::Version; @@ -242,7 +242,7 @@ async fn do_lock( allow_insecure_host, resolution, prerelease, - static_metadata, + metadata_override, config_setting, no_build_isolation, no_build_isolation_package, @@ -411,7 +411,7 @@ async fn do_lock( interpreter, index_locations, &flat_index, - static_metadata, + metadata_override, &state.index, &state.git, &state.capabilities, @@ -439,7 +439,7 @@ async fn do_lock( &constraints, &overrides, environments, - static_metadata, + metadata_override, interpreter, &requires_python, index_locations, @@ -561,7 +561,7 @@ async fn do_lock( requirements, constraints, overrides, - static_metadata.values().cloned(), + metadata_override.values().cloned(), ) .relative_to(workspace)?; @@ -601,7 +601,7 @@ impl ValidatedLock { constraints: &[Requirement], overrides: &[Requirement], environments: Option<&SupportedEnvironments>, - static_metadata: &StaticMetadata, + metadata_override: &MetadataOverrides, interpreter: &Interpreter, requires_python: &RequiresPython, index_locations: &IndexLocations, @@ -721,7 +721,7 @@ impl ValidatedLock { requirements, constraints, overrides, - static_metadata, + metadata_override, indexes, build_options, interpreter.tags()?, diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs index e3d06708fb5e7..8ff67ce08446a 100644 --- a/crates/uv/src/commands/project/mod.rs +++ b/crates/uv/src/commands/project/mod.rs @@ -573,7 +573,7 @@ pub(crate) async fn resolve_names( allow_insecure_host, resolution: _, prerelease: _, - static_metadata, + metadata_override, config_setting, no_build_isolation, no_build_isolation_package, @@ -630,7 +630,7 @@ pub(crate) async fn resolve_names( interpreter, index_locations, &flat_index, - static_metadata, + metadata_override, &state.index, &state.git, &state.capabilities, @@ -684,7 +684,7 @@ pub(crate) async fn resolve_environment<'a>( allow_insecure_host, resolution, prerelease, - static_metadata, + metadata_override, config_setting, no_build_isolation, no_build_isolation_package, @@ -775,7 +775,7 @@ pub(crate) async fn resolve_environment<'a>( interpreter, index_locations, &flat_index, - static_metadata, + metadata_override, &state.index, &state.git, &state.capabilities, @@ -839,7 +839,7 @@ pub(crate) async fn sync_environment( index_strategy, keyring_provider, allow_insecure_host, - static_metadata, + metadata_override, config_setting, no_build_isolation, no_build_isolation_package, @@ -906,7 +906,7 @@ pub(crate) async fn sync_environment( interpreter, index_locations, &flat_index, - static_metadata, + metadata_override, &state.index, &state.git, &state.capabilities, @@ -993,7 +993,7 @@ pub(crate) async fn update_environment( allow_insecure_host, resolution, prerelease, - static_metadata, + metadata_override, config_setting, no_build_isolation, no_build_isolation_package, @@ -1109,7 +1109,7 @@ pub(crate) async fn update_environment( interpreter, index_locations, &flat_index, - static_metadata, + metadata_override, &state.index, &state.git, &state.capabilities, diff --git a/crates/uv/src/commands/project/sync.rs b/crates/uv/src/commands/project/sync.rs index cf8136e107385..f1c29c459ec66 100644 --- a/crates/uv/src/commands/project/sync.rs +++ b/crates/uv/src/commands/project/sync.rs @@ -177,7 +177,7 @@ pub(super) async fn do_sync( index_strategy, keyring_provider, allow_insecure_host, - static_metadata, + metadata_override, config_setting, no_build_isolation, no_build_isolation_package, @@ -299,7 +299,7 @@ pub(super) async fn do_sync( venv.interpreter(), index_locations, &flat_index, - static_metadata, + metadata_override, &state.index, &state.git, &state.capabilities, diff --git a/crates/uv/src/commands/venv.rs b/crates/uv/src/commands/venv.rs index 8dbe7fb0db356..46c1cff68ae10 100644 --- a/crates/uv/src/commands/venv.rs +++ b/crates/uv/src/commands/venv.rs @@ -9,7 +9,7 @@ use miette::{Diagnostic, IntoDiagnostic}; use owo_colors::OwoColorize; use thiserror::Error; -use distribution_types::{IndexLocations, StaticMetadata}; +use distribution_types::{IndexLocations, MetadataOverrides}; use install_wheel_rs::linker::LinkMode; use pypi_types::Requirement; use uv_auth::store_credentials_from_url; @@ -48,7 +48,7 @@ pub(crate) async fn venv( link_mode: LinkMode, index_locations: &IndexLocations, index_strategy: IndexStrategy, - static_metadata: StaticMetadata, + metadata_override: MetadataOverrides, keyring_provider: KeyringProviderType, allow_insecure_host: Vec, prompt: uv_virtualenv::Prompt, @@ -71,7 +71,7 @@ pub(crate) async fn venv( link_mode, index_locations, index_strategy, - static_metadata, + metadata_override, keyring_provider, allow_insecure_host, prompt, @@ -127,7 +127,7 @@ async fn venv_impl( link_mode: LinkMode, index_locations: &IndexLocations, index_strategy: IndexStrategy, - static_metadata: StaticMetadata, + metadata_override: MetadataOverrides, keyring_provider: KeyringProviderType, allow_insecure_host: Vec, prompt: uv_virtualenv::Prompt, @@ -320,7 +320,7 @@ async fn venv_impl( interpreter, index_locations, &flat_index, - &static_metadata, + &metadata_override, &state.index, &state.git, &state.capabilities, diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index 886ccf1144208..55152eef0c4a3 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -323,7 +323,7 @@ async fn run(cli: Cli) -> Result { args.settings.emit_index_annotation, args.settings.index_locations, args.settings.index_strategy, - args.settings.static_metadata, + args.settings.metadata_override, args.settings.keyring_provider, args.settings.allow_insecure_host, args.settings.config_setting, @@ -391,7 +391,7 @@ async fn run(cli: Cli) -> Result { args.settings.hash_checking, args.settings.index_locations, args.settings.index_strategy, - args.settings.static_metadata, + args.settings.metadata_override, args.settings.keyring_provider, args.settings.allow_insecure_host, args.settings.allow_empty_requirements, @@ -475,7 +475,7 @@ async fn run(cli: Cli) -> Result { args.settings.upgrade, args.settings.index_locations, args.settings.index_strategy, - args.settings.static_metadata, + args.settings.metadata_override, args.settings.keyring_provider, args.settings.allow_insecure_host, args.settings.reinstall, @@ -735,7 +735,7 @@ async fn run(cli: Cli) -> Result { args.settings.link_mode, &args.settings.index_locations, args.settings.index_strategy, - args.settings.static_metadata, + args.settings.metadata_override, args.settings.keyring_provider, args.settings.allow_insecure_host, uv_virtualenv::Prompt::from_args(prompt), @@ -882,7 +882,7 @@ async fn run(cli: Cli) -> Result { ) .collect::>(); - commands::tool_install( + Box::pin(commands::tool_install( args.package, args.editable, args.from, @@ -898,7 +898,7 @@ async fn run(cli: Cli) -> Result { globals.native_tls, cache, printer, - ) + )) .await } Commands::Tool(ToolNamespace { diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index 2052605850f38..b2adc55396f40 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use std::process; use std::str::FromStr; -use distribution_types::{IndexLocations, StaticMetadata}; +use distribution_types::{IndexLocations, MetadataOverrides}; use install_wheel_rs::linker::LinkMode; use pep508_rs::{ExtraName, RequirementOrigin}; use pypi_types::{Requirement, SupportedEnvironments}; @@ -1781,7 +1781,7 @@ pub(crate) struct InstallerSettingsRef<'a> { pub(crate) index_strategy: IndexStrategy, pub(crate) keyring_provider: KeyringProviderType, pub(crate) allow_insecure_host: &'a [TrustedHost], - pub(crate) static_metadata: &'a StaticMetadata, + pub(crate) metadata_override: &'a MetadataOverrides, pub(crate) config_setting: &'a ConfigSettings, pub(crate) no_build_isolation: bool, pub(crate) no_build_isolation_package: &'a [PackageName], @@ -1806,7 +1806,7 @@ pub(crate) struct ResolverSettings { pub(crate) allow_insecure_host: Vec, pub(crate) resolution: ResolutionMode, pub(crate) prerelease: PrereleaseMode, - pub(crate) static_metadata: StaticMetadata, + pub(crate) metadata_override: MetadataOverrides, pub(crate) config_setting: ConfigSettings, pub(crate) no_build_isolation: bool, pub(crate) no_build_isolation_package: Vec, @@ -1825,7 +1825,7 @@ pub(crate) struct ResolverSettingsRef<'a> { pub(crate) allow_insecure_host: &'a [TrustedHost], pub(crate) resolution: ResolutionMode, pub(crate) prerelease: PrereleaseMode, - pub(crate) static_metadata: &'a StaticMetadata, + pub(crate) metadata_override: &'a MetadataOverrides, pub(crate) config_setting: &'a ConfigSettings, pub(crate) no_build_isolation: bool, pub(crate) no_build_isolation_package: &'a [PackageName], @@ -1857,7 +1857,7 @@ impl ResolverSettings { allow_insecure_host: &self.allow_insecure_host, resolution: self.resolution, prerelease: self.prerelease, - static_metadata: &self.static_metadata, + metadata_override: &self.metadata_override, config_setting: &self.config_setting, no_build_isolation: self.no_build_isolation, no_build_isolation_package: &self.no_build_isolation_package, @@ -1881,8 +1881,8 @@ impl From for ResolverSettings { ), resolution: value.resolution.unwrap_or_default(), prerelease: value.prerelease.unwrap_or_default(), - static_metadata: StaticMetadata::from_entries( - value.static_metadata.into_iter().flatten(), + metadata_override: MetadataOverrides::from_entries( + value.metadata_override.into_iter().flatten(), ), index_strategy: value.index_strategy.unwrap_or_default(), keyring_provider: value.keyring_provider.unwrap_or_default(), @@ -1918,7 +1918,7 @@ pub(crate) struct ResolverInstallerSettingsRef<'a> { pub(crate) allow_insecure_host: &'a [TrustedHost], pub(crate) resolution: ResolutionMode, pub(crate) prerelease: PrereleaseMode, - pub(crate) static_metadata: &'a StaticMetadata, + pub(crate) metadata_override: &'a MetadataOverrides, pub(crate) config_setting: &'a ConfigSettings, pub(crate) no_build_isolation: bool, pub(crate) no_build_isolation_package: &'a [PackageName], @@ -1945,7 +1945,7 @@ pub(crate) struct ResolverInstallerSettings { pub(crate) allow_insecure_host: Vec, pub(crate) resolution: ResolutionMode, pub(crate) prerelease: PrereleaseMode, - pub(crate) static_metadata: StaticMetadata, + pub(crate) metadata_override: MetadataOverrides, pub(crate) config_setting: ConfigSettings, pub(crate) no_build_isolation: bool, pub(crate) no_build_isolation_package: Vec, @@ -1982,7 +1982,7 @@ impl ResolverInstallerSettings { allow_insecure_host: &self.allow_insecure_host, resolution: self.resolution, prerelease: self.prerelease, - static_metadata: &self.static_metadata, + metadata_override: &self.metadata_override, config_setting: &self.config_setting, no_build_isolation: self.no_build_isolation, no_build_isolation_package: &self.no_build_isolation_package, @@ -2008,8 +2008,8 @@ impl From for ResolverInstallerSettings { ), resolution: value.resolution.unwrap_or_default(), prerelease: value.prerelease.unwrap_or_default(), - static_metadata: StaticMetadata::from_entries( - value.static_metadata.into_iter().flatten(), + metadata_override: MetadataOverrides::from_entries( + value.metadata_override.into_iter().flatten(), ), index_strategy: value.index_strategy.unwrap_or_default(), keyring_provider: value.keyring_provider.unwrap_or_default(), @@ -2067,7 +2067,7 @@ pub(crate) struct PipSettings { pub(crate) dependency_mode: DependencyMode, pub(crate) resolution: ResolutionMode, pub(crate) prerelease: PrereleaseMode, - pub(crate) static_metadata: StaticMetadata, + pub(crate) metadata_override: MetadataOverrides, pub(crate) output_file: Option, pub(crate) no_strip_extras: bool, pub(crate) no_strip_markers: bool, @@ -2127,7 +2127,7 @@ impl PipSettings { allow_empty_requirements, resolution, prerelease, - static_metadata, + metadata_override, output_file, no_strip_extras, no_strip_markers, @@ -2168,7 +2168,7 @@ impl PipSettings { allow_insecure_host: top_level_allow_insecure_host, resolution: top_level_resolution, prerelease: top_level_prerelease, - static_metadata: top_level_static_metadata, + metadata_override: top_level_metadata_override, config_settings: top_level_config_settings, no_build_isolation: top_level_no_build_isolation, no_build_isolation_package: top_level_no_build_isolation_package, @@ -2199,7 +2199,7 @@ impl PipSettings { let allow_insecure_host = allow_insecure_host.combine(top_level_allow_insecure_host); let resolution = resolution.combine(top_level_resolution); let prerelease = prerelease.combine(top_level_prerelease); - let static_metadata = static_metadata.combine(top_level_static_metadata); + let metadata_override = metadata_override.combine(top_level_metadata_override); let config_settings = config_settings.combine(top_level_config_settings); let no_build_isolation = no_build_isolation.combine(top_level_no_build_isolation); let no_build_isolation_package = @@ -2233,9 +2233,9 @@ impl PipSettings { }, resolution: args.resolution.combine(resolution).unwrap_or_default(), prerelease: args.prerelease.combine(prerelease).unwrap_or_default(), - static_metadata: StaticMetadata::from_entries( - args.static_metadata - .combine(static_metadata) + metadata_override: MetadataOverrides::from_entries( + args.metadata_override + .combine(metadata_override) .unwrap_or_default(), ), output_file: args.output_file.combine(output_file), @@ -2382,7 +2382,7 @@ impl<'a> From> for ResolverSettingsRef<'a> { allow_insecure_host: settings.allow_insecure_host, resolution: settings.resolution, prerelease: settings.prerelease, - static_metadata: settings.static_metadata, + metadata_override: settings.metadata_override, config_setting: settings.config_setting, no_build_isolation: settings.no_build_isolation, no_build_isolation_package: settings.no_build_isolation_package, @@ -2402,7 +2402,7 @@ impl<'a> From> for InstallerSettingsRef<'a> { index_strategy: settings.index_strategy, keyring_provider: settings.keyring_provider, allow_insecure_host: settings.allow_insecure_host, - static_metadata: settings.static_metadata, + metadata_override: settings.metadata_override, config_setting: settings.config_setting, no_build_isolation: settings.no_build_isolation, no_build_isolation_package: settings.no_build_isolation_package, diff --git a/crates/uv/tests/lock.rs b/crates/uv/tests/lock.rs index 0e014aa011fbb..fdff33a75058a 100644 --- a/crates/uv/tests/lock.rs +++ b/crates/uv/tests/lock.rs @@ -12531,7 +12531,7 @@ fn lock_python_upper_bound() -> Result<()> { } #[test] -fn lock_static_metadata() -> Result<()> { +fn lock_metadata_override() -> Result<()> { let context = TestContext::new("3.12"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); @@ -12547,7 +12547,7 @@ fn lock_static_metadata() -> Result<()> { requires = ["setuptools>=42"] build-backend = "setuptools.build_meta" - [[tool.uv.static-metadata]] + [[tool.uv.metadata-override]] name = "anyio" version = "3.7.0" requires-dist = ["iniconfig"] @@ -12578,7 +12578,7 @@ fn lock_static_metadata() -> Result<()> { [manifest] - [[manifest.static-metadata]] + [[manifest.metadata-override]] name = "anyio" version = "3.7.0" requires-dist = ["iniconfig"] @@ -12666,7 +12666,7 @@ fn lock_static_metadata() -> Result<()> { requires = ["setuptools>=42"] build-backend = "setuptools.build_meta" - [[tool.uv.static-metadata]] + [[tool.uv.metadata-override]] name = "anyio" version = "3.7.0" requires-dist = ["typing-extensions"] @@ -12713,6 +12713,38 @@ fn lock_static_metadata() -> Result<()> { Removed typing-extensions v4.10.0 "###); + // Use a blanket match. + pyproject_toml.write_str( + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = ["anyio==3.7.0"] + + [build-system] + requires = ["setuptools>=42"] + build-backend = "setuptools.build_meta" + + [[tool.uv.metadata-override]] + name = "anyio" + requires-dist = ["iniconfig"] + "#, + )?; + + // The lockfile should update. + uv_snapshot!(context.filters(), context.lock(), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 3 packages in [TIME] + Removed idna v3.6 + Added iniconfig v2.0.0 + Removed sniffio v1.3.1 + "###); + Ok(()) } diff --git a/crates/uv/tests/show_settings.rs b/crates/uv/tests/show_settings.rs index 099e9052998af..80c17b068af6a 100644 --- a/crates/uv/tests/show_settings.rs +++ b/crates/uv/tests/show_settings.rs @@ -140,6 +140,9 @@ fn resolve_uv_toml() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: LowestDirect, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -279,6 +282,9 @@ fn resolve_uv_toml() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: Highest, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -419,6 +425,9 @@ fn resolve_uv_toml() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: Highest, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -591,6 +600,9 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: LowestDirect, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -709,6 +721,9 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: Highest, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -859,6 +874,9 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: LowestDirect, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -1046,6 +1064,9 @@ fn resolve_index_url() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: Highest, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -1232,6 +1253,9 @@ fn resolve_index_url() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: Highest, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -1396,6 +1420,9 @@ fn resolve_find_links() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: Highest, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -1536,6 +1563,9 @@ fn resolve_top_level() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: LowestDirect, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -1714,6 +1744,9 @@ fn resolve_top_level() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: Highest, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -1875,6 +1908,9 @@ fn resolve_top_level() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: LowestDirect, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -2015,6 +2051,9 @@ fn resolve_user_configuration() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: LowestDirect, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -2138,6 +2177,9 @@ fn resolve_user_configuration() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: LowestDirect, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -2261,6 +2303,9 @@ fn resolve_user_configuration() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: Highest, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -2386,6 +2431,9 @@ fn resolve_user_configuration() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: LowestDirect, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -2509,6 +2557,7 @@ fn resolve_tool() -> anyhow::Result<()> { LowestDirect, ), prerelease: None, + metadata_override: None, config_settings: None, no_build_isolation: None, no_build_isolation_package: None, @@ -2543,6 +2592,9 @@ fn resolve_tool() -> anyhow::Result<()> { allow_insecure_host: [], resolution: LowestDirect, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), config_setting: ConfigSettings( {}, ), @@ -2683,6 +2735,9 @@ fn resolve_poetry_toml() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: LowestDirect, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -2857,6 +2912,9 @@ fn resolve_both() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: LowestDirect, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -3022,6 +3080,9 @@ fn resolve_config_file() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: LowestDirect, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -3240,6 +3301,9 @@ fn resolve_skip_empty() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: LowestDirect, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -3366,6 +3430,9 @@ fn resolve_skip_empty() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: Highest, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, @@ -3511,6 +3578,9 @@ fn allow_insecure_host() -> anyhow::Result<()> { dependency_mode: Transitive, resolution: Highest, prerelease: IfNecessaryOrExplicit, + metadata_override: MetadataOverrides( + {}, + ), output_file: None, no_strip_extras: false, no_strip_markers: false, diff --git a/docs/concepts/projects.md b/docs/concepts/projects.md index cb98cf2d0bcf2..db94aa95fac77 100644 --- a/docs/concepts/projects.md +++ b/docs/concepts/projects.md @@ -715,8 +715,8 @@ $ uv sync --extra build $ uv sync --extra build --extra compile ``` -Some packages, like `cchardet`, only require build dependencies for the _installation_ phase of -`uv sync`. Others, like `flash-attn`, require their build dependencies to be present even just to +Some packages, like `cchardet` above, only require build dependencies for the _installation_ phase +of `uv sync`. Others, like `flash-attn`, require their build dependencies to be present even just to resolve the project's lockfile during the _resolution_ phase. In such cases, the build dependencies must be installed prior to running any `uv lock` or `uv sync` @@ -735,10 +735,54 @@ dependencies = ["flash-attn"] no-build-isolation-package = ["flash-attn"] ``` -You could run the following sequence of commands: +You could run the following sequence of commands to sync `flash-attn`: ```console $ uv venv $ uv pip install torch $ uv sync ``` + +Alternatively, you can provide the `flash-attn` metadata upfront via the +[`metadata-override`](../reference/settings.md#metadata-override) setting, thereby forgoing the need +to build the package during the dependency resolution phase. For example, to provide the +`flash-attn` metadata upfront, include the following in your `pyproject.toml`: + +```toml title="pyproject.toml" +[[tool.uv.metadata-override]] +name = "flash-attn" +version = "2.6.3" +requires-dist = ["torch", "einops"] +``` + +Once included, you can again use the two-step `uv sync` process to install the build dependencies. +Given the following `pyproject.toml`: + +```toml title="pyproject.toml" +[project] +name = "project" +version = "0.1.0" +description = "..." +readme = "README.md" +requires-python = ">=3.12" +dependencies = [] + +[project.optional-dependencies] +build = ["torch", "setuptools", "packaging"] +compile = ["flash-attn"] + +[tool.uv] +no-build-isolation-package = ["flash-attn"] + +[[tool.uv.metadata-override]] +name = "flash-attn" +version = "2.6.3" +requires-dist = ["torch", "einops"] +``` + +You could run the following sequence of commands to sync `flash-attn`: + +```console +$ uv sync --extra build +$ uv sync --extra build --extra compile +``` diff --git a/docs/concepts/resolution.md b/docs/concepts/resolution.md index 9681615fa6bdb..3d4f461d69991 100644 --- a/docs/concepts/resolution.md +++ b/docs/concepts/resolution.md @@ -227,28 +227,35 @@ For more details, see ## Dependency constraints -uv supports constraints files (`--constraint constraints.txt`), like pip, which narrow the set of -acceptable versions for the given packages. Constraint files are like a regular requirements files, -but they do not add packages to the requirements — they only take affect if the package is requested -in a direct or transitive dependency. Constraints are often useful for reducing the range of -available versions for a transitive dependency without adding a direct requirement on the package. +Like pip, uv supports constraint files (`--constraint constraints.txt`) which narrow the set of +acceptable versions for the given packages. Constraint files are similar to requirements files, but +being listed as a constraint alone will not cause a package to be included to the resolution. +Instead, constraints only take effect if a requested package is already pulled in as a direct or +transitive dependency. Constraints are useful for reducing the range of available versions for a +transitive dependency. They can also be used to keep a resolution in sync with some other set of +resolved versions, regardless of which packages are overlapping between the two. ## Dependency overrides -Overrides allow bypassing failing or undesirable resolutions by overriding the declared dependencies -of a package. Overrides are a useful last resort for cases in which the you know that a dependency -is compatible with a newer version of a package than it declares, but the it has not yet been -updated to declare that compatibility. +Dependency overrides allow bypassing failing or undesirable resolutions by overriding a package's +declared dependencies. Overrides are a useful last resort for cases in which you _know_ that a +dependency is compatible with a certain version of a package, despite the metadata indicating +otherwise. -For example, if a transitive dependency declares the requirement `pydantic>=1.0,<2.0`, but _works_ -with `pydantic>=2.0`, the user can override the declared dependency with `pydantic>=1.0,<3` to allow -the resolver to installer a newer version of `pydantic`. +For example, if a transitive dependency declares the requirement `pydantic>=1.0,<2.0`, but _does_ +work with `pydantic>=2.0`, the user can override the declared dependency by including +`pydantic>=1.0,<3` in the overrides, thereby allowing the resolver to choose a newer version of +`pydantic`. -While constraints and dependencies are purely additive, and thus cannot expand the set of acceptable -versions for a package, overrides can expand the set of acceptable versions for a package, providing -an escape hatch for erroneous upper version bounds. As with constraints, overrides do not add a -dependency on the package and only take affect if the package is requested in a direct or transitive -dependency. +Concretely, if `pydantic>=1.0,<3` is included as an override, uv will ignore all declared +requirements on `pydantic`, replacing them with the override. In the above example, the +`pydantic>=1.0,<2.0` requirement would be ignored completely, and would instead be replaced with +`pydantic>=1.0,<3`. + +While constraints can only _reduce_ the set of acceptable versions for a package, overrides can +_expand_ the set of acceptable versions, providing an escape hatch for erroneous upper version +bounds. As with constraints, overrides do not add a dependency on the package and only take effect +if the package is requested in a direct or transitive dependency. In a `pyproject.toml`, use `tool.uv.override-dependencies` to define a list of overrides. In the pip-compatible interface, the `--override` option can be used to pass files with the same format as @@ -258,6 +265,46 @@ If multiple overrides are provided for the same package, they must be differenti [markers](#platform-markers). If a package has a dependency with a marker, it is replaced unconditionally when using overrides — it does not matter if the marker evaluates to true or false. +## Metadata overrides + +Metadata overrides allow overriding the metadata of a specific package. For example, to provide +metadata for `chumpy` upfront, include a metadata override in the `pyproject.toml`: + +```toml +[[tool.uv.metadata-override]] +name = "chumpy" +version = "0.70" +requires-dist = ["numpy>=1.8.1", "scipy>=0.13.0", "six>=1.11.0"] +``` + +Metadata overrides are intended for cases in which a package does _not_ declare static metadata +upfront. Typically, uv would be required to build such packages to determine their metadata, e.g., +by invoking `setup.py`. This imposes the requirement that the package can be built on all platforms, +which may not be true. + +For example, you may have a package that should only be built and installed on Linux, but doesn't +build successfully on macOS or Windows. By declaring the metadata upfront, uv can skip the build +step and use the provided metadata instead. + +Metadata overrides are also useful for packages that require disabling build isolation. In such +cases, it may be easier to declare the package metadata upfront, rather than creating a custom build +environment prior to resolving the package. + +For example, you can declare the metadata for `flash-attn`, allowing uv to resolve without building +the package from source (which itself requires installing `torch`): + +```toml +[[tool.uv.metadata-override]] +name = "flash-attn" +version = "2.6.3" +requires-dist = ["torch", "einops"] +``` + +Like dependency overrides, metadata overrides can also be used for cases in which a package's +metadata is incorrect or incomplete, or when a package is not available in the package index. While +dependency overrides allow overriding the allowed versions of a package globally, metadata overrides +allow overriding the declared metadata of a specific package. + ## Lower bounds By default, `uv add` adds lower bounds to dependencies and, when using uv to manage projects, uv diff --git a/docs/reference/settings.md b/docs/reference/settings.md index 3df49d1038f37..db5b891b4d6bf 100644 --- a/docs/reference/settings.md +++ b/docs/reference/settings.md @@ -672,6 +672,47 @@ Windows. --- +### [`metadata-override`](#metadata-override) {: #metadata-override } + +Pre-defined static metadata for dependencies of the project (direct or transitive). When +provided, enables the resolver to use the specified metadata instead of querying the +registry or building the relevant package from source. + +Metadata should be provided in adherence with the [Metadata 2.3](https://packaging.python.org/en/latest/specifications/core-metadata/) +standard, though only the following fields are respected: + +- `name`: The name of the package. +- (Optional) `version`: The version of the package. If omitted, the metadata will be applied + to all versions of the package. +- (Optional) `requires-dist`: The dependencies of the package (e.g., `werkzeug>=0.14`). +- (Optional) `requires-python`: The Python version required by the package (e.g., `>=3.10`). +- (Optional) `provides-extras`: The extras provided by the package. + +**Default value**: `[]` + +**Type**: `list[dict]` + +**Example usage**: + +=== "pyproject.toml" + + ```toml + [tool.uv] + metadata-override = [ + { name = "flask", version = "1.0.0", requires-dist = ["werkzeug"], requires-python = ">=3.6" }, + ] + ``` +=== "uv.toml" + + ```toml + + metadata-override = [ + { name = "flask", version = "1.0.0", requires-dist = ["werkzeug"], requires-python = ">=3.6" }, + ] + ``` + +--- + ### [`native-tls`](#native-tls) {: #native-tls } Whether to load TLS certificates from the platform's native certificate store. @@ -1175,44 +1216,6 @@ By default, uv will use the latest compatible version of each package (`highest` --- -### [`static-metadata`](#static-metadata) {: #static-metadata } - -Pre-defined static metadata for dependencies of the project (direct or transitive). - -Metadata should be provided in adherence with the [Metadata 2.3](https://packaging.python.org/en/latest/specifications/core-metadata/) -standard, though only the following fields are respected: - -- `name` -- `version` -- `requires-dist` -- `requires-python` -- `provides-extras` - -**Default value**: `[]` - -**Type**: `list[dict]` - -**Example usage**: - -=== "pyproject.toml" - - ```toml - [tool.uv] - static-metadata = [ - { name = "flask", version = "1.0.0", requires-dist = ["werkzeug"], requires-python = ">=3.6" }, - ] - ``` -=== "uv.toml" - - ```toml - - static-metadata = [ - { name = "flask", version = "1.0.0", requires-dist = ["werkzeug"], requires-python = ">=3.6" }, - ] - ``` - ---- - ### [`upgrade`](#upgrade) {: #upgrade } Allow package upgrades, ignoring pinned versions in any existing output file. @@ -1930,6 +1933,48 @@ Windows. --- +#### [`metadata-override`](#pip_metadata-override) {: #pip_metadata-override } + + +Pre-defined static metadata for dependencies of the project (direct or transitive). When +provided, enables the resolver to use the specified metadata instead of querying the +registry or building the relevant package from source. + +Metadata should be provided in adherence with the [Metadata 2.3](https://packaging.python.org/en/latest/specifications/core-metadata/) +standard, though only the following fields are respected: + +- `name`: The name of the package. +- (Optional) `version`: The version of the package. If omitted, the metadata will be applied + to all versions of the package. +- (Optional) `requires-dist`: The dependencies of the package (e.g., `werkzeug>=0.14`). +- (Optional) `requires-python`: The Python version required by the package (e.g., `>=3.10`). +- (Optional) `provides-extras`: The extras provided by the package. + +**Default value**: `[]` + +**Type**: `list[dict]` + +**Example usage**: + +=== "pyproject.toml" + + ```toml + [tool.uv.pip] + metadata-override = [ + { name = "flask", version = "1.0.0", requires-dist = ["werkzeug"], requires-python = ">=3.6" }, + ] + ``` +=== "uv.toml" + + ```toml + [pip] + metadata-override = [ + { name = "flask", version = "1.0.0", requires-dist = ["werkzeug"], requires-python = ">=3.6" }, + ] + ``` + +--- + #### [`no-annotate`](#pip_no-annotate) {: #pip_no-annotate } @@ -2623,45 +2668,6 @@ By default, uv will use the latest compatible version of each package (`highest` --- -#### [`static-metadata`](#pip_static-metadata) {: #pip_static-metadata } - - -Pre-defined static metadata for dependencies of the project (direct or transitive). - -Metadata should be provided in adherence with the [Metadata 2.3](https://packaging.python.org/en/latest/specifications/core-metadata/) -standard, though only the following fields are respected: - -- `name` -- `version` -- `requires-dist` -- `requires-python` -- `provides-extras` - -**Default value**: `[]` - -**Type**: `list[dict]` - -**Example usage**: - -=== "pyproject.toml" - - ```toml - [tool.uv.pip] - static-metadata = [ - { name = "flask", version = "1.0.0", requires-dist = ["werkzeug"], requires-python = ">=3.6" }, - ] - ``` -=== "uv.toml" - - ```toml - [pip] - static-metadata = [ - { name = "flask", version = "1.0.0", requires-dist = ["werkzeug"], requires-python = ">=3.6" }, - ] - ``` - ---- - #### [`strict`](#pip_strict) {: #pip_strict } diff --git a/uv.schema.json b/uv.schema.json index acce66eb70d52..369e245e9eeea 100644 --- a/uv.schema.json +++ b/uv.schema.json @@ -189,6 +189,16 @@ "null" ] }, + "metadata-override": { + "description": "Pre-defined static metadata for dependencies of the project (direct or transitive). When provided, enables the resolver to use the specified metadata instead of querying the registry or building the relevant package from source.\n\nMetadata should be provided in adherence with the [Metadata 2.3](https://packaging.python.org/en/latest/specifications/core-metadata/) standard, though only the following fields are respected:\n\n- `name`: The name of the package. - (Optional) `version`: The version of the package. If omitted, the metadata will be applied to all versions of the package. - (Optional) `requires-dist`: The dependencies of the package (e.g., `werkzeug>=0.14`). - (Optional) `requires-python`: The Python version required by the package (e.g., `>=3.10`). - (Optional) `provides-extras`: The extras provided by the package.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/MetadataOverride" + } + }, "native-tls": { "description": "Whether to load TLS certificates from the platform's native certificate store.\n\nBy default, uv loads certificates from the bundled `webpki-roots` crate. The `webpki-roots` are a reliable set of trust roots from Mozilla, and including them in uv improves portability and performance (especially on macOS).\n\nHowever, in some cases, you may want to use the platform's native certificate store, especially if you're relying on a corporate trust root (e.g., for a mandatory proxy) that's included in your system's certificate store.", "type": [ @@ -381,16 +391,6 @@ } ] }, - "static-metadata": { - "description": "Pre-defined static metadata for dependencies of the project (direct or transitive).\n\nMetadata should be provided in adherence with the [Metadata 2.3](https://packaging.python.org/en/latest/specifications/core-metadata/) standard, though only the following fields are respected:\n\n- `name` - `version` - `requires-dist` - `requires-python` - `provides-extras`", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Metadata23" - } - }, "upgrade": { "description": "Allow package upgrades, ignoring pinned versions in any existing output file.", "type": [ @@ -607,8 +607,8 @@ } ] }, - "Metadata23": { - "description": "Python Package Metadata 2.3 as specified in .\n\nThis is a subset of the full metadata specification, and only includes the fields that are relevant to dependency resolution.\n\nAt present, we support up to version 2.3 of the metadata specification.", + "MetadataOverride": { + "description": "A subset of the Python Package Metadata 2.3 standard as specified in .", "type": "object", "required": [ "name", @@ -852,6 +852,16 @@ } ] }, + "metadata-override": { + "description": "Pre-defined static metadata for dependencies of the project (direct or transitive). When provided, enables the resolver to use the specified metadata instead of querying the registry or building the relevant package from source.\n\nMetadata should be provided in adherence with the [Metadata 2.3](https://packaging.python.org/en/latest/specifications/core-metadata/) standard, though only the following fields are respected:\n\n- `name`: The name of the package. - (Optional) `version`: The version of the package. If omitted, the metadata will be applied to all versions of the package. - (Optional) `requires-dist`: The dependencies of the package (e.g., `werkzeug>=0.14`). - (Optional) `requires-python`: The Python version required by the package (e.g., `>=3.10`). - (Optional) `provides-extras`: The extras provided by the package.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/MetadataOverride" + } + }, "no-annotate": { "description": "Exclude comment annotations indicating the source of each package from the output file generated by `uv pip compile`.", "type": [ @@ -1044,16 +1054,6 @@ } ] }, - "static-metadata": { - "description": "Pre-defined static metadata for dependencies of the project (direct or transitive).\n\nMetadata should be provided in adherence with the [Metadata 2.3](https://packaging.python.org/en/latest/specifications/core-metadata/) standard, though only the following fields are respected:\n\n- `name` - `version` - `requires-dist` - `requires-python` - `provides-extras`", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Metadata23" - } - }, "strict": { "description": "Validate the Python environment, to detect packages with missing dependencies and other issues.", "type": [