mirror of
				https://github.com/astral-sh/uv.git
				synced 2025-10-31 12:06:13 +00:00 
			
		
		
		
	Add --find-links source distributions to the registry cache (#2986)
				
					
				
			## Summary Source distributions in `--find-links` are now properly picked up in the cache. Closes https://github.com/astral-sh/uv/issues/2978.
This commit is contained in:
		
							parent
							
								
									32f129c245
								
							
						
					
					
						commit
						3dd673677a
					
				
					 7 changed files with 182 additions and 71 deletions
				
			
		|  | @ -24,6 +24,7 @@ static DEFAULT_INDEX_URL: Lazy<IndexUrl> = | ||||||
| pub enum IndexUrl { | pub enum IndexUrl { | ||||||
|     Pypi(VerbatimUrl), |     Pypi(VerbatimUrl), | ||||||
|     Url(VerbatimUrl), |     Url(VerbatimUrl), | ||||||
|  |     Path(VerbatimUrl), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl IndexUrl { | impl IndexUrl { | ||||||
|  | @ -32,6 +33,7 @@ impl IndexUrl { | ||||||
|         match self { |         match self { | ||||||
|             Self::Pypi(url) => url.raw(), |             Self::Pypi(url) => url.raw(), | ||||||
|             Self::Url(url) => url.raw(), |             Self::Url(url) => url.raw(), | ||||||
|  |             Self::Path(url) => url.raw(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -41,6 +43,7 @@ impl Display for IndexUrl { | ||||||
|         match self { |         match self { | ||||||
|             Self::Pypi(url) => Display::fmt(url, f), |             Self::Pypi(url) => Display::fmt(url, f), | ||||||
|             Self::Url(url) => Display::fmt(url, f), |             Self::Url(url) => Display::fmt(url, f), | ||||||
|  |             Self::Path(url) => Display::fmt(url, f), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -50,6 +53,7 @@ impl Verbatim for IndexUrl { | ||||||
|         match self { |         match self { | ||||||
|             Self::Pypi(url) => url.verbatim(), |             Self::Pypi(url) => url.verbatim(), | ||||||
|             Self::Url(url) => url.verbatim(), |             Self::Url(url) => url.verbatim(), | ||||||
|  |             Self::Path(url) => url.verbatim(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -83,6 +87,7 @@ impl From<IndexUrl> for Url { | ||||||
|         match index { |         match index { | ||||||
|             IndexUrl::Pypi(url) => url.to_url(), |             IndexUrl::Pypi(url) => url.to_url(), | ||||||
|             IndexUrl::Url(url) => url.to_url(), |             IndexUrl::Url(url) => url.to_url(), | ||||||
|  |             IndexUrl::Path(url) => url.to_url(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -94,6 +99,7 @@ impl Deref for IndexUrl { | ||||||
|         match &self { |         match &self { | ||||||
|             Self::Pypi(url) => url, |             Self::Pypi(url) => url, | ||||||
|             Self::Url(url) => url, |             Self::Url(url) => url, | ||||||
|  |             Self::Path(url) => url, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -205,7 +205,7 @@ impl<'a> FlatIndexClient<'a> { | ||||||
|     fn read_from_directory(path: &PathBuf) -> Result<FlatIndexEntries, std::io::Error> { |     fn read_from_directory(path: &PathBuf) -> Result<FlatIndexEntries, std::io::Error> { | ||||||
|         // Absolute paths are required for the URL conversion.
 |         // Absolute paths are required for the URL conversion.
 | ||||||
|         let path = fs_err::canonicalize(path)?; |         let path = fs_err::canonicalize(path)?; | ||||||
|         let index_url = IndexUrl::Url(VerbatimUrl::from_path(&path)); |         let index_url = IndexUrl::Path(VerbatimUrl::from_path(&path)); | ||||||
| 
 | 
 | ||||||
|         let mut dists = Vec::new(); |         let mut dists = Vec::new(); | ||||||
|         for entry in fs_err::read_dir(path)? { |         for entry in fs_err::read_dir(path)? { | ||||||
|  |  | ||||||
|  | @ -285,6 +285,7 @@ impl RegistryClient { | ||||||
|             Path::new(&match index { |             Path::new(&match index { | ||||||
|                 IndexUrl::Pypi(_) => "pypi".to_string(), |                 IndexUrl::Pypi(_) => "pypi".to_string(), | ||||||
|                 IndexUrl::Url(url) => cache_key::digest(&cache_key::CanonicalUrl::new(url)), |                 IndexUrl::Url(url) => cache_key::digest(&cache_key::CanonicalUrl::new(url)), | ||||||
|  |                 IndexUrl::Path(url) => cache_key::digest(&cache_key::CanonicalUrl::new(url)), | ||||||
|             }), |             }), | ||||||
|             format!("{package_name}.rkyv"), |             format!("{package_name}.rkyv"), | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|  | @ -20,9 +20,14 @@ pub struct CachedWheel { | ||||||
| 
 | 
 | ||||||
| impl CachedWheel { | impl CachedWheel { | ||||||
|     /// Try to parse a distribution from a cached directory name (like `typing-extensions-4.8.0-py3-none-any`).
 |     /// Try to parse a distribution from a cached directory name (like `typing-extensions-4.8.0-py3-none-any`).
 | ||||||
|     pub fn from_built_source(path: &Path) -> Option<Self> { |     pub fn from_built_source(path: impl AsRef<Path>) -> Option<Self> { | ||||||
|  |         let path = path.as_ref(); | ||||||
|  | 
 | ||||||
|  |         // Determine the wheel filename.
 | ||||||
|         let filename = path.file_name()?.to_str()?; |         let filename = path.file_name()?.to_str()?; | ||||||
|         let filename = WheelFilename::from_stem(filename).ok()?; |         let filename = WheelFilename::from_stem(filename).ok()?; | ||||||
|  | 
 | ||||||
|  |         // Convert to a cached wheel.
 | ||||||
|         let archive = path.canonicalize().ok()?; |         let archive = path.canonicalize().ok()?; | ||||||
|         let entry = CacheEntry::from_path(archive); |         let entry = CacheEntry::from_path(archive); | ||||||
|         let hashes = Vec::new(); |         let hashes = Vec::new(); | ||||||
|  | @ -54,7 +59,9 @@ impl CachedWheel { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Read a cached wheel from a `.http` pointer (e.g., `anyio-4.0.0-py3-none-any.http`).
 |     /// Read a cached wheel from a `.http` pointer (e.g., `anyio-4.0.0-py3-none-any.http`).
 | ||||||
|     pub fn from_http_pointer(path: &Path, cache: &Cache) -> Option<Self> { |     pub fn from_http_pointer(path: impl AsRef<Path>, cache: &Cache) -> Option<Self> { | ||||||
|  |         let path = path.as_ref(); | ||||||
|  | 
 | ||||||
|         // Determine the wheel filename.
 |         // Determine the wheel filename.
 | ||||||
|         let filename = path.file_name()?.to_str()?; |         let filename = path.file_name()?.to_str()?; | ||||||
|         let filename = WheelFilename::from_stem(filename).ok()?; |         let filename = WheelFilename::from_stem(filename).ok()?; | ||||||
|  | @ -73,7 +80,9 @@ impl CachedWheel { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Read a cached wheel from a `.rev` pointer (e.g., `anyio-4.0.0-py3-none-any.rev`).
 |     /// Read a cached wheel from a `.rev` pointer (e.g., `anyio-4.0.0-py3-none-any.rev`).
 | ||||||
|     pub fn from_local_pointer(path: &Path, cache: &Cache) -> Option<Self> { |     pub fn from_local_pointer(path: impl AsRef<Path>, cache: &Cache) -> Option<Self> { | ||||||
|  |         let path = path.as_ref(); | ||||||
|  | 
 | ||||||
|         // Determine the wheel filename.
 |         // Determine the wheel filename.
 | ||||||
|         let filename = path.file_name()?.to_str()?; |         let filename = path.file_name()?.to_str()?; | ||||||
|         let filename = WheelFilename::from_stem(filename).ok()?; |         let filename = WheelFilename::from_stem(filename).ok()?; | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ use uv_normalize::PackageName; | ||||||
| use uv_types::HashStrategy; | use uv_types::HashStrategy; | ||||||
| 
 | 
 | ||||||
| use crate::index::cached_wheel::CachedWheel; | use crate::index::cached_wheel::CachedWheel; | ||||||
| use crate::source::{HttpRevisionPointer, HTTP_REVISION}; | use crate::source::{HttpRevisionPointer, LocalRevisionPointer, HTTP_REVISION, LOCAL_REVISION}; | ||||||
| 
 | 
 | ||||||
| /// A local index of distributions that originate from a registry, like `PyPI`.
 | /// A local index of distributions that originate from a registry, like `PyPI`.
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
|  | @ -88,13 +88,13 @@ impl<'a> RegistryWheelIndex<'a> { | ||||||
|     ) -> BTreeMap<Version, CachedRegistryDist> { |     ) -> BTreeMap<Version, CachedRegistryDist> { | ||||||
|         let mut versions = BTreeMap::new(); |         let mut versions = BTreeMap::new(); | ||||||
| 
 | 
 | ||||||
|         // Collect into owned `IndexUrl`
 |         // Collect into owned `IndexUrl`.
 | ||||||
|         let flat_index_urls: Vec<IndexUrl> = index_locations |         let flat_index_urls: Vec<IndexUrl> = index_locations | ||||||
|             .flat_index() |             .flat_index() | ||||||
|             .filter_map(|flat_index| match flat_index { |             .filter_map(|flat_index| match flat_index { | ||||||
|                 FlatIndexLocation::Path(path) => { |                 FlatIndexLocation::Path(path) => { | ||||||
|                     let path = fs_err::canonicalize(path).ok()?; |                     let path = fs_err::canonicalize(path).ok()?; | ||||||
|                     Some(IndexUrl::Url(VerbatimUrl::from_path(path))) |                     Some(IndexUrl::Path(VerbatimUrl::from_path(path))) | ||||||
|                 } |                 } | ||||||
|                 FlatIndexLocation::Url(url) => { |                 FlatIndexLocation::Url(url) => { | ||||||
|                     Some(IndexUrl::Url(VerbatimUrl::unknown(url.clone()))) |                     Some(IndexUrl::Url(VerbatimUrl::unknown(url.clone()))) | ||||||
|  | @ -112,30 +112,37 @@ impl<'a> RegistryWheelIndex<'a> { | ||||||
|             // For registry wheels, the cache structure is: `<index>/<package-name>/<wheel>.http`
 |             // For registry wheels, the cache structure is: `<index>/<package-name>/<wheel>.http`
 | ||||||
|             // or `<index>/<package-name>/<version>/<wheel>.rev`.
 |             // or `<index>/<package-name>/<version>/<wheel>.rev`.
 | ||||||
|             for file in files(&wheel_dir) { |             for file in files(&wheel_dir) { | ||||||
|                 if file |                 match index_url { | ||||||
|                     .extension() |                     // Add files from remote registries.
 | ||||||
|                     .is_some_and(|ext| ext.eq_ignore_ascii_case("http")) |                     IndexUrl::Pypi(_) | IndexUrl::Url(_) => { | ||||||
|                 { |                         if file | ||||||
|                     if let Some(wheel) = |                             .extension() | ||||||
|                         CachedWheel::from_http_pointer(&wheel_dir.join(&file), cache) |                             .is_some_and(|ext| ext.eq_ignore_ascii_case("http")) | ||||||
|                     { |                         { | ||||||
|                         // Enforce hash-checking based on the built distribution.
 |                             if let Some(wheel) = | ||||||
|                         if wheel.satisfies(hasher.get(package)) { |                                 CachedWheel::from_http_pointer(wheel_dir.join(file), cache) | ||||||
|                             Self::add_wheel(wheel, tags, &mut versions); |                             { | ||||||
|  |                                 // Enforce hash-checking based on the built distribution.
 | ||||||
|  |                                 if wheel.satisfies(hasher.get(package)) { | ||||||
|  |                                     Self::add_wheel(wheel, tags, &mut versions); | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                     // Add files from local registries (e.g., `--find-links`).
 | ||||||
| 
 |                     IndexUrl::Path(_) => { | ||||||
|                 if file |                         if file | ||||||
|                     .extension() |                             .extension() | ||||||
|                     .is_some_and(|ext| ext.eq_ignore_ascii_case("rev")) |                             .is_some_and(|ext| ext.eq_ignore_ascii_case("rev")) | ||||||
|                 { |                         { | ||||||
|                     if let Some(wheel) = |                             if let Some(wheel) = | ||||||
|                         CachedWheel::from_local_pointer(&wheel_dir.join(&file), cache) |                                 CachedWheel::from_local_pointer(wheel_dir.join(file), cache) | ||||||
|                     { |                             { | ||||||
|                         // Enforce hash-checking based on the built distribution.
 |                                 // Enforce hash-checking based on the built distribution.
 | ||||||
|                         if wheel.satisfies(hasher.get(package)) { |                                 if wheel.satisfies(hasher.get(package)) { | ||||||
|                             Self::add_wheel(wheel, tags, &mut versions); |                                     Self::add_wheel(wheel, tags, &mut versions); | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  | @ -152,18 +159,39 @@ impl<'a> RegistryWheelIndex<'a> { | ||||||
|             for shard in directories(&cache_shard) { |             for shard in directories(&cache_shard) { | ||||||
|                 // Read the existing metadata from the cache, if it exists.
 |                 // Read the existing metadata from the cache, if it exists.
 | ||||||
|                 let cache_shard = cache_shard.shard(shard); |                 let cache_shard = cache_shard.shard(shard); | ||||||
|                 let revision_entry = cache_shard.entry(HTTP_REVISION); | 
 | ||||||
|                 if let Ok(Some(pointer)) = HttpRevisionPointer::read_from(&revision_entry) { |                 // Read the revision from the cache.
 | ||||||
|  |                 let revision = match index_url { | ||||||
|  |                     // Add files from remote registries.
 | ||||||
|  |                     IndexUrl::Pypi(_) | IndexUrl::Url(_) => { | ||||||
|  |                         let revision_entry = cache_shard.entry(HTTP_REVISION); | ||||||
|  |                         if let Ok(Some(pointer)) = HttpRevisionPointer::read_from(revision_entry) { | ||||||
|  |                             Some(pointer.into_revision()) | ||||||
|  |                         } else { | ||||||
|  |                             None | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     // Add files from local registries (e.g., `--find-links`).
 | ||||||
|  |                     IndexUrl::Path(_) => { | ||||||
|  |                         let revision_entry = cache_shard.entry(LOCAL_REVISION); | ||||||
|  |                         if let Ok(Some(pointer)) = LocalRevisionPointer::read_from(revision_entry) { | ||||||
|  |                             Some(pointer.into_revision()) | ||||||
|  |                         } else { | ||||||
|  |                             None | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |                 if let Some(revision) = revision { | ||||||
|                     // Enforce hash-checking based on the source distribution.
 |                     // Enforce hash-checking based on the source distribution.
 | ||||||
|                     let revision = pointer.into_revision(); |  | ||||||
|                     if revision.satisfies(hasher.get(package)) { |                     if revision.satisfies(hasher.get(package)) { | ||||||
|                         for wheel_dir in symlinks(cache_shard.join(revision.id())) { |                         for wheel_dir in symlinks(cache_shard.join(revision.id())) { | ||||||
|                             if let Some(wheel) = CachedWheel::from_built_source(&wheel_dir) { |                             if let Some(wheel) = CachedWheel::from_built_source(wheel_dir) { | ||||||
|                                 Self::add_wheel(wheel, tags, &mut versions); |                                 Self::add_wheel(wheel, tags, &mut versions); | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 }; |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -87,6 +87,15 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { | ||||||
|     ) -> Result<BuiltWheelMetadata, Error> { |     ) -> Result<BuiltWheelMetadata, Error> { | ||||||
|         let built_wheel_metadata = match &source { |         let built_wheel_metadata = match &source { | ||||||
|             BuildableSource::Dist(SourceDist::Registry(dist)) => { |             BuildableSource::Dist(SourceDist::Registry(dist)) => { | ||||||
|  |                 // For registry source distributions, shard by package, then version, for
 | ||||||
|  |                 // convenience in debugging.
 | ||||||
|  |                 let cache_shard = self.build_context.cache().shard( | ||||||
|  |                     CacheBucket::BuiltWheels, | ||||||
|  |                     WheelCache::Index(&dist.index) | ||||||
|  |                         .wheel_dir(dist.filename.name.as_ref()) | ||||||
|  |                         .join(dist.filename.version.to_string()), | ||||||
|  |                 ); | ||||||
|  | 
 | ||||||
|                 let url = match &dist.file.url { |                 let url = match &dist.file.url { | ||||||
|                     FileLocation::RelativeUrl(base, url) => { |                     FileLocation::RelativeUrl(base, url) => { | ||||||
|                         pypi_types::base_url_join_relative(base, url)? |                         pypi_types::base_url_join_relative(base, url)? | ||||||
|  | @ -103,6 +112,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { | ||||||
|                                     url: &url, |                                     url: &url, | ||||||
|                                     path: Cow::Borrowed(path), |                                     path: Cow::Borrowed(path), | ||||||
|                                 }, |                                 }, | ||||||
|  |                                 &cache_shard, | ||||||
|                                 tags, |                                 tags, | ||||||
|                                 hashes, |                                 hashes, | ||||||
|                             ) |                             ) | ||||||
|  | @ -111,15 +121,6 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { | ||||||
|                     } |                     } | ||||||
|                 }; |                 }; | ||||||
| 
 | 
 | ||||||
|                 // For registry source distributions, shard by package, then version, for
 |  | ||||||
|                 // convenience in debugging.
 |  | ||||||
|                 let cache_shard = self.build_context.cache().shard( |  | ||||||
|                     CacheBucket::BuiltWheels, |  | ||||||
|                     WheelCache::Index(&dist.index) |  | ||||||
|                         .wheel_dir(dist.filename.name.as_ref()) |  | ||||||
|                         .join(dist.filename.version.to_string()), |  | ||||||
|                 ); |  | ||||||
| 
 |  | ||||||
|                 self.url( |                 self.url( | ||||||
|                     source, |                     source, | ||||||
|                     &dist.file.filename, |                     &dist.file.filename, | ||||||
|  | @ -165,9 +166,19 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { | ||||||
|                         .boxed() |                         .boxed() | ||||||
|                         .await? |                         .await? | ||||||
|                 } else { |                 } else { | ||||||
|                     self.archive(source, &PathSourceUrl::from(dist), tags, hashes) |                     let cache_shard = self | ||||||
|                         .boxed() |                         .build_context | ||||||
|                         .await? |                         .cache() | ||||||
|  |                         .shard(CacheBucket::BuiltWheels, WheelCache::Path(&dist.url).root()); | ||||||
|  |                     self.archive( | ||||||
|  |                         source, | ||||||
|  |                         &PathSourceUrl::from(dist), | ||||||
|  |                         &cache_shard, | ||||||
|  |                         tags, | ||||||
|  |                         hashes, | ||||||
|  |                     ) | ||||||
|  |                     .boxed() | ||||||
|  |                     .await? | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             BuildableSource::Url(SourceUrl::Direct(resource)) => { |             BuildableSource::Url(SourceUrl::Direct(resource)) => { | ||||||
|  | @ -204,7 +215,13 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { | ||||||
|                         .boxed() |                         .boxed() | ||||||
|                         .await? |                         .await? | ||||||
|                 } else { |                 } else { | ||||||
|                     self.archive(source, resource, tags, hashes).boxed().await? |                     let cache_shard = self.build_context.cache().shard( | ||||||
|  |                         CacheBucket::BuiltWheels, | ||||||
|  |                         WheelCache::Path(resource.url).root(), | ||||||
|  |                     ); | ||||||
|  |                     self.archive(source, resource, &cache_shard, tags, hashes) | ||||||
|  |                         .boxed() | ||||||
|  |                         .await? | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|  | @ -222,6 +239,14 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { | ||||||
|     ) -> Result<ArchiveMetadata, Error> { |     ) -> Result<ArchiveMetadata, Error> { | ||||||
|         let metadata = match &source { |         let metadata = match &source { | ||||||
|             BuildableSource::Dist(SourceDist::Registry(dist)) => { |             BuildableSource::Dist(SourceDist::Registry(dist)) => { | ||||||
|  |                 // For registry source distributions, shard by package, then version.
 | ||||||
|  |                 let cache_shard = self.build_context.cache().shard( | ||||||
|  |                     CacheBucket::BuiltWheels, | ||||||
|  |                     WheelCache::Index(&dist.index) | ||||||
|  |                         .wheel_dir(dist.filename.name.as_ref()) | ||||||
|  |                         .join(dist.filename.version.to_string()), | ||||||
|  |                 ); | ||||||
|  | 
 | ||||||
|                 let url = match &dist.file.url { |                 let url = match &dist.file.url { | ||||||
|                     FileLocation::RelativeUrl(base, url) => { |                     FileLocation::RelativeUrl(base, url) => { | ||||||
|                         pypi_types::base_url_join_relative(base, url)? |                         pypi_types::base_url_join_relative(base, url)? | ||||||
|  | @ -238,6 +263,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { | ||||||
|                                     url: &url, |                                     url: &url, | ||||||
|                                     path: Cow::Borrowed(path), |                                     path: Cow::Borrowed(path), | ||||||
|                                 }, |                                 }, | ||||||
|  |                                 &cache_shard, | ||||||
|                                 hashes, |                                 hashes, | ||||||
|                             ) |                             ) | ||||||
|                             .boxed() |                             .boxed() | ||||||
|  | @ -245,14 +271,6 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { | ||||||
|                     } |                     } | ||||||
|                 }; |                 }; | ||||||
| 
 | 
 | ||||||
|                 // For registry source distributions, shard by package, then version.
 |  | ||||||
|                 let cache_shard = self.build_context.cache().shard( |  | ||||||
|                     CacheBucket::BuiltWheels, |  | ||||||
|                     WheelCache::Index(&dist.index) |  | ||||||
|                         .wheel_dir(dist.filename.name.as_ref()) |  | ||||||
|                         .join(dist.filename.version.to_string()), |  | ||||||
|                 ); |  | ||||||
| 
 |  | ||||||
|                 self.url_metadata( |                 self.url_metadata( | ||||||
|                     source, |                     source, | ||||||
|                     &dist.file.filename, |                     &dist.file.filename, | ||||||
|  | @ -296,7 +314,11 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { | ||||||
|                         .boxed() |                         .boxed() | ||||||
|                         .await? |                         .await? | ||||||
|                 } else { |                 } else { | ||||||
|                     self.archive_metadata(source, &PathSourceUrl::from(dist), hashes) |                     let cache_shard = self | ||||||
|  |                         .build_context | ||||||
|  |                         .cache() | ||||||
|  |                         .shard(CacheBucket::BuiltWheels, WheelCache::Path(&dist.url).root()); | ||||||
|  |                     self.archive_metadata(source, &PathSourceUrl::from(dist), &cache_shard, hashes) | ||||||
|                         .boxed() |                         .boxed() | ||||||
|                         .await? |                         .await? | ||||||
|                 } |                 } | ||||||
|  | @ -334,7 +356,11 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { | ||||||
|                         .boxed() |                         .boxed() | ||||||
|                         .await? |                         .await? | ||||||
|                 } else { |                 } else { | ||||||
|                     self.archive_metadata(source, resource, hashes) |                     let cache_shard = self.build_context.cache().shard( | ||||||
|  |                         CacheBucket::BuiltWheels, | ||||||
|  |                         WheelCache::Path(resource.url).root(), | ||||||
|  |                     ); | ||||||
|  |                     self.archive_metadata(source, resource, &cache_shard, hashes) | ||||||
|                         .boxed() |                         .boxed() | ||||||
|                         .await? |                         .await? | ||||||
|                 } |                 } | ||||||
|  | @ -573,17 +599,13 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { | ||||||
|         &self, |         &self, | ||||||
|         source: &BuildableSource<'_>, |         source: &BuildableSource<'_>, | ||||||
|         resource: &PathSourceUrl<'_>, |         resource: &PathSourceUrl<'_>, | ||||||
|  |         cache_shard: &CacheShard, | ||||||
|         tags: &Tags, |         tags: &Tags, | ||||||
|         hashes: HashPolicy<'_>, |         hashes: HashPolicy<'_>, | ||||||
|     ) -> Result<BuiltWheelMetadata, Error> { |     ) -> Result<BuiltWheelMetadata, Error> { | ||||||
|         let cache_shard = self.build_context.cache().shard( |  | ||||||
|             CacheBucket::BuiltWheels, |  | ||||||
|             WheelCache::Path(resource.url).root(), |  | ||||||
|         ); |  | ||||||
| 
 |  | ||||||
|         // Fetch the revision for the source distribution.
 |         // Fetch the revision for the source distribution.
 | ||||||
|         let revision = self |         let revision = self | ||||||
|             .archive_revision(source, resource, &cache_shard, hashes) |             .archive_revision(source, resource, cache_shard, hashes) | ||||||
|             .await?; |             .await?; | ||||||
| 
 | 
 | ||||||
|         // Before running the build, check that the hashes match.
 |         // Before running the build, check that the hashes match.
 | ||||||
|  | @ -644,16 +666,12 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { | ||||||
|         &self, |         &self, | ||||||
|         source: &BuildableSource<'_>, |         source: &BuildableSource<'_>, | ||||||
|         resource: &PathSourceUrl<'_>, |         resource: &PathSourceUrl<'_>, | ||||||
|  |         cache_shard: &CacheShard, | ||||||
|         hashes: HashPolicy<'_>, |         hashes: HashPolicy<'_>, | ||||||
|     ) -> Result<ArchiveMetadata, Error> { |     ) -> Result<ArchiveMetadata, Error> { | ||||||
|         let cache_shard = self.build_context.cache().shard( |  | ||||||
|             CacheBucket::BuiltWheels, |  | ||||||
|             WheelCache::Path(resource.url).root(), |  | ||||||
|         ); |  | ||||||
| 
 |  | ||||||
|         // Fetch the revision for the source distribution.
 |         // Fetch the revision for the source distribution.
 | ||||||
|         let revision = self |         let revision = self | ||||||
|             .archive_revision(source, resource, &cache_shard, hashes) |             .archive_revision(source, resource, cache_shard, hashes) | ||||||
|             .await?; |             .await?; | ||||||
| 
 | 
 | ||||||
|         // Before running the build, check that the hashes match.
 |         // Before running the build, check that the hashes match.
 | ||||||
|  |  | ||||||
|  | @ -2539,12 +2539,12 @@ fn find_links_offline_no_match() -> Result<()> { | ||||||
| 
 | 
 | ||||||
| /// Sync using `--find-links` with a local directory. Ensure that cached wheels are reused.
 | /// Sync using `--find-links` with a local directory. Ensure that cached wheels are reused.
 | ||||||
| #[test] | #[test] | ||||||
| fn find_links_cache() -> Result<()> { | fn find_links_wheel_cache() -> Result<()> { | ||||||
|     let context = TestContext::new("3.12"); |     let context = TestContext::new("3.12"); | ||||||
| 
 | 
 | ||||||
|     let requirements_txt = context.temp_dir.child("requirements.txt"); |     let requirements_txt = context.temp_dir.child("requirements.txt"); | ||||||
|     requirements_txt.write_str(indoc! {r" |     requirements_txt.write_str(indoc! {r" | ||||||
|         tqdm |         tqdm==1000.0.0 | ||||||
|     "})?;
 |     "})?;
 | ||||||
| 
 | 
 | ||||||
|     // Install `tqdm`.
 |     // Install `tqdm`.
 | ||||||
|  | @ -2585,6 +2585,55 @@ fn find_links_cache() -> Result<()> { | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// Sync using `--find-links` with a local directory. Ensure that cached source distributions are
 | ||||||
|  | /// reused.
 | ||||||
|  | #[test] | ||||||
|  | fn find_links_source_cache() -> Result<()> { | ||||||
|  |     let context = TestContext::new("3.12"); | ||||||
|  | 
 | ||||||
|  |     let requirements_txt = context.temp_dir.child("requirements.txt"); | ||||||
|  |     requirements_txt.write_str(indoc! {r" | ||||||
|  |         tqdm==999.0.0 | ||||||
|  |     "})?;
 | ||||||
|  | 
 | ||||||
|  |     // Install `tqdm`.
 | ||||||
|  |     uv_snapshot!(context.filters(), command(&context) | ||||||
|  |         .arg("requirements.txt") | ||||||
|  |         .arg("--find-links") | ||||||
|  |         .arg(context.workspace_root.join("scripts/links/")), @r###" | ||||||
|  |     success: true | ||||||
|  |     exit_code: 0 | ||||||
|  |     ----- stdout ----- | ||||||
|  | 
 | ||||||
|  |     ----- stderr ----- | ||||||
|  |     Resolved 1 package in [TIME] | ||||||
|  |     Downloaded 1 package in [TIME] | ||||||
|  |     Installed 1 package in [TIME] | ||||||
|  |      + tqdm==999.0.0 | ||||||
|  |     "###
 | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     // Reinstall `tqdm` with `--reinstall`. Ensure that the wheel is reused.
 | ||||||
|  |     uv_snapshot!(context.filters(), command(&context) | ||||||
|  |         .arg("requirements.txt") | ||||||
|  |         .arg("--reinstall") | ||||||
|  |         .arg("--find-links") | ||||||
|  |         .arg(context.workspace_root.join("scripts/links/")), @r###" | ||||||
|  |     success: true | ||||||
|  |     exit_code: 0 | ||||||
|  |     ----- stdout ----- | ||||||
|  | 
 | ||||||
|  |     ----- stderr ----- | ||||||
|  |     Uninstalled 1 package in [TIME] | ||||||
|  |     Installed 1 package in [TIME] | ||||||
|  |      - tqdm==999.0.0 | ||||||
|  |      + tqdm==999.0.0 | ||||||
|  |     "###
 | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /// Install without network access via the `--offline` flag.
 | /// Install without network access via the `--offline` flag.
 | ||||||
| #[test] | #[test] | ||||||
| fn offline() -> Result<()> { | fn offline() -> Result<()> { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Charlie Marsh
						Charlie Marsh