) -> Self {
Self {
diff --git a/crates/uv-resolver/src/universal_marker.rs b/crates/uv-resolver/src/universal_marker.rs
index 093253843..dcfce50d8 100644
--- a/crates/uv-resolver/src/universal_marker.rs
+++ b/crates/uv-resolver/src/universal_marker.rs
@@ -6,7 +6,10 @@ use itertools::Itertools;
use rustc_hash::FxHashMap;
use uv_normalize::{ExtraName, GroupName, PackageName};
-use uv_pep508::{ExtraOperator, MarkerEnvironment, MarkerExpression, MarkerOperator, MarkerTree};
+use uv_pep508::{
+ ExtraOperator, MarkerEnvironment, MarkerExpression, MarkerOperator, MarkerTree,
+ MarkerVariantsEnvironment, MarkerVariantsUniversal,
+};
use uv_pypi_types::{ConflictItem, ConflictKind, Conflicts, Inference};
use crate::ResolveError;
@@ -293,7 +296,7 @@ impl UniversalMarker {
/// This should only be used when evaluating a marker that is known not to
/// have any extras. For example, the PEP 508 markers on a fork.
pub(crate) fn evaluate_no_extras(self, env: &MarkerEnvironment) -> bool {
- self.marker.evaluate(env, &[])
+ self.marker.evaluate(env, &MarkerVariantsUniversal, &[])
}
/// Returns true if this universal marker is satisfied by the given marker
@@ -305,6 +308,7 @@ impl UniversalMarker {
pub(crate) fn evaluate(
self,
env: &MarkerEnvironment,
+ variants: &impl MarkerVariantsEnvironment,
projects: impl Iterator- ,
extras: impl Iterator
- ,
groups: impl Iterator
- ,
@@ -321,6 +325,7 @@ impl UniversalMarker {
groups.map(|(package, group)| encode_package_group(package.borrow(), group.borrow()));
self.marker.evaluate(
env,
+ variants,
&projects
.chain(extras)
.chain(groups)
@@ -829,7 +834,6 @@ pub(crate) fn resolve_conflicts(
mod tests {
use super::*;
use std::str::FromStr;
-
use uv_pypi_types::ConflictSet;
/// Creates a collection of declared conflicts from the sets
@@ -959,7 +963,7 @@ mod tests {
.collect::>();
let groups = Vec::<(PackageName, GroupName)>::new();
assert!(
- !UniversalMarker::new(MarkerTree::TRUE, cm).evaluate_only_extras(&extras, &groups),
+ !UniversalMarker::new(MarkerTree::TRUE, cm).evaluate_only_extras(&extras, &groups,),
"expected `{extra_names:?}` to evaluate to `false` in `{cm:?}`"
);
}
@@ -982,7 +986,7 @@ mod tests {
.collect::>();
let groups = Vec::<(PackageName, GroupName)>::new();
assert!(
- UniversalMarker::new(MarkerTree::TRUE, cm).evaluate_only_extras(&extras, &groups),
+ UniversalMarker::new(MarkerTree::TRUE, cm).evaluate_only_extras(&extras, &groups,),
"expected `{extra_names:?}` to evaluate to `true` in `{cm:?}`"
);
}
diff --git a/crates/uv-resolver/src/version_map.rs b/crates/uv-resolver/src/version_map.rs
index 77415022d..aa2ba392d 100644
--- a/crates/uv-resolver/src/version_map.rs
+++ b/crates/uv-resolver/src/version_map.rs
@@ -11,15 +11,16 @@ use uv_client::{FlatIndexEntry, OwnedArchive, SimpleDetailMetadata, VersionFiles
use uv_configuration::BuildOptions;
use uv_distribution_filename::{DistFilename, WheelFilename};
use uv_distribution_types::{
- HashComparison, IncompatibleSource, IncompatibleWheel, IndexUrl, PrioritizedDist,
- RegistryBuiltWheel, RegistrySourceDist, RequiresPython, SourceDistCompatibility,
- WheelCompatibility,
+ HashComparison, IncompatibleSource, IncompatibleWheel, IndexEntryFilename, IndexUrl,
+ PrioritizedDist, RegistryBuiltWheel, RegistrySourceDist, RegistryVariantsJson, RequiresPython,
+ SourceDistCompatibility, WheelCompatibility,
};
use uv_normalize::PackageName;
use uv_pep440::Version;
use uv_platform_tags::{IncompatibleTag, TagCompatibility, Tags};
use uv_pypi_types::{HashDigest, ResolutionMetadata, Yanked};
use uv_types::HashStrategy;
+use uv_variants::VariantPriority;
use uv_warnings::warn_user_once;
use crate::flat_index::FlatDistributions;
@@ -467,7 +468,7 @@ impl VersionMapLazy {
let yanked = file.yanked.as_deref();
let hashes = file.hashes.clone();
match filename {
- DistFilename::WheelFilename(filename) => {
+ IndexEntryFilename::DistFilename(DistFilename::WheelFilename(filename)) => {
let compatibility = self.wheel_compatibility(
&filename,
&filename.name,
@@ -484,7 +485,9 @@ impl VersionMapLazy {
};
priority_dist.insert_built(dist, hashes, compatibility);
}
- DistFilename::SourceDistFilename(filename) => {
+ IndexEntryFilename::DistFilename(DistFilename::SourceDistFilename(
+ filename,
+ )) => {
let compatibility = self.source_dist_compatibility(
&filename.name,
&filename.version,
@@ -503,6 +506,14 @@ impl VersionMapLazy {
};
priority_dist.insert_source(dist, hashes, compatibility);
}
+ IndexEntryFilename::VariantJson(filename) => {
+ let variant_json = RegistryVariantsJson {
+ filename,
+ file: Box::new(file),
+ index: self.index.clone(),
+ };
+ priority_dist.insert_variant_json(variant_json);
+ }
}
}
if priority_dist.is_empty() {
@@ -589,8 +600,8 @@ impl VersionMapLazy {
}
}
- // Determine a compatibility for the wheel based on tags.
- let priority = if let Some(tags) = &self.tags {
+ // Determine a priority for the wheel based on tags.
+ let tag_priority = if let Some(tags) = &self.tags {
match filename.compatibility(tags) {
TagCompatibility::Incompatible(tag) => {
return WheelCompatibility::Incompatible(IncompatibleWheel::Tag(tag));
@@ -608,6 +619,13 @@ impl VersionMapLazy {
None
};
+ // TODO(konsti): Currently we ignore variants here on only determine them later
+ let variant_priority = if filename.variant().is_none() {
+ VariantPriority::NonVariant
+ } else {
+ VariantPriority::Unknown
+ };
+
// Check if hashes line up. If hashes aren't required, they're considered matching.
let hash_policy = self.hasher.get_package(name, version);
let required_hashes = hash_policy.digests();
@@ -626,7 +644,12 @@ impl VersionMapLazy {
// Break ties with the build tag.
let build_tag = filename.build_tag().cloned();
- WheelCompatibility::Compatible(hash, priority, build_tag)
+ WheelCompatibility::Compatible {
+ hash,
+ tag_priority,
+ variant_priority,
+ build_tag,
+ }
}
}
diff --git a/crates/uv-small-str/src/lib.rs b/crates/uv-small-str/src/lib.rs
index 1524f1b99..0d751186d 100644
--- a/crates/uv-small-str/src/lib.rs
+++ b/crates/uv-small-str/src/lib.rs
@@ -3,6 +3,8 @@ use std::cmp::PartialEq;
use std::ops::Deref;
/// An optimized type for immutable identifiers. Represented as an [`arcstr::ArcStr`] internally.
+///
+/// This type is one pointer wide.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SmallString(arcstr::ArcStr);
@@ -159,3 +161,13 @@ impl schemars::JsonSchema for SmallString {
String::json_schema(generator)
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn small_str_size() {
+ assert_eq!(size_of::(), size_of::());
+ }
+}
diff --git a/crates/uv-static/src/env_vars.rs b/crates/uv-static/src/env_vars.rs
index 020d2e72b..ebc942bd7 100644
--- a/crates/uv-static/src/env_vars.rs
+++ b/crates/uv-static/src/env_vars.rs
@@ -1236,4 +1236,15 @@ impl EnvVars {
/// around invalid artifacts in rare cases.
#[attr_added_in("0.8.23")]
pub const UV_SKIP_WHEEL_FILENAME_CHECK: &'static str = "UV_SKIP_WHEEL_FILENAME_CHECK";
+
+ /// A comma separated list of variant provider backends that use the current environment instead
+ /// of an isolated environment.
+ ///
+ /// The requirements need to be installed in the current environment, no provider `requires`
+ /// will be installed. This option is intended for development and as an escape hatch where
+ /// isolation fails.
+ ///
+ /// Example: `UV_NO_PROVIDER_ISOLATION=gpu.provider:api,cpu,blas.backend`
+ #[attr_added_in("0.9.2")]
+ pub const UV_NO_PROVIDER_ISOLATION: &'static str = "UV_NO_PROVIDER_ISOLATION";
}
diff --git a/crates/uv-types/Cargo.toml b/crates/uv-types/Cargo.toml
index c295adf13..9383d7e8b 100644
--- a/crates/uv-types/Cargo.toml
+++ b/crates/uv-types/Cargo.toml
@@ -24,9 +24,11 @@ uv-git = { workspace = true }
uv-normalize = { workspace = true }
uv-once-map = { workspace = true }
uv-pep440 = { workspace = true }
+uv-pep508 = { workspace = true }
uv-pypi-types = { workspace = true }
uv-python = { workspace = true }
uv-redacted = { workspace = true }
+uv-variants = { workspace = true }
uv-workspace = { workspace = true }
anyhow = { workspace = true }
diff --git a/crates/uv-types/src/hash.rs b/crates/uv-types/src/hash.rs
index 9d48c8221..039af2a01 100644
--- a/crates/uv-types/src/hash.rs
+++ b/crates/uv-types/src/hash.rs
@@ -10,6 +10,7 @@ use uv_distribution_types::{
};
use uv_normalize::PackageName;
use uv_pep440::Version;
+use uv_pep508::MarkerVariantsUniversal;
use uv_pypi_types::{HashDigest, HashDigests, HashError, ResolverMarkerEnvironment};
use uv_redacted::DisplaySafeUrl;
@@ -134,9 +135,11 @@ impl HashStrategy {
// First, index the constraints by name.
for (requirement, digests) in constraints {
- if !requirement
- .evaluate_markers(marker_env.map(ResolverMarkerEnvironment::markers), &[])
- {
+ if !requirement.evaluate_markers(
+ marker_env.map(ResolverMarkerEnvironment::markers),
+ &MarkerVariantsUniversal,
+ &[],
+ ) {
continue;
}
@@ -178,9 +181,11 @@ impl HashStrategy {
// package.
let mut requirement_hashes = FxHashMap::>::default();
for (requirement, digests) in requirements {
- if !requirement
- .evaluate_markers(marker_env.map(ResolverMarkerEnvironment::markers), &[])
- {
+ if !requirement.evaluate_markers(
+ marker_env.map(ResolverMarkerEnvironment::markers),
+ &MarkerVariantsUniversal,
+ &[],
+ ) {
continue;
}
diff --git a/crates/uv-types/src/traits.rs b/crates/uv-types/src/traits.rs
index 6054ba302..9f7eac724 100644
--- a/crates/uv-types/src/traits.rs
+++ b/crates/uv-types/src/traits.rs
@@ -5,7 +5,6 @@ use std::path::{Path, PathBuf};
use anyhow::Result;
use rustc_hash::FxHashSet;
-
use uv_cache::Cache;
use uv_configuration::{BuildKind, BuildOptions, BuildOutput, SourceStrategy};
use uv_distribution_filename::DistFilename;
@@ -17,6 +16,8 @@ use uv_distribution_types::{
use uv_git::GitResolver;
use uv_normalize::PackageName;
use uv_python::{Interpreter, PythonEnvironment};
+use uv_variants::VariantProviderOutput;
+use uv_variants::cache::VariantProviderCache;
use uv_workspace::WorkspaceCache;
use crate::{BuildArena, BuildIsolation};
@@ -60,6 +61,7 @@ use crate::{BuildArena, BuildIsolation};
/// them.
pub trait BuildContext {
type SourceDistBuilder: SourceBuildTrait;
+ type VariantsBuilder: VariantsTrait;
// Note: this function is async deliberately, because downstream code may need to
// run async code to get the interpreter, to resolve the Python version.
@@ -72,6 +74,9 @@ pub trait BuildContext {
/// Return a reference to the Git resolver.
fn git(&self) -> &GitResolver;
+ /// Return a reference to the variant cache.
+ fn variants(&self) -> &VariantProviderCache;
+
/// Return a reference to the build arena.
fn build_arena(&self) -> &BuildArena;
@@ -161,6 +166,14 @@ pub trait BuildContext {
build_kind: BuildKind,
version_id: Option<&'a str>,
) -> impl Future