From 2c68dfd4a93d650c231388a7eb88774e0126ecba Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Fri, 20 Dec 2024 14:11:27 -0500 Subject: [PATCH] Backtrack to non-local versions when wheels are missing platform support (#10046) ## Summary This is yet another variation on https://github.com/astral-sh/uv/pull/9928, with a few minor changes: 1. It only applies to local versions (e.g., `2.5.1+cpu`). 2. It only _considers_ the non-local version as an alternative (e.g., `2.5.1`). 3. It only _considers_ the non-local alternative if it _does_ support the unsupported platform. 4. Instead of failing, it falls back to using the local version. So, this is far less strict, and is effectively designed to solve PyTorch but nothing else. It's also not user-configurable, except by way of using `environments` to exclude platforms. --- .../src/prioritized_distribution.rs | 77 +- .../uv-resolver/src/resolver/availability.rs | 7 +- .../uv-resolver/src/resolver/environment.rs | 45 +- crates/uv-resolver/src/resolver/mod.rs | 401 ++++++++--- crates/uv/tests/it/lock.rs | 677 ++++++++++++++++++ crates/uv/tests/it/pip_compile.rs | 118 ++- .../it__ecosystem__packse-lock-file.snap | 2 +- ...__ecosystem__warehouse-uv-lock-output.snap | 2 +- docs/guides/integration/pytorch.md | 20 +- 9 files changed, 1206 insertions(+), 143 deletions(-) diff --git a/crates/uv-distribution-types/src/prioritized_distribution.rs b/crates/uv-distribution-types/src/prioritized_distribution.rs index daa1f38b1..029514cff 100644 --- a/crates/uv-distribution-types/src/prioritized_distribution.rs +++ b/crates/uv-distribution-types/src/prioritized_distribution.rs @@ -1,7 +1,8 @@ use std::fmt::{Display, Formatter}; -use uv_distribution_filename::BuildTag; +use uv_distribution_filename::{BuildTag, WheelFilename}; use uv_pep440::VersionSpecifiers; +use uv_pep508::{MarkerExpression, MarkerOperator, MarkerTree, MarkerValueString}; use uv_platform_tags::{IncompatibleTag, TagPriority}; use uv_pypi_types::{HashDigest, Yanked}; @@ -14,7 +15,7 @@ use crate::{ pub struct PrioritizedDist(Box); /// [`PrioritizedDist`] is boxed because [`Dist`] is large. -#[derive(Debug, Default, Clone)] +#[derive(Debug, Clone)] struct PrioritizedDistInner { /// The highest-priority source distribution. Between compatible source distributions this priority is arbitrary. source: Option<(RegistrySourceDist, SourceDistCompatibility)>, @@ -25,6 +26,20 @@ struct PrioritizedDistInner { wheels: Vec<(RegistryBuiltWheel, WheelCompatibility)>, /// The hashes for each distribution. hashes: Vec, + /// The set of supported platforms for the distribution, described in terms of their markers. + markers: MarkerTree, +} + +impl Default for PrioritizedDistInner { + fn default() -> Self { + Self { + source: None, + best_wheel_index: None, + wheels: Vec::new(), + hashes: Vec::new(), + markers: MarkerTree::FALSE, + } + } } /// A distribution that can be used for both resolution and installation. @@ -70,6 +85,16 @@ impl CompatibleDist<'_> { CompatibleDist::IncompatibleWheel { sdist, .. } => sdist.file.requires_python.as_ref(), } } + + /// Return the set of supported platform the distribution, in terms of their markers. + pub fn implied_markers(&self) -> MarkerTree { + match self { + CompatibleDist::InstalledDist(_) => MarkerTree::TRUE, + CompatibleDist::SourceDist { prioritized, .. } => prioritized.0.markers, + CompatibleDist::CompatibleWheel { prioritized, .. } => prioritized.0.markers, + CompatibleDist::IncompatibleWheel { prioritized, .. } => prioritized.0.markers, + } + } } #[derive(Debug, PartialEq, Eq, Clone)] @@ -257,6 +282,7 @@ impl PrioritizedDist { compatibility: WheelCompatibility, ) -> Self { Self(Box::new(PrioritizedDistInner { + markers: implied_markers(&dist.filename), best_wheel_index: Some(0), wheels: vec![(dist, compatibility)], source: None, @@ -271,6 +297,7 @@ impl PrioritizedDist { compatibility: SourceDistCompatibility, ) -> Self { Self(Box::new(PrioritizedDistInner { + markers: MarkerTree::TRUE, best_wheel_index: None, wheels: vec![], source: Some((dist, compatibility)), @@ -293,8 +320,11 @@ impl PrioritizedDist { } else { self.0.best_wheel_index = Some(self.0.wheels.len()); } - self.0.wheels.push((dist, compatibility)); self.0.hashes.extend(hashes); + if !self.0.markers.is_true() { + self.0.markers.or(implied_markers(&dist.filename)); + } + self.0.wheels.push((dist, compatibility)); } /// Insert the given source distribution into the [`PrioritizedDist`]. @@ -312,7 +342,9 @@ impl PrioritizedDist { } else { self.0.source = Some((dist, compatibility)); } - + if !self.0.markers.is_true() { + self.0.markers.or(MarkerTree::TRUE); + } self.0.hashes.extend(hashes); } @@ -563,6 +595,7 @@ impl IncompatibleSource { } impl IncompatibleWheel { + #[allow(clippy::match_like_matches_macro)] fn is_more_compatible(&self, other: &Self) -> bool { match self { Self::ExcludeNewer(timestamp_self) => match other { @@ -599,3 +632,39 @@ impl IncompatibleWheel { } } } + +/// Given a wheel filename, determine the set of supported platforms, in terms of their markers. +pub fn implied_markers(filename: &WheelFilename) -> MarkerTree { + let mut marker = MarkerTree::FALSE; + for platform_tag in &filename.platform_tag { + match platform_tag.as_str() { + "any" => marker.or(MarkerTree::TRUE), + tag if tag.starts_with("win") => { + marker.or(MarkerTree::expression(MarkerExpression::String { + key: MarkerValueString::SysPlatform, + operator: MarkerOperator::Equal, + value: "win32".to_string(), + })); + } + tag if tag.starts_with("macosx") => { + marker.or(MarkerTree::expression(MarkerExpression::String { + key: MarkerValueString::SysPlatform, + operator: MarkerOperator::Equal, + value: "darwin".to_string(), + })); + } + tag if tag.starts_with("manylinux") + || tag.starts_with("musllinux") + || tag.starts_with("linux") => + { + marker.or(MarkerTree::expression(MarkerExpression::String { + key: MarkerValueString::SysPlatform, + operator: MarkerOperator::Equal, + value: "linux".to_string(), + })); + } + _ => {} + } + } + marker +} diff --git a/crates/uv-resolver/src/resolver/availability.rs b/crates/uv-resolver/src/resolver/availability.rs index 3a499c1ae..cf8a42b60 100644 --- a/crates/uv-resolver/src/resolver/availability.rs +++ b/crates/uv-resolver/src/resolver/availability.rs @@ -3,8 +3,7 @@ use std::fmt::{Display, Formatter}; use uv_distribution_types::IncompatibleDist; use uv_pep440::{Version, VersionSpecifiers}; -use crate::resolver::MetadataUnavailable; -use crate::ResolverEnvironment; +use crate::resolver::{MetadataUnavailable, VersionFork}; /// The reason why a package or a version cannot be used. #[derive(Debug, Clone, Eq, PartialEq)] @@ -170,6 +169,6 @@ pub(crate) enum ResolverVersion { Unavailable(Version, UnavailableVersion), /// A usable version Unforked(Version), - /// A set of forks. - Forked(Vec), + /// A set of forks, optionally with resolved versions + Forked(Vec), } diff --git a/crates/uv-resolver/src/resolver/environment.rs b/crates/uv-resolver/src/resolver/environment.rs index 02c012830..5d2914985 100644 --- a/crates/uv-resolver/src/resolver/environment.rs +++ b/crates/uv-resolver/src/resolver/environment.rs @@ -512,7 +512,7 @@ impl<'d> Forker<'d> { } /// Fork the resolver based on a `Requires-Python` specifier. -pub(crate) fn fork_python_requirement( +pub(crate) fn fork_version_by_python_requirement( requires_python: &VersionSpecifiers, python_requirement: &PythonRequirement, env: &ResolverEnvironment, @@ -555,6 +555,49 @@ pub(crate) fn fork_python_requirement( envs } +/// Fork the resolver based on a marker. +pub(crate) fn fork_version_by_marker( + env: &ResolverEnvironment, + marker: MarkerTree, +) -> Option<(ResolverEnvironment, ResolverEnvironment)> { + let Kind::Universal { + markers: ref env_marker, + .. + } = env.kind + else { + panic!("resolver must be in universal mode for forking") + }; + + // Attempt to split based on the marker. + // + // For example, given `python_version >= '3.10'` and the split marker `sys_platform == 'linux'`, + // the result will be: + // + // `python_version >= '3.10' and sys_platform == 'linux'` + // `python_version >= '3.10' and sys_platform != 'linux'` + // + // If the marker is disjoint with the current environment, then we should return an empty list. + // If the marker complement is disjoint with the current environment, then we should also return + // an empty list. + // + // For example, given `python_version >= '3.10' and sys_platform == 'linux'` and the split marker + // `sys_platform == 'win32'`, return an empty list, since the following isn't satisfiable: + // + // python_version >= '3.10' and sys_platform == 'linux' and sys_platform == 'win32' + if env_marker.is_disjoint(marker) { + return None; + } + let with_marker = env.narrow_environment(marker); + + let complement = marker.negate(); + if env_marker.is_disjoint(complement) { + return None; + } + let without_marker = env.narrow_environment(complement); + + Some((with_marker, without_marker)) +} + #[cfg(test)] mod tests { use std::ops::Bound; diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs index 57e8cffa3..aeeebb8d9 100644 --- a/crates/uv-resolver/src/resolver/mod.rs +++ b/crates/uv-resolver/src/resolver/mod.rs @@ -39,7 +39,7 @@ use uv_pypi_types::{ use uv_types::{BuildContext, HashStrategy, InstalledPackagesProvider}; use uv_warnings::warn_user_once; -use crate::candidate_selector::{CandidateDist, CandidateSelector}; +use crate::candidate_selector::{Candidate, CandidateDist, CandidateSelector}; use crate::dependency_provider::UvDependencyProvider; use crate::error::{NoSolutionError, ResolveError}; use crate::fork_indexes::ForkIndexes; @@ -61,7 +61,9 @@ pub(crate) use crate::resolver::availability::{ use crate::resolver::batch_prefetch::BatchPrefetcher; pub use crate::resolver::derivation::DerivationChainBuilder; pub use crate::resolver::environment::ResolverEnvironment; -use crate::resolver::environment::{fork_python_requirement, ForkingPossibility}; +use crate::resolver::environment::{ + fork_version_by_marker, fork_version_by_python_requirement, ForkingPossibility, +}; pub(crate) use crate::resolver::fork_map::{ForkMap, ForkSet}; pub(crate) use crate::resolver::urls::Urls; use crate::universal_marker::{ConflictMarker, UniversalMarker}; @@ -76,7 +78,7 @@ pub use crate::resolver::provider::{ use crate::resolver::reporter::Facade; pub use crate::resolver::reporter::{BuildId, Reporter}; use crate::yanks::AllowedYanks; -use crate::{marker, DependencyMode, Exclusions, FlatIndex, Options, ResolutionMode}; +use crate::{marker, DependencyMode, Exclusions, FlatIndex, Options, ResolutionMode, VersionMap}; mod availability; mod batch_prefetch; @@ -333,7 +335,7 @@ impl ResolverState ResolverState { - debug!("No compatible version found for: {next_package}"); - - let term_intersection = state - .pubgrub - .partial_solution - .term_intersection_for_package(next_id) - .expect("a package was chosen but we don't have a term"); - - if let PubGrubPackageInner::Package { ref name, .. } = &**next_package { - // Check if the decision was due to the package being unavailable - if let Some(entry) = self.unavailable_packages.get(name) { - state - .pubgrub - .add_incompatibility(Incompatibility::custom_term( - next_id, - term_intersection.clone(), - UnavailableReason::Package(entry.clone()), - )); - continue; - } - } - - state - .pubgrub - .add_incompatibility(Incompatibility::no_versions( - next_id, - term_intersection.clone(), - )); - continue; - } - Some(version) => version, - }; - - let version = match version { - ResolverVersion::Unforked(version) => version, - ResolverVersion::Forked(forks) => { - for mut fork in self.version_forks_to_fork_states(state, forks) { - fork.initial = Some(next_id); - forked_states.push(fork); - } - continue 'FORK; - } - ResolverVersion::Unavailable(version, reason) => { - state.add_unavailable_version(version, reason); - continue; - } - }; - - // Only consider registry packages for prefetch. - if url.is_none() { - state.prefetcher.prefetch_batches( + let version = if let Some(version) = state.initial_version.take() { + // If we just forked based on platform support, we can skip version selection, + // since the fork operation itself already selected the appropriate version for + // the platform. + version + } else { + let term_intersection = state + .pubgrub + .partial_solution + .term_intersection_for_package(next_id) + .expect("a package was chosen but we don't have a term"); + let decision = self.choose_version( next_package, + next_id, index, - &version, term_intersection.unwrap_positive(), - state - .pubgrub - .partial_solution - .unchanging_term_for_package(next_id), - &state.python_requirement, - &self.selector, + &mut state.pins, + &preferences, + &state.fork_urls, &state.env, + &state.python_requirement, + &mut visited, + &request_sink, )?; - } + + // Pick the next compatible version. + let version = match decision { + None => { + debug!("No compatible version found for: {next_package}"); + + let term_intersection = state + .pubgrub + .partial_solution + .term_intersection_for_package(next_id) + .expect("a package was chosen but we don't have a term"); + + if let PubGrubPackageInner::Package { ref name, .. } = &**next_package { + // Check if the decision was due to the package being unavailable + if let Some(entry) = self.unavailable_packages.get(name) { + state.pubgrub.add_incompatibility( + Incompatibility::custom_term( + next_id, + term_intersection.clone(), + UnavailableReason::Package(entry.clone()), + ), + ); + continue; + } + } + + state + .pubgrub + .add_incompatibility(Incompatibility::no_versions( + next_id, + term_intersection.clone(), + )); + continue; + } + Some(version) => version, + }; + + let version = match version { + ResolverVersion::Unforked(version) => version, + ResolverVersion::Forked(forks) => { + forked_states.extend(self.version_forks_to_fork_states(state, forks)); + continue 'FORK; + } + ResolverVersion::Unavailable(version, reason) => { + state.add_unavailable_version(version, reason); + continue; + } + }; + + // Only consider registry packages for prefetch. + if url.is_none() { + state.prefetcher.prefetch_batches( + next_package, + index, + &version, + term_intersection.unwrap_positive(), + state + .pubgrub + .partial_solution + .unchanging_term_for_package(next_id), + &state.python_requirement, + &self.selector, + &state.env, + )?; + } + + version + }; self.on_progress(next_package, &version); @@ -878,7 +887,7 @@ impl ResolverState, + forks: Vec, ) -> impl Iterator + '_ { // This is a somewhat tortured technique to ensure // that our resolver state is only cloned as much @@ -889,12 +898,13 @@ impl ResolverState ResolverState, index: Option<&IndexUrl>, range: &Range, pins: &mut FilePins, @@ -1038,10 +1049,11 @@ impl ResolverState ResolverState, name: &PackageName, index: Option<&IndexUrl>, range: &Range, - package: &PubGrubPackage, preferences: &Preferences, env: &ResolverEnvironment, python_requirement: &PythonRequirement, @@ -1216,7 +1229,11 @@ impl ResolverState ResolverState>() .join(", ") ); + let forks = forks + .into_iter() + .map(|env| VersionFork { + env, + id, + version: None, + }) + .collect(); return Ok(Some(ResolverVersion::Forked(forks))); } } @@ -1241,6 +1266,24 @@ impl ResolverState sdist .filename() @@ -1258,12 +1301,160 @@ impl ResolverState, + name: &PackageName, + index: Option<&IndexUrl>, + range: &Range, + preferences: &Preferences, + env: &ResolverEnvironment, + pins: &mut FilePins, + request_sink: &Sender, + ) -> Result, ResolveError> { + // For now, we only apply this to local versions. + if !candidate.version().is_local() { + return Ok(None); + } + + // If the package is already compatible with all environments (as is the case for + // packages that include a source distribution), we don't need to fork. + if dist.implied_markers().is_true() { + return Ok(None); + }; + + debug!( + "Looking at local version: {}=={}", + name, + candidate.version() + ); + + // If there's a non-local version... + let range = range.clone().intersection(&Range::singleton( + candidate.version().clone().without_local(), + )); + + let Some(base_candidate) = self.selector.select( + name, + &range, + version_maps, + preferences, + &self.installed_packages, + &self.exclusions, + index, + env, + ) else { + return Ok(None); + }; + let CandidateDist::Compatible(base_dist) = base_candidate.dist() else { + return Ok(None); + }; + + // ...and the non-local version has greater platform support... + let remainder = { + let mut remainder = base_dist.implied_markers(); + remainder.and(dist.implied_markers().negate()); + remainder + }; + if remainder.is_false() { + return Ok(None); + } + + // If the remainder isn't relevant to the current environment, there's no need to fork. + // For example, if we're solving for `sys_platform == 'darwin'` but the remainder is + // `sys_platform == 'linux'`, we don't need to fork. + if !env.included_by_marker(remainder) { + return Ok(None); + } + + // Similarly, if the local distribution is incompatible with the current environment, then + // use the base distribution instead (but don't fork). + if !env.included_by_marker(dist.implied_markers()) { + let filename = match dist.for_installation() { + ResolvedDistRef::InstallableRegistrySourceDist { sdist, .. } => sdist + .filename() + .unwrap_or(Cow::Borrowed("unknown filename")), + ResolvedDistRef::InstallableRegistryBuiltDist { wheel, .. } => wheel + .filename() + .unwrap_or(Cow::Borrowed("unknown filename")), + ResolvedDistRef::Installed { .. } => Cow::Borrowed("installed"), + }; + + debug!( + "Preferring non-local candidate: {}=={} [{}] ({})", + name, + base_candidate.version(), + base_candidate.choice_kind(), + filename, + ); + self.visit_candidate(&base_candidate, base_dist, package, pins, request_sink)?; + + return Ok(Some(ResolverVersion::Unforked( + base_candidate.version().clone(), + ))); + } + + // Otherwise, we need to fork. + let Some((base_env, local_env)) = fork_version_by_marker(env, remainder) else { + return Ok(None); + }; + + debug!( + "Forking platform for {}=={} ({})", + name, + candidate.version(), + [&base_env, &local_env] + .iter() + .map(ToString::to_string) + .collect::>() + .join(", ") + ); + self.visit_candidate(candidate, dist, package, pins, request_sink)?; + self.visit_candidate(&base_candidate, base_dist, package, pins, request_sink)?; + + let forks = vec![ + VersionFork { + env: base_env.clone(), + id, + version: Some(base_candidate.version().clone()), + }, + VersionFork { + env: local_env.clone(), + id, + version: Some(candidate.version().clone()), + }, + ]; + Ok(Some(ResolverVersion::Forked(forks))) + } + + /// Visit a selected candidate. + fn visit_candidate( + &self, + candidate: &Candidate, + dist: &CompatibleDist, + package: &PubGrubPackage, + pins: &mut FilePins, + request_sink: &Sender, + ) -> Result<(), ResolveError> { + // We want to return a package pinned to a specific version; but we _also_ want to + // store the exact file that we selected to satisfy that version. + pins.insert(candidate, dist); // Emit a request to fetch the metadata for this version. if matches!(&**package, PubGrubPackageInner::Package { .. }) { @@ -1283,7 +1474,7 @@ impl ResolverState, /// The initial package to select. If set, the first iteration over this state will avoid /// asking PubGrub for the highest-priority package, and will instead use the provided package. - initial: Option>, + initial_id: Option>, + /// The initial version to select. If set, the first iteration over this state will avoid + /// asking PubGrub for the highest-priority version, and will instead use the provided version. + initial_version: Option, /// The next package on which to run unit propagation. next: Id, /// The set of pinned versions we accrue throughout resolution. @@ -2278,7 +2472,8 @@ impl ForkState { prefetcher: BatchPrefetcher, ) -> Self { Self { - initial: None, + initial_id: None, + initial_version: None, next: pubgrub.root_package, pubgrub, pins: FilePins::default(), @@ -3306,6 +3501,16 @@ impl PartialEq for Fork { } } +#[derive(Debug, Clone)] +pub(crate) struct VersionFork { + /// The environment to use in the fork. + env: ResolverEnvironment, + /// The initial package to select in the fork. + id: Id, + /// The initial version to set for the selected package in the fork. + version: Option, +} + /// Returns an error if a conflicting extra is found in the given requirements. /// /// Specifically, if there is any conflicting extra (just one is enough) that diff --git a/crates/uv/tests/it/lock.rs b/crates/uv/tests/it/lock.rs index 6606340b9..1d42f178f 100644 --- a/crates/uv/tests/it/lock.rs +++ b/crates/uv/tests/it/lock.rs @@ -14727,6 +14727,49 @@ fn lock_named_index_cli() -> Result<()> { Resolved 3 packages in [TIME] "###); + let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock")).unwrap(); + + insta::with_settings!({ + filters => context.filters(), + }, { + assert_snapshot!( + lock, @r###" + version = 1 + requires-python = ">=3.12" + + [[package]] + name = "jinja2" + version = "3.1.2" + source = { registry = "https://download.pytorch.org/whl/cu121" } + dependencies = [ + { name = "markupsafe" }, + ] + wheels = [ + { url = "https://download.pytorch.org/whl/Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" }, + ] + + [[package]] + name = "markupsafe" + version = "3.0.2" + source = { registry = "https://download.pytorch.org/whl/cu121" } + wheels = [ + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396" }, + ] + + [[package]] + name = "project" + version = "0.1.0" + source = { virtual = "." } + dependencies = [ + { name = "jinja2" }, + ] + + [package.metadata] + requires-dist = [{ name = "jinja2", specifier = "==3.1.2", index = "https://download.pytorch.org/whl/cu121" }] + "### + ); + }); + Ok(()) } @@ -20785,6 +20828,71 @@ fn lock_self_marker_incompatible() -> Result<()> { Ok(()) } +/// When resolving `PyQt5-Qt5`, we choose the latest version, even though it doesn't support +/// Windows. This may change in the future. +#[test] +fn lock_split_on_windows() -> Result<()> { + let context = TestContext::new("3.12"); + + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str( + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = ["pyqt5-qt5"] + "#, + )?; + + uv_snapshot!(context.filters(), context.lock(), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 2 packages in [TIME] + "###); + + let lock = context.read("uv.lock"); + + insta::with_settings!({ + filters => context.filters(), + }, { + assert_snapshot!( + lock, @r###" + version = 1 + requires-python = ">=3.12" + + [options] + exclude-newer = "2024-03-25T00:00:00Z" + + [[package]] + name = "project" + version = "0.1.0" + source = { virtual = "." } + dependencies = [ + { name = "pyqt5-qt5" }, + ] + + [package.metadata] + requires-dist = [{ name = "pyqt5-qt5" }] + + [[package]] + name = "pyqt5-qt5" + version = "5.15.13" + source = { registry = "https://pypi.org/simple" } + wheels = [ + { url = "https://files.pythonhosted.org/packages/40/dc/96d9d0ba0d13256343b53efffe8729f278e62409ab4c937bb22e70ab98ac/PyQt5_Qt5-5.15.13-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:92575a9e96a27c4ed67c56c7048ded7461a1655d5d21f0e05064664e6e9fcbdf", size = 38771962 }, + { url = "https://files.pythonhosted.org/packages/c9/8b/4441c208c8ca29b50fab6467ebfa32b6401d16c5c915a031a48dc85dfa7a/PyQt5_Qt5-5.15.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:141859f2ffe04cc6c5db970e2b6ad9f98897805d886a14c52614e3799daab6d6", size = 36663754 }, + ] + "### + ); + }); + + Ok(()) +} + #[test] fn lock_missing_git_prefix() -> Result<()> { let context = TestContext::new("3.12"); @@ -20820,3 +20928,572 @@ fn lock_missing_git_prefix() -> Result<()> { Ok(()) } + +#[test] +fn lock_pytorch_cpu() -> Result<()> { + let context = TestContext::new("3.12"); + + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str( + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12.0" + dependencies = [] + + [project.optional-dependencies] + cpu = [ + "torch>=2.5.1", + "torchvision>=0.20.1", + ] + cu124 = [ + "torch>=2.5.1", + "torchvision>=0.20.1", + ] + + [tool.uv] + conflicts = [ + [ + { extra = "cpu" }, + { extra = "cu124" }, + ], + ] + + [tool.uv.sources] + torch = [ + { index = "pytorch-cpu", extra = "cpu" }, + { index = "pytorch-cu124", extra = "cu124" }, + ] + torchvision = [ + { index = "pytorch-cpu", extra = "cpu" }, + { index = "pytorch-cu124", extra = "cu124" }, + ] + + [[tool.uv.index]] + name = "pytorch-cpu" + url = "https://download.pytorch.org/whl/cpu" + explicit = true + + [[tool.uv.index]] + name = "pytorch-cu124" + url = "https://download.pytorch.org/whl/cu124" + explicit = true + + "#, + )?; + + uv_snapshot!(context.filters(), context.lock().env_remove(EnvVars::UV_EXCLUDE_NEWER), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 31 packages in [TIME] + "###); + + let lock = context.read("uv.lock"); + + insta::with_settings!({ + filters => context.filters(), + }, { + assert_snapshot!( + lock, @r###" + version = 1 + requires-python = ">=3.12.[X]" + resolution-markers = [ + "sys_platform != 'darwin'", + "sys_platform == 'darwin'", + ] + conflicts = [[ + { package = "project", extra = "cpu" }, + { package = "project", extra = "cu124" }, + ]] + + [[package]] + name = "filelock" + version = "3.16.1" + source = { registry = "https://pypi.org/simple" } + sdist = { url = "https://files.pythonhosted.org/packages/9d/db/3ef5bb276dae18d6ec2124224403d1d67bccdbefc17af4cc8f553e341ab1/filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435", size = 18037 } + wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", size = 16163 }, + ] + + [[package]] + name = "fsspec" + version = "2024.12.0" + source = { registry = "https://pypi.org/simple" } + sdist = { url = "https://files.pythonhosted.org/packages/ee/11/de70dee31455c546fbc88301971ec03c328f3d1138cfba14263f651e9551/fsspec-2024.12.0.tar.gz", hash = "sha256:670700c977ed2fb51e0d9f9253177ed20cbde4a3e5c0283cc5385b5870c8533f", size = 291600 } + wheels = [ + { url = "https://files.pythonhosted.org/packages/de/86/5486b0188d08aa643e127774a99bac51ffa6cf343e3deb0583956dca5b22/fsspec-2024.12.0-py3-none-any.whl", hash = "sha256:b520aed47ad9804237ff878b504267a3b0b441e97508bd6d2d8774e3db85cee2", size = 183862 }, + ] + + [[package]] + name = "jinja2" + version = "3.1.4" + source = { registry = "https://pypi.org/simple" } + dependencies = [ + { name = "markupsafe" }, + ] + sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } + wheels = [ + { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, + ] + + [[package]] + name = "markupsafe" + version = "3.0.2" + source = { registry = "https://pypi.org/simple" } + sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } + wheels = [ + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, + ] + + [[package]] + name = "mpmath" + version = "1.3.0" + source = { registry = "https://pypi.org/simple" } + sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } + wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, + ] + + [[package]] + name = "networkx" + version = "3.4.2" + source = { registry = "https://pypi.org/simple" } + sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 } + wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, + ] + + [[package]] + name = "numpy" + version = "2.2.0" + source = { registry = "https://pypi.org/simple" } + sdist = { url = "https://files.pythonhosted.org/packages/47/1b/1d565e0f6e156e1522ab564176b8b29d71e13d8caf003a08768df3d5cec5/numpy-2.2.0.tar.gz", hash = "sha256:140dd80ff8981a583a60980be1a655068f8adebf7a45a06a6858c873fcdcd4a0", size = 20225497 } + wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/bc/a20dc4e1d051149052762e7647455311865d11c603170c476d1e910a353e/numpy-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cff210198bb4cae3f3c100444c5eaa573a823f05c253e7188e1362a5555235b3", size = 20909153 }, + { url = "https://files.pythonhosted.org/packages/60/3d/ac4fb63f36db94f4c7db05b45e3ecb3f88f778ca71850664460c78cfde41/numpy-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b92a5828bd4d9aa0952492b7de803135038de47343b2aa3cc23f3b71a3dc4e", size = 14095021 }, + { url = "https://files.pythonhosted.org/packages/41/6d/a654d519d24e4fcc7a83d4a51209cda086f26cf30722b3d8ffc1aa9b775e/numpy-2.2.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:ebe5e59545401fbb1b24da76f006ab19734ae71e703cdb4a8b347e84a0cece67", size = 5125491 }, + { url = "https://files.pythonhosted.org/packages/e6/22/fab7e1510a62e5092f4e6507a279020052b89f11d9cfe52af7f52c243b04/numpy-2.2.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e2b8cd48a9942ed3f85b95ca4105c45758438c7ed28fff1e4ce3e57c3b589d8e", size = 6658534 }, + { url = "https://files.pythonhosted.org/packages/fc/29/a3d938ddc5a534cd53df7ab79d20a68db8c67578de1df0ae0118230f5f54/numpy-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57fcc997ffc0bef234b8875a54d4058afa92b0b0c4223fc1f62f24b3b5e86038", size = 14046306 }, + { url = "https://files.pythonhosted.org/packages/90/24/d0bbb56abdd8934f30384632e3c2ca1ebfeb5d17e150c6e366ba291de36b/numpy-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ad7d11b309bd132d74397fcf2920933c9d1dc865487128f5c03d580f2c3d03", size = 16095819 }, + { url = "https://files.pythonhosted.org/packages/99/9c/58a673faa9e8a0e77248e782f7a17410cf7259b326265646fd50ed49c4e1/numpy-2.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cb24cca1968b21355cc6f3da1a20cd1cebd8a023e3c5b09b432444617949085a", size = 15243215 }, + { url = "https://files.pythonhosted.org/packages/9c/61/f311693f78cbf635cfb69ce9e1e857ff83937a27d93c96ac5932fd33e330/numpy-2.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0798b138c291d792f8ea40fe3768610f3c7dd2574389e37c3f26573757c8f7ef", size = 17860175 }, + { url = "https://files.pythonhosted.org/packages/11/3e/491c34262cb1fc9dd13a00beb80d755ee0517b17db20e54cac7aa524533e/numpy-2.2.0-cp312-cp312-win32.whl", hash = "sha256:afe8fb968743d40435c3827632fd36c5fbde633b0423da7692e426529b1759b1", size = 6273281 }, + { url = "https://files.pythonhosted.org/packages/89/ea/00537f599eb230771157bc509f6ea5b2dddf05d4b09f9d2f1d7096a18781/numpy-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a4199f519e57d517ebd48cb76b36c82da0360781c6a0353e64c0cac30ecaad3", size = 12613227 }, + { url = "https://files.pythonhosted.org/packages/bd/4c/0d1eef206545c994289e7a9de21b642880a11e0ed47a2b0c407c688c4f69/numpy-2.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f8c8b141ef9699ae777c6278b52c706b653bf15d135d302754f6b2e90eb30367", size = 20895707 }, + { url = "https://files.pythonhosted.org/packages/16/cb/88f6c1e6df83002c421d5f854ccf134aa088aa997af786a5dac3f32ec99b/numpy-2.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f0986e917aca18f7a567b812ef7ca9391288e2acb7a4308aa9d265bd724bdae", size = 14110592 }, + { url = "https://files.pythonhosted.org/packages/b4/54/817e6894168a43f33dca74199ba0dd0f1acd99aa6323ed6d323d63d640a2/numpy-2.2.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:1c92113619f7b272838b8d6702a7f8ebe5edea0df48166c47929611d0b4dea69", size = 5110858 }, + { url = "https://files.pythonhosted.org/packages/c7/99/00d8a1a8eb70425bba7880257ed73fed08d3e8d05da4202fb6b9a81d5ee4/numpy-2.2.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5a145e956b374e72ad1dff82779177d4a3c62bc8248f41b80cb5122e68f22d13", size = 6645143 }, + { url = "https://files.pythonhosted.org/packages/34/86/5b9c2b7c56e7a9d9297a0a4be0b8433f498eba52a8f5892d9132b0f64627/numpy-2.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18142b497d70a34b01642b9feabb70156311b326fdddd875a9981f34a369b671", size = 14042812 }, + { url = "https://files.pythonhosted.org/packages/df/54/13535f74391dbe5f479ceed96f1403267be302c840040700d4fd66688089/numpy-2.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7d41d1612c1a82b64697e894b75db6758d4f21c3ec069d841e60ebe54b5b571", size = 16093419 }, + { url = "https://files.pythonhosted.org/packages/dd/37/dfb2056842ac61315f225aa56f455da369f5223e4c5a38b91d20da1b628b/numpy-2.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a98f6f20465e7618c83252c02041517bd2f7ea29be5378f09667a8f654a5918d", size = 15238969 }, + { url = "https://files.pythonhosted.org/packages/5a/3d/d20d24ee313992f0b7e7b9d9eef642d9b545d39d5b91c4a2cc8c98776328/numpy-2.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e09d40edfdb4e260cb1567d8ae770ccf3b8b7e9f0d9b5c2a9992696b30ce2742", size = 17855705 }, + { url = "https://files.pythonhosted.org/packages/5b/40/944c9ee264f875a2db6f79380944fd2b5bb9d712bb4a134d11f45ad5b693/numpy-2.2.0-cp313-cp313-win32.whl", hash = "sha256:3905a5fffcc23e597ee4d9fb3fcd209bd658c352657548db7316e810ca80458e", size = 6270078 }, + { url = "https://files.pythonhosted.org/packages/30/04/e1ee6f8b22034302d4c5c24e15782bdedf76d90b90f3874ed0b48525def0/numpy-2.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a184288538e6ad699cbe6b24859206e38ce5fba28f3bcfa51c90d0502c1582b2", size = 12605791 }, + { url = "https://files.pythonhosted.org/packages/ef/fb/51d458625cd6134d60ac15180ae50995d7d21b0f2f92a6286ae7b0792d19/numpy-2.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7832f9e8eb00be32f15fdfb9a981d6955ea9adc8574c521d48710171b6c55e95", size = 20920160 }, + { url = "https://files.pythonhosted.org/packages/b4/34/162ae0c5d2536ea4be98c813b5161c980f0443cd5765fde16ddfe3450140/numpy-2.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0dd071b95bbca244f4cb7f70b77d2ff3aaaba7fa16dc41f58d14854a6204e6c", size = 14119064 }, + { url = "https://files.pythonhosted.org/packages/17/6c/4195dd0e1c41c55f466d516e17e9e28510f32af76d23061ea3da67438e3c/numpy-2.2.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0b227dcff8cdc3efbce66d4e50891f04d0a387cce282fe1e66199146a6a8fca", size = 5152778 }, + { url = "https://files.pythonhosted.org/packages/2f/47/ea804ae525832c8d05ed85b560dfd242d34e4bb0962bc269ccaa720fb934/numpy-2.2.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ab153263a7c5ccaf6dfe7e53447b74f77789f28ecb278c3b5d49db7ece10d6d", size = 6667605 }, + { url = "https://files.pythonhosted.org/packages/76/99/34d20e50b3d894bb16b5374bfbee399ab8ff3a33bf1e1f0b8acfe7bbd70d/numpy-2.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e500aba968a48e9019e42c0c199b7ec0696a97fa69037bea163b55398e390529", size = 14013275 }, + { url = "https://files.pythonhosted.org/packages/69/8f/a1df7bd02d434ab82539517d1b98028985700cfc4300bc5496fb140ca648/numpy-2.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:440cfb3db4c5029775803794f8638fbdbf71ec702caf32735f53b008e1eaece3", size = 16074900 }, + { url = "https://files.pythonhosted.org/packages/04/94/b419e7a76bf21a00fcb03c613583f10e389fdc8dfe420412ff5710c8ad3d/numpy-2.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a55dc7a7f0b6198b07ec0cd445fbb98b05234e8b00c5ac4874a63372ba98d4ab", size = 15219122 }, + { url = "https://files.pythonhosted.org/packages/65/d9/dddf398b2b6c5d750892a207a469c2854a8db0f033edaf72103af8cf05aa/numpy-2.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4bddbaa30d78c86329b26bd6aaaea06b1e47444da99eddac7bf1e2fab717bd72", size = 17851668 }, + { url = "https://files.pythonhosted.org/packages/d4/dc/09a4e5819a9782a213c0eb4eecacdc1cd75ad8dac99279b04cfccb7eeb0a/numpy-2.2.0-cp313-cp313t-win32.whl", hash = "sha256:30bf971c12e4365153afb31fc73f441d4da157153f3400b82db32d04de1e4066", size = 6325288 }, + { url = "https://files.pythonhosted.org/packages/ce/e1/e0d06ec34036c92b43aef206efe99a5f5f04e12c776eab82a36e00c40afc/numpy-2.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d35717333b39d1b6bb8433fa758a55f1081543de527171543a2b710551d40881", size = 12692303 }, + ] + + [[package]] + name = "nvidia-cublas-cu12" + version = "12.4.5.8" + source = { registry = "https://pypi.org/simple" } + wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/7f/7fbae15a3982dc9595e49ce0f19332423b260045d0a6afe93cdbe2f1f624/nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0f8aa1706812e00b9f19dfe0cdb3999b092ccb8ca168c0db5b8ea712456fd9b3", size = 363333771 }, + { url = "https://files.pythonhosted.org/packages/ae/71/1c91302526c45ab494c23f61c7a84aa568b8c1f9d196efa5993957faf906/nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2fc8da60df463fdefa81e323eef2e36489e1c94335b5358bcb38360adf75ac9b", size = 363438805 }, + { url = "https://files.pythonhosted.org/packages/e2/2a/4f27ca96232e8b5269074a72e03b4e0d43aa68c9b965058b1684d07c6ff8/nvidia_cublas_cu12-12.4.5.8-py3-none-win_amd64.whl", hash = "sha256:5a796786da89203a0657eda402bcdcec6180254a8ac22d72213abc42069522dc", size = 396895858 }, + ] + + [[package]] + name = "nvidia-cuda-cupti-cu12" + version = "12.4.127" + source = { registry = "https://pypi.org/simple" } + wheels = [ + { url = "https://files.pythonhosted.org/packages/93/b5/9fb3d00386d3361b03874246190dfec7b206fd74e6e287b26a8fcb359d95/nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:79279b35cf6f91da114182a5ce1864997fd52294a87a16179ce275773799458a", size = 12354556 }, + { url = "https://files.pythonhosted.org/packages/67/42/f4f60238e8194a3106d06a058d494b18e006c10bb2b915655bd9f6ea4cb1/nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9dec60f5ac126f7bb551c055072b69d85392b13311fcc1bcda2202d172df30fb", size = 13813957 }, + { url = "https://files.pythonhosted.org/packages/f3/79/8cf313ec17c58ccebc965568e5bcb265cdab0a1df99c4e674bb7a3b99bfe/nvidia_cuda_cupti_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:5688d203301ab051449a2b1cb6690fbe90d2b372f411521c86018b950f3d7922", size = 9938035 }, + ] + + [[package]] + name = "nvidia-cuda-nvrtc-cu12" + version = "12.4.127" + source = { registry = "https://pypi.org/simple" } + wheels = [ + { url = "https://files.pythonhosted.org/packages/77/aa/083b01c427e963ad0b314040565ea396f914349914c298556484f799e61b/nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0eedf14185e04b76aa05b1fea04133e59f465b6f960c0cbf4e37c3cb6b0ea198", size = 24133372 }, + { url = "https://files.pythonhosted.org/packages/2c/14/91ae57cd4db3f9ef7aa99f4019cfa8d54cb4caa7e00975df6467e9725a9f/nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a178759ebb095827bd30ef56598ec182b85547f1508941a3d560eb7ea1fbf338", size = 24640306 }, + { url = "https://files.pythonhosted.org/packages/7c/30/8c844bfb770f045bcd8b2c83455c5afb45983e1a8abf0c4e5297b481b6a5/nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:a961b2f1d5f17b14867c619ceb99ef6fcec12e46612711bcec78eb05068a60ec", size = 19751955 }, + ] + + [[package]] + name = "nvidia-cuda-runtime-cu12" + version = "12.4.127" + source = { registry = "https://pypi.org/simple" } + wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/aa/b656d755f474e2084971e9a297def515938d56b466ab39624012070cb773/nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:961fe0e2e716a2a1d967aab7caee97512f71767f852f67432d572e36cb3a11f3", size = 894177 }, + { url = "https://files.pythonhosted.org/packages/ea/27/1795d86fe88ef397885f2e580ac37628ed058a92ed2c39dc8eac3adf0619/nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:64403288fa2136ee8e467cdc9c9427e0434110899d07c779f25b5c068934faa5", size = 883737 }, + { url = "https://files.pythonhosted.org/packages/a8/8b/450e93fab75d85a69b50ea2d5fdd4ff44541e0138db16f9cd90123ef4de4/nvidia_cuda_runtime_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:09c2e35f48359752dfa822c09918211844a3d93c100a715d79b59591130c5e1e", size = 878808 }, + ] + + [[package]] + name = "nvidia-cudnn-cu12" + version = "9.1.0.70" + source = { registry = "https://pypi.org/simple" } + dependencies = [ + { name = "nvidia-cublas-cu12" }, + ] + wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f", size = 664752741 }, + { url = "https://files.pythonhosted.org/packages/3f/d0/f90ee6956a628f9f04bf467932c0a25e5a7e706a684b896593c06c82f460/nvidia_cudnn_cu12-9.1.0.70-py3-none-win_amd64.whl", hash = "sha256:6278562929433d68365a07a4a1546c237ba2849852c0d4b2262a486e805b977a", size = 679925892 }, + ] + + [[package]] + name = "nvidia-cufft-cu12" + version = "11.2.1.3" + source = { registry = "https://pypi.org/simple" } + dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, + ] + wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/8a/0e728f749baca3fbeffad762738276e5df60851958be7783af121a7221e7/nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5dad8008fc7f92f5ddfa2101430917ce2ffacd86824914c82e28990ad7f00399", size = 211422548 }, + { url = "https://files.pythonhosted.org/packages/27/94/3266821f65b92b3138631e9c8e7fe1fb513804ac934485a8d05776e1dd43/nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f083fc24912aa410be21fa16d157fed2055dab1cc4b6934a0e03cba69eb242b9", size = 211459117 }, + { url = "https://files.pythonhosted.org/packages/f6/ee/3f3f8e9874f0be5bbba8fb4b62b3de050156d159f8b6edc42d6f1074113b/nvidia_cufft_cu12-11.2.1.3-py3-none-win_amd64.whl", hash = "sha256:d802f4954291101186078ccbe22fc285a902136f974d369540fd4a5333d1440b", size = 210576476 }, + ] + + [[package]] + name = "nvidia-curand-cu12" + version = "10.3.5.147" + source = { registry = "https://pypi.org/simple" } + wheels = [ + { url = "https://files.pythonhosted.org/packages/80/9c/a79180e4d70995fdf030c6946991d0171555c6edf95c265c6b2bf7011112/nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1f173f09e3e3c76ab084aba0de819c49e56614feae5c12f69883f4ae9bb5fad9", size = 56314811 }, + { url = "https://files.pythonhosted.org/packages/8a/6d/44ad094874c6f1b9c654f8ed939590bdc408349f137f9b98a3a23ccec411/nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a88f583d4e0bb643c49743469964103aa59f7f708d862c3ddb0fc07f851e3b8b", size = 56305206 }, + { url = "https://files.pythonhosted.org/packages/1c/22/2573503d0d4e45673c263a313f79410e110eb562636b0617856fdb2ff5f6/nvidia_curand_cu12-10.3.5.147-py3-none-win_amd64.whl", hash = "sha256:f307cc191f96efe9e8f05a87096abc20d08845a841889ef78cb06924437f6771", size = 55799918 }, + ] + + [[package]] + name = "nvidia-cusolver-cu12" + version = "11.6.1.9" + source = { registry = "https://pypi.org/simple" } + dependencies = [ + { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cusparse-cu12" }, + { name = "nvidia-nvjitlink-cu12" }, + ] + wheels = [ + { url = "https://files.pythonhosted.org/packages/46/6b/a5c33cf16af09166845345275c34ad2190944bcc6026797a39f8e0a282e0/nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d338f155f174f90724bbde3758b7ac375a70ce8e706d70b018dd3375545fc84e", size = 127634111 }, + { url = "https://files.pythonhosted.org/packages/3a/e1/5b9089a4b2a4790dfdea8b3a006052cfecff58139d5a4e34cb1a51df8d6f/nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:19e33fa442bcfd085b3086c4ebf7e8debc07cfe01e11513cc6d332fd918ac260", size = 127936057 }, + { url = "https://files.pythonhosted.org/packages/f2/be/d435b7b020e854d5d5a682eb5de4328fd62f6182507406f2818280e206e2/nvidia_cusolver_cu12-11.6.1.9-py3-none-win_amd64.whl", hash = "sha256:e77314c9d7b694fcebc84f58989f3aa4fb4cb442f12ca1a9bde50f5e8f6d1b9c", size = 125224015 }, + ] + + [[package]] + name = "nvidia-cusparse-cu12" + version = "12.3.1.170" + source = { registry = "https://pypi.org/simple" } + dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, + ] + wheels = [ + { url = "https://files.pythonhosted.org/packages/96/a9/c0d2f83a53d40a4a41be14cea6a0bf9e668ffcf8b004bd65633f433050c0/nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9d32f62896231ebe0480efd8a7f702e143c98cfaa0e8a76df3386c1ba2b54df3", size = 207381987 }, + { url = "https://files.pythonhosted.org/packages/db/f7/97a9ea26ed4bbbfc2d470994b8b4f338ef663be97b8f677519ac195e113d/nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ea4f11a2904e2a8dc4b1833cc1b5181cde564edd0d5cd33e3c168eff2d1863f1", size = 207454763 }, + { url = "https://files.pythonhosted.org/packages/a2/e0/3155ca539760a8118ec94cc279b34293309bcd14011fc724f87f31988843/nvidia_cusparse_cu12-12.3.1.170-py3-none-win_amd64.whl", hash = "sha256:9bc90fb087bc7b4c15641521f31c0371e9a612fc2ba12c338d3ae032e6b6797f", size = 204684315 }, + ] + + [[package]] + name = "nvidia-nccl-cu12" + version = "2.21.5" + source = { registry = "https://pypi.org/simple" } + wheels = [ + { url = "https://files.pythonhosted.org/packages/df/99/12cd266d6233f47d00daf3a72739872bdc10267d0383508b0b9c84a18bb6/nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8579076d30a8c24988834445f8d633c697d42397e92ffc3f63fa26766d25e0a0", size = 188654414 }, + ] + + [[package]] + name = "nvidia-nvjitlink-cu12" + version = "12.4.127" + source = { registry = "https://pypi.org/simple" } + wheels = [ + { url = "https://files.pythonhosted.org/packages/02/45/239d52c05074898a80a900f49b1615d81c07fceadd5ad6c4f86a987c0bc4/nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4abe7fef64914ccfa909bc2ba39739670ecc9e820c83ccc7a6ed414122599b83", size = 20552510 }, + { url = "https://files.pythonhosted.org/packages/ff/ff/847841bacfbefc97a00036e0fce5a0f086b640756dc38caea5e1bb002655/nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57", size = 21066810 }, + { url = "https://files.pythonhosted.org/packages/81/19/0babc919031bee42620257b9a911c528f05fb2688520dcd9ca59159ffea8/nvidia_nvjitlink_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:fd9020c501d27d135f983c6d3e244b197a7ccad769e34df53a42e276b0e25fa1", size = 95336325 }, + ] + + [[package]] + name = "nvidia-nvtx-cu12" + version = "12.4.127" + source = { registry = "https://pypi.org/simple" } + wheels = [ + { url = "https://files.pythonhosted.org/packages/06/39/471f581edbb7804b39e8063d92fc8305bdc7a80ae5c07dbe6ea5c50d14a5/nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7959ad635db13edf4fc65c06a6e9f9e55fc2f92596db928d169c0bb031e88ef3", size = 100417 }, + { url = "https://files.pythonhosted.org/packages/87/20/199b8713428322a2f22b722c62b8cc278cc53dffa9705d744484b5035ee9/nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a", size = 99144 }, + { url = "https://files.pythonhosted.org/packages/54/1b/f77674fbb73af98843be25803bbd3b9a4f0a96c75b8d33a2854a5c7d2d77/nvidia_nvtx_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:641dccaaa1139f3ffb0d3164b4b84f9d253397e38246a4f2f36728b48566d485", size = 66307 }, + ] + + [[package]] + name = "pillow" + version = "11.0.0" + source = { registry = "https://pypi.org/simple" } + sdist = { url = "https://files.pythonhosted.org/packages/a5/26/0d95c04c868f6bdb0c447e3ee2de5564411845e36a858cfd63766bc7b563/pillow-11.0.0.tar.gz", hash = "sha256:72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739", size = 46737780 } + wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/a3/26e606ff0b2daaf120543e537311fa3ae2eb6bf061490e4fea51771540be/pillow-11.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2c0a187a92a1cb5ef2c8ed5412dd8d4334272617f532d4ad4de31e0495bd923", size = 3147642 }, + { url = "https://files.pythonhosted.org/packages/4f/d5/1caabedd8863526a6cfa44ee7a833bd97f945dc1d56824d6d76e11731939/pillow-11.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:084a07ef0821cfe4858fe86652fffac8e187b6ae677e9906e192aafcc1b69903", size = 2978999 }, + { url = "https://files.pythonhosted.org/packages/d9/ff/5a45000826a1aa1ac6874b3ec5a856474821a1b59d838c4f6ce2ee518fe9/pillow-11.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8069c5179902dcdce0be9bfc8235347fdbac249d23bd90514b7a47a72d9fecf4", size = 4196794 }, + { url = "https://files.pythonhosted.org/packages/9d/21/84c9f287d17180f26263b5f5c8fb201de0f88b1afddf8a2597a5c9fe787f/pillow-11.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f02541ef64077f22bf4924f225c0fd1248c168f86e4b7abdedd87d6ebaceab0f", size = 4300762 }, + { url = "https://files.pythonhosted.org/packages/84/39/63fb87cd07cc541438b448b1fed467c4d687ad18aa786a7f8e67b255d1aa/pillow-11.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:fcb4621042ac4b7865c179bb972ed0da0218a076dc1820ffc48b1d74c1e37fe9", size = 4210468 }, + { url = "https://files.pythonhosted.org/packages/7f/42/6e0f2c2d5c60f499aa29be14f860dd4539de322cd8fb84ee01553493fb4d/pillow-11.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:00177a63030d612148e659b55ba99527803288cea7c75fb05766ab7981a8c1b7", size = 4381824 }, + { url = "https://files.pythonhosted.org/packages/31/69/1ef0fb9d2f8d2d114db982b78ca4eeb9db9a29f7477821e160b8c1253f67/pillow-11.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8853a3bf12afddfdf15f57c4b02d7ded92c7a75a5d7331d19f4f9572a89c17e6", size = 4296436 }, + { url = "https://files.pythonhosted.org/packages/44/ea/dad2818c675c44f6012289a7c4f46068c548768bc6c7f4e8c4ae5bbbc811/pillow-11.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3107c66e43bda25359d5ef446f59c497de2b5ed4c7fdba0894f8d6cf3822dafc", size = 4429714 }, + { url = "https://files.pythonhosted.org/packages/af/3a/da80224a6eb15bba7a0dcb2346e2b686bb9bf98378c0b4353cd88e62b171/pillow-11.0.0-cp312-cp312-win32.whl", hash = "sha256:86510e3f5eca0ab87429dd77fafc04693195eec7fd6a137c389c3eeb4cfb77c6", size = 2249631 }, + { url = "https://files.pythonhosted.org/packages/57/97/73f756c338c1d86bb802ee88c3cab015ad7ce4b838f8a24f16b676b1ac7c/pillow-11.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:8ec4a89295cd6cd4d1058a5e6aec6bf51e0eaaf9714774e1bfac7cfc9051db47", size = 2567533 }, + { url = "https://files.pythonhosted.org/packages/0b/30/2b61876e2722374558b871dfbfcbe4e406626d63f4f6ed92e9c8e24cac37/pillow-11.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:27a7860107500d813fcd203b4ea19b04babe79448268403172782754870dac25", size = 2254890 }, + { url = "https://files.pythonhosted.org/packages/63/24/e2e15e392d00fcf4215907465d8ec2a2f23bcec1481a8ebe4ae760459995/pillow-11.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699", size = 3147300 }, + { url = "https://files.pythonhosted.org/packages/43/72/92ad4afaa2afc233dc44184adff289c2e77e8cd916b3ddb72ac69495bda3/pillow-11.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38", size = 2978742 }, + { url = "https://files.pythonhosted.org/packages/9e/da/c8d69c5bc85d72a8523fe862f05ababdc52c0a755cfe3d362656bb86552b/pillow-11.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2", size = 4194349 }, + { url = "https://files.pythonhosted.org/packages/cd/e8/686d0caeed6b998351d57796496a70185376ed9c8ec7d99e1d19ad591fc6/pillow-11.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2", size = 4298714 }, + { url = "https://files.pythonhosted.org/packages/ec/da/430015cec620d622f06854be67fd2f6721f52fc17fca8ac34b32e2d60739/pillow-11.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527", size = 4208514 }, + { url = "https://files.pythonhosted.org/packages/44/ae/7e4f6662a9b1cb5f92b9cc9cab8321c381ffbee309210940e57432a4063a/pillow-11.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa", size = 4380055 }, + { url = "https://files.pythonhosted.org/packages/74/d5/1a807779ac8a0eeed57f2b92a3c32ea1b696e6140c15bd42eaf908a261cd/pillow-11.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f", size = 4296751 }, + { url = "https://files.pythonhosted.org/packages/38/8c/5fa3385163ee7080bc13026d59656267daaaaf3c728c233d530e2c2757c8/pillow-11.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb", size = 4430378 }, + { url = "https://files.pythonhosted.org/packages/ca/1d/ad9c14811133977ff87035bf426875b93097fb50af747793f013979facdb/pillow-11.0.0-cp313-cp313-win32.whl", hash = "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798", size = 2249588 }, + { url = "https://files.pythonhosted.org/packages/fb/01/3755ba287dac715e6afdb333cb1f6d69740a7475220b4637b5ce3d78cec2/pillow-11.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de", size = 2567509 }, + { url = "https://files.pythonhosted.org/packages/c0/98/2c7d727079b6be1aba82d195767d35fcc2d32204c7a5820f822df5330152/pillow-11.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84", size = 2254791 }, + { url = "https://files.pythonhosted.org/packages/eb/38/998b04cc6f474e78b563716b20eecf42a2fa16a84589d23c8898e64b0ffd/pillow-11.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b", size = 3150854 }, + { url = "https://files.pythonhosted.org/packages/13/8e/be23a96292113c6cb26b2aa3c8b3681ec62b44ed5c2bd0b258bd59503d3c/pillow-11.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003", size = 2982369 }, + { url = "https://files.pythonhosted.org/packages/97/8a/3db4eaabb7a2ae8203cd3a332a005e4aba00067fc514aaaf3e9721be31f1/pillow-11.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2", size = 4333703 }, + { url = "https://files.pythonhosted.org/packages/28/ac/629ffc84ff67b9228fe87a97272ab125bbd4dc462745f35f192d37b822f1/pillow-11.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a", size = 4412550 }, + { url = "https://files.pythonhosted.org/packages/d6/07/a505921d36bb2df6868806eaf56ef58699c16c388e378b0dcdb6e5b2fb36/pillow-11.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8", size = 4461038 }, + { url = "https://files.pythonhosted.org/packages/d6/b9/fb620dd47fc7cc9678af8f8bd8c772034ca4977237049287e99dda360b66/pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8", size = 2253197 }, + { url = "https://files.pythonhosted.org/packages/df/86/25dde85c06c89d7fc5db17940f07aae0a56ac69aa9ccb5eb0f09798862a8/pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904", size = 2572169 }, + { url = "https://files.pythonhosted.org/packages/51/85/9c33f2517add612e17f3381aee7c4072779130c634921a756c97bc29fb49/pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3", size = 2256828 }, + ] + + [[package]] + name = "project" + version = "0.1.0" + source = { virtual = "." } + + [package.optional-dependencies] + cpu = [ + { name = "torch", version = "2.5.1", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" }, + { name = "torch", version = "2.5.1+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" }, + { name = "torchvision", version = "0.20.1", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" }, + { name = "torchvision", version = "0.20.1+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" }, + ] + cu124 = [ + { name = "torch", version = "2.5.1+cu124", source = { registry = "https://download.pytorch.org/whl/cu124" } }, + { name = "torchvision", version = "0.20.1+cu124", source = { registry = "https://download.pytorch.org/whl/cu124" } }, + ] + + [package.metadata] + requires-dist = [ + { name = "torch", marker = "extra == 'cpu'", specifier = ">=2.5.1", index = "https://download.pytorch.org/whl/cpu", conflict = { package = "project", extra = "cpu" } }, + { name = "torch", marker = "extra == 'cu124'", specifier = ">=2.5.1", index = "https://download.pytorch.org/whl/cu124", conflict = { package = "project", extra = "cu124" } }, + { name = "torchvision", marker = "extra == 'cpu'", specifier = ">=0.20.1", index = "https://download.pytorch.org/whl/cpu", conflict = { package = "project", extra = "cpu" } }, + { name = "torchvision", marker = "extra == 'cu124'", specifier = ">=0.20.1", index = "https://download.pytorch.org/whl/cu124", conflict = { package = "project", extra = "cu124" } }, + ] + + [[package]] + name = "setuptools" + version = "75.6.0" + source = { registry = "https://pypi.org/simple" } + sdist = { url = "https://files.pythonhosted.org/packages/43/54/292f26c208734e9a7f067aea4a7e282c080750c4546559b58e2e45413ca0/setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6", size = 1337429 } + wheels = [ + { url = "https://files.pythonhosted.org/packages/55/21/47d163f615df1d30c094f6c8bbb353619274edccf0327b185cc2493c2c33/setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d", size = 1224032 }, + ] + + [[package]] + name = "sympy" + version = "1.13.1" + source = { registry = "https://pypi.org/simple" } + dependencies = [ + { name = "mpmath" }, + ] + sdist = { url = "https://files.pythonhosted.org/packages/ca/99/5a5b6f19ff9f083671ddf7b9632028436167cd3d33e11015754e41b249a4/sympy-1.13.1.tar.gz", hash = "sha256:9cebf7e04ff162015ce31c9c6c9144daa34a93bd082f54fd8f12deca4f47515f", size = 7533040 } + wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/fe/81695a1aa331a842b582453b605175f419fe8540355886031328089d840a/sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8", size = 6189177 }, + ] + + [[package]] + name = "torch" + version = "2.5.1" + source = { registry = "https://download.pytorch.org/whl/cpu" } + resolution-markers = [ + "sys_platform == 'darwin'", + ] + dependencies = [ + { name = "filelock", marker = "sys_platform == 'darwin'" }, + { name = "fsspec", marker = "sys_platform == 'darwin'" }, + { name = "jinja2", marker = "sys_platform == 'darwin'" }, + { name = "networkx", marker = "sys_platform == 'darwin'" }, + { name = "setuptools", marker = "sys_platform == 'darwin'" }, + { name = "sympy", marker = "sys_platform == 'darwin'" }, + { name = "typing-extensions", marker = "sys_platform == 'darwin'" }, + ] + wheels = [ + { url = "https://download.pytorch.org/whl/cpu/torch-2.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36d1be99281b6f602d9639bd0af3ee0006e7aab16f6718d86f709d395b6f262c" }, + { url = "https://download.pytorch.org/whl/cpu/torch-2.5.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:8c712df61101964eb11910a846514011f0b6f5920c55dbf567bff8a34163d5b1" }, + ] + + [[package]] + name = "torch" + version = "2.5.1+cpu" + source = { registry = "https://download.pytorch.org/whl/cpu" } + resolution-markers = [ + "sys_platform != 'darwin'", + ] + dependencies = [ + { name = "filelock", marker = "sys_platform != 'darwin'" }, + { name = "fsspec", marker = "sys_platform != 'darwin'" }, + { name = "jinja2", marker = "sys_platform != 'darwin'" }, + { name = "networkx", marker = "sys_platform != 'darwin'" }, + { name = "setuptools", marker = "sys_platform != 'darwin'" }, + { name = "sympy", marker = "sys_platform != 'darwin'" }, + { name = "typing-extensions", marker = "sys_platform != 'darwin'" }, + ] + wheels = [ + { url = "https://download.pytorch.org/whl/cpu/torch-2.5.1%2Bcpu-cp312-cp312-linux_x86_64.whl", hash = "sha256:4856f9d6925121d13c2df07aa7580b767f449dfe71ae5acde9c27535d5da4840" }, + { url = "https://download.pytorch.org/whl/cpu/torch-2.5.1%2Bcpu-cp312-cp312-win_amd64.whl", hash = "sha256:a6b720410350765d3d77c01a5ce098a6c45af446284e45e87a98b8a16e7d564d" }, + { url = "https://download.pytorch.org/whl/cpu/torch-2.5.1%2Bcpu-cp313-cp313-linux_x86_64.whl", hash = "sha256:5dbbdf83caa90d0bcaa50e4933ca424889133b35226db79000877d4ec5d9ea37" }, + ] + + [[package]] + name = "torch" + version = "2.5.1+cu124" + source = { registry = "https://download.pytorch.org/whl/cu124" } + dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions" }, + ] + wheels = [ + { url = "https://download.pytorch.org/whl/cu124/torch-2.5.1%2Bcu124-cp312-cp312-linux_x86_64.whl", hash = "sha256:bf6484bfe5bc4f92a4a1a1bf553041505e19a911f717065330eb061afe0e14d7" }, + { url = "https://download.pytorch.org/whl/cu124/torch-2.5.1%2Bcu124-cp312-cp312-win_amd64.whl", hash = "sha256:3c3f705fb125edbd77f9579fa11a138c56af8968a10fc95834cdd9fdf4f1f1a6" }, + { url = "https://download.pytorch.org/whl/cu124/torch-2.5.1%2Bcu124-cp313-cp313-linux_x86_64.whl", hash = "sha256:e9bebf91ede89267577911da4b0709ac6113a0cff6a1c2202c046b1ec2a51601" }, + ] + + [[package]] + name = "torchvision" + version = "0.20.1" + source = { registry = "https://download.pytorch.org/whl/cpu" } + resolution-markers = [ + "sys_platform == 'darwin'", + ] + dependencies = [ + { name = "numpy", marker = "sys_platform == 'darwin'" }, + { name = "pillow", marker = "sys_platform == 'darwin'" }, + { name = "torch", version = "2.5.1", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" }, + ] + wheels = [ + { url = "https://download.pytorch.org/whl/cpu/torchvision-0.20.1-cp312-cp312-linux_aarch64.whl", hash = "sha256:9f853ba4497ac4691815ad41b523ee23cf5ba4f87b1ce869d704052e233ca8b7" }, + { url = "https://download.pytorch.org/whl/cpu/torchvision-0.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1a31256ff945d64f006bb306813a7c95a531fe16bfb2535c837dd4c104533d7a" }, + ] + + [[package]] + name = "torchvision" + version = "0.20.1+cpu" + source = { registry = "https://download.pytorch.org/whl/cpu" } + resolution-markers = [ + "sys_platform != 'darwin'", + ] + dependencies = [ + { name = "numpy", marker = "sys_platform != 'darwin'" }, + { name = "pillow", marker = "sys_platform != 'darwin'" }, + { name = "torch", version = "2.5.1+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" }, + ] + wheels = [ + { url = "https://download.pytorch.org/whl/cpu/torchvision-0.20.1%2Bcpu-cp312-cp312-linux_x86_64.whl", hash = "sha256:5f46c7ac7f00a065cb40bfb1e1bfc4ba16a35f5d46b3fe70cca6b3cea7f822f7" }, + { url = "https://download.pytorch.org/whl/cpu/torchvision-0.20.1%2Bcpu-cp312-cp312-win_amd64.whl", hash = "sha256:64832095b9b8b8185f24788f1a531ae96c937bf797cd3474a2ea7511852e5cd7" }, + ] + + [[package]] + name = "torchvision" + version = "0.20.1+cu124" + source = { registry = "https://download.pytorch.org/whl/cu124" } + dependencies = [ + { name = "numpy" }, + { name = "pillow" }, + { name = "torch", version = "2.5.1+cu124", source = { registry = "https://download.pytorch.org/whl/cu124" } }, + ] + wheels = [ + { url = "https://download.pytorch.org/whl/cu124/torchvision-0.20.1%2Bcu124-cp312-cp312-linux_x86_64.whl", hash = "sha256:d1053ec5054549e7dac2613b151bffe323f3c924939d296df4d7d34925aaf3ad" }, + { url = "https://download.pytorch.org/whl/cu124/torchvision-0.20.1%2Bcu124-cp312-cp312-win_amd64.whl", hash = "sha256:0f6c7b3b0e13663fb3359e64f3604c0ab74c2b4809ae6949ace5635a5240f0e5" }, + ] + + [[package]] + name = "triton" + version = "3.1.0" + source = { registry = "https://pypi.org/simple" } + dependencies = [ + { name = "filelock" }, + ] + wheels = [ + { url = "https://files.pythonhosted.org/packages/78/eb/65f5ba83c2a123f6498a3097746607e5b2f16add29e36765305e4ac7fdd8/triton-3.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8182f42fd8080a7d39d666814fa36c5e30cc00ea7eeeb1a2983dbb4c99a0fdc", size = 209551444 }, + ] + + [[package]] + name = "typing-extensions" + version = "4.12.2" + source = { registry = "https://pypi.org/simple" } + sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } + wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, + ] + "### + ); + }); + + Ok(()) +} diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs index 4c203ce53..7b4739882 100644 --- a/crates/uv/tests/it/pip_compile.rs +++ b/crates/uv/tests/it/pip_compile.rs @@ -7459,6 +7459,56 @@ fn universal_multi_version() -> Result<()> { Ok(()) } +#[test] +fn universal_platform_fork() -> Result<()> { + let context = TestContext::new("3.12"); + let requirements_in = context.temp_dir.child("requirements.in"); + requirements_in.write_str(indoc::indoc! {r" + --index-url https://download.pytorch.org/whl/cpu + + torch==2.5.1 + "})?; + + uv_snapshot!(context.filters(), windows_filters=false, context.pip_compile() + .arg("requirements.in") + .arg("--universal") + .env_remove(EnvVars::UV_EXCLUDE_NEWER), @r###" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv pip compile --cache-dir [CACHE_DIR] requirements.in --universal + filelock==3.13.1 + # via torch + fsspec==2024.2.0 + # via torch + jinja2==3.1.3 + # via torch + markupsafe==3.0.2 + # via jinja2 + mpmath==1.3.0 + # via sympy + networkx==3.2.1 + # via torch + setuptools==70.0.0 + # via torch + sympy==1.13.1 + # via torch + torch==2.5.1 ; sys_platform == 'darwin' + # via -r requirements.in + torch==2.5.1+cpu ; sys_platform != 'darwin' + # via -r requirements.in + typing-extensions==4.9.0 + # via torch + + ----- stderr ----- + Resolved 11 packages in [TIME] + "### + ); + + Ok(()) +} + // Requested distinct local versions with disjoint markers. #[test] fn universal_disjoint_locals() -> Result<()> { @@ -7579,7 +7629,9 @@ fn universal_transitive_disjoint_locals() -> Result<()> { # -r requirements.in # torchvision # triton - torchvision==0.15.1+rocm5.4.2 + torchvision==0.15.1 ; sys_platform == 'darwin' or sys_platform == 'win32' + # via -r requirements.in + torchvision==0.15.1+rocm5.4.2 ; sys_platform != 'darwin' and sys_platform != 'win32' # via -r requirements.in triton==2.0.0 ; platform_machine == 'x86_64' and sys_platform == 'linux' # via torch @@ -7589,7 +7641,7 @@ fn universal_transitive_disjoint_locals() -> Result<()> { # via requests ----- stderr ----- - Resolved 20 packages in [TIME] + Resolved 21 packages in [TIME] "### ); @@ -7872,7 +7924,11 @@ fn universal_disjoint_base_or_local_requirement() -> Result<()> { # via torch sympy==1.12 # via torch - torch==2.0.0+cpu ; python_full_version < '3.11' or python_full_version >= '3.13' + torch==2.0.0 ; python_full_version < '3.11' and sys_platform == 'darwin' + # via + # -r requirements.in + # example + torch==2.0.0+cpu ; python_full_version >= '3.13' or (python_full_version < '3.11' and sys_platform != 'darwin') # via # -r requirements.in # example @@ -7887,7 +7943,7 @@ fn universal_disjoint_base_or_local_requirement() -> Result<()> { # via torch ----- stderr ----- - Resolved 13 packages in [TIME] + Resolved 14 packages in [TIME] "### ); @@ -7954,7 +8010,7 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> { # via sympy networkx==3.2.1 # via torch - pytorch-triton-rocm==2.3.0 ; platform_machine != 'x86_64' + pytorch-triton-rocm==2.3.0 ; platform_machine != 'x86_64' and sys_platform != 'darwin' and sys_platform != 'win32' # via torch sympy==1.12 # via torch @@ -7965,7 +8021,9 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> { # -r requirements.in # example # triton - torch==2.3.0+rocm6.0 ; platform_machine != 'x86_64' + torch==2.3.0 ; (platform_machine != 'x86_64' and sys_platform == 'darwin') or (platform_machine != 'x86_64' and sys_platform == 'win32') + # via -r requirements.in + torch==2.3.0+rocm6.0 ; platform_machine != 'x86_64' and sys_platform != 'darwin' and sys_platform != 'win32' # via -r requirements.in triton==2.0.0 ; platform_machine == 'x86_64' and sys_platform == 'linux' # via torch @@ -7973,7 +8031,7 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> { # via torch ----- stderr ----- - Resolved 18 packages in [TIME] + Resolved 19 packages in [TIME] "### ); @@ -8031,7 +8089,7 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> { # via sympy networkx==3.2.1 # via torch - pytorch-triton-rocm==2.3.0 ; platform_machine != 'x86_64' + pytorch-triton-rocm==2.3.0 ; platform_machine != 'x86_64' and sys_platform != 'darwin' and sys_platform != 'win32' # via torch sympy==1.12 # via torch @@ -8042,7 +8100,9 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> { # -r requirements.in # example # triton - torch==2.3.0+rocm6.0 ; platform_machine != 'x86_64' + torch==2.3.0 ; (platform_machine != 'x86_64' and sys_platform == 'darwin') or (platform_machine != 'x86_64' and sys_platform == 'win32') + # via -r requirements.in + torch==2.3.0+rocm6.0 ; platform_machine != 'x86_64' and sys_platform != 'darwin' and sys_platform != 'win32' # via -r requirements.in triton==2.0.0 ; platform_machine == 'x86_64' and sys_platform == 'linux' # via torch @@ -8050,7 +8110,7 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> { # via torch ----- stderr ----- - Resolved 18 packages in [TIME] + Resolved 19 packages in [TIME] "### ); @@ -8119,7 +8179,7 @@ fn universal_nested_disjoint_local_requirement() -> Result<()> { # via sympy networkx==3.2.1 # via torch - pytorch-triton-rocm==2.3.0 ; os_name != 'Linux' + pytorch-triton-rocm==2.3.0 ; os_name != 'Linux' and sys_platform != 'darwin' and sys_platform != 'win32' # via torch sympy==1.12 # via torch @@ -8134,7 +8194,9 @@ fn universal_nested_disjoint_local_requirement() -> Result<()> { # -r requirements.in # example # triton - torch==2.3.0+rocm6.0 ; os_name != 'Linux' + torch==2.3.0 ; (os_name != 'Linux' and sys_platform == 'darwin') or (os_name != 'Linux' and sys_platform == 'win32') + # via -r requirements.in + torch==2.3.0+rocm6.0 ; os_name != 'Linux' and sys_platform != 'darwin' and sys_platform != 'win32' # via -r requirements.in triton==2.0.0 ; os_name == 'Linux' and platform_machine == 'x86_64' and sys_platform == 'linux' # via torch @@ -8142,7 +8204,7 @@ fn universal_nested_disjoint_local_requirement() -> Result<()> { # via torch ----- stderr ----- - Resolved 19 packages in [TIME] + Resolved 20 packages in [TIME] "### ); @@ -8903,7 +8965,7 @@ fn universal_marker_propagation() -> Result<()> { # via requests charset-normalizer==3.3.2 # via requests - cmake==3.28.4 ; platform_machine == 'x86_64' + cmake==3.28.4 ; platform_machine == 'x86_64' and sys_platform != 'darwin' and sys_platform != 'win32' # via pytorch-triton-rocm filelock==3.13.1 # via @@ -8915,7 +8977,7 @@ fn universal_marker_propagation() -> Result<()> { # via requests jinja2==3.1.3 # via torch - lit==18.1.2 ; platform_machine == 'x86_64' + lit==18.1.2 ; platform_machine == 'x86_64' and sys_platform != 'darwin' and sys_platform != 'win32' # via pytorch-triton-rocm markupsafe==2.1.5 # via jinja2 @@ -8929,26 +8991,38 @@ fn universal_marker_propagation() -> Result<()> { # via torchvision pillow==10.2.0 # via torchvision - pytorch-triton-rocm==2.0.2 ; platform_machine == 'x86_64' + pytorch-triton-rocm==2.0.2 ; platform_machine == 'x86_64' and sys_platform != 'darwin' and sys_platform != 'win32' # via torch - pytorch-triton-rocm==2.2.0 ; platform_machine != 'x86_64' + pytorch-triton-rocm==2.2.0 ; platform_machine != 'x86_64' and sys_platform != 'darwin' and sys_platform != 'win32' # via torch requests==2.31.0 # via torchvision sympy==1.12 # via torch - torch==2.0.0+rocm5.4.2 ; platform_machine == 'x86_64' + torch==2.0.0 ; (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'win32') + # via + # -r requirements.in + # torchvision + torch==2.0.0+rocm5.4.2 ; platform_machine == 'x86_64' and sys_platform != 'darwin' and sys_platform != 'win32' # via # -r requirements.in # pytorch-triton-rocm # torchvision - torch==2.2.0+rocm5.7 ; platform_machine != 'x86_64' + torch==2.2.0 ; (platform_machine != 'x86_64' and sys_platform == 'darwin') or (platform_machine != 'x86_64' and sys_platform == 'win32') # via # -r requirements.in # torchvision - torchvision==0.15.1+rocm5.4.2 ; platform_machine == 'x86_64' + torch==2.2.0+rocm5.7 ; platform_machine != 'x86_64' and sys_platform != 'darwin' and sys_platform != 'win32' + # via + # -r requirements.in + # torchvision + torchvision==0.15.1 ; (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'win32') # via -r requirements.in - torchvision==0.17.0+rocm5.7 ; platform_machine != 'x86_64' + torchvision==0.15.1+rocm5.4.2 ; platform_machine == 'x86_64' and sys_platform != 'darwin' and sys_platform != 'win32' + # via -r requirements.in + torchvision==0.17.0 ; (platform_machine != 'x86_64' and sys_platform == 'darwin') or (platform_machine != 'x86_64' and sys_platform == 'win32') + # via -r requirements.in + torchvision==0.17.0+rocm5.7 ; platform_machine != 'x86_64' and sys_platform != 'darwin' and sys_platform != 'win32' # via -r requirements.in typing-extensions==4.10.0 # via torch @@ -8957,7 +9031,7 @@ fn universal_marker_propagation() -> Result<()> { ----- stderr ----- warning: The requested Python version 3.8 is not available; 3.12.[X] will be used to build dependencies instead. - Resolved 24 packages in [TIME] + Resolved 28 packages in [TIME] "### ); diff --git a/crates/uv/tests/it/snapshots/it__ecosystem__packse-lock-file.snap b/crates/uv/tests/it/snapshots/it__ecosystem__packse-lock-file.snap index 70e94d40c..792152554 100644 --- a/crates/uv/tests/it/snapshots/it__ecosystem__packse-lock-file.snap +++ b/crates/uv/tests/it/snapshots/it__ecosystem__packse-lock-file.snap @@ -1,5 +1,5 @@ --- -source: crates/uv/tests/ecosystem.rs +source: crates/uv/tests/it/ecosystem.rs expression: lock --- version = 1 diff --git a/crates/uv/tests/it/snapshots/it__ecosystem__warehouse-uv-lock-output.snap b/crates/uv/tests/it/snapshots/it__ecosystem__warehouse-uv-lock-output.snap index 508bbbfa4..14d0e1f0a 100644 --- a/crates/uv/tests/it/snapshots/it__ecosystem__warehouse-uv-lock-output.snap +++ b/crates/uv/tests/it/snapshots/it__ecosystem__warehouse-uv-lock-output.snap @@ -1,5 +1,5 @@ --- -source: crates/uv/tests/ecosystem.rs +source: crates/uv/tests/it/ecosystem.rs expression: snapshot --- success: true diff --git a/docs/guides/integration/pytorch.md b/docs/guides/integration/pytorch.md index 89eb51385..8b71d8567 100644 --- a/docs/guides/integration/pytorch.md +++ b/docs/guides/integration/pytorch.md @@ -114,16 +114,13 @@ Next, update the `pyproject.toml` to point `torch` and `torchvision` to the desi === "CPU-only" - PyTorch doesn't publish CPU-only builds for macOS, since macOS builds are always considered CPU-only. - As such, we gate on `platform_system` to instruct uv to ignore the PyTorch index when resolving for macOS. - ```toml [tool.uv.sources] torch = [ - { index = "pytorch-cpu", marker = "platform_system != 'Darwin'"}, + { index = "pytorch-cpu" }, ] torchvision = [ - { index = "pytorch-cpu", marker = "platform_system != 'Darwin'"}, + { index = "pytorch-cpu" }, ] ``` @@ -201,10 +198,10 @@ dependencies = [ [tool.uv.sources] torch = [ - { index = "pytorch-cpu", marker = "platform_system != 'Darwin'" }, + { index = "pytorch-cpu" }, ] torchvision = [ - { index = "pytorch-cpu", marker = "platform_system != 'Darwin'" }, + { index = "pytorch-cpu" }, ] [[tool.uv.index]] @@ -290,11 +287,11 @@ conflicts = [ [tool.uv.sources] torch = [ - { index = "pytorch-cpu", extra = "cpu", marker = "platform_system != 'Darwin'" }, + { index = "pytorch-cpu", extra = "cpu" }, { index = "pytorch-cu124", extra = "cu124" }, ] torchvision = [ - { index = "pytorch-cpu", extra = "cpu", marker = "platform_system != 'Darwin'" }, + { index = "pytorch-cpu", extra = "cpu" }, { index = "pytorch-cu124", extra = "cu124" }, ] @@ -311,9 +308,8 @@ explicit = true !!! note - Since GPU-accelerated builds aren't available on macOS, the above configuration will continue to use - the CPU-only builds on macOS via the `"platform_system != 'Darwin'"` marker, regardless of the extra - provided. + Since GPU-accelerated builds aren't available on macOS, the above configuration will fail to install + on macOS when the `cu124` extra is enabled. ## The `uv pip` interface