From de190d2aec1c4a6ddc90417272c1066a6af26324 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Fri, 20 Feb 2026 10:57:32 +0100 Subject: [PATCH 1/2] refactor: remove experimental_whl_packages field Remove the packages.whl top-level key from RepoData, Shard, and PatchInstructions. Wheel packages should now be stored under experimental_v3.whl instead. --- .../rattler_conda_types/src/repo_data/mod.rs | 43 +------------------ .../src/repo_data/patches.rs | 35 +++------------ .../src/repo_data/sharded.rs | 6 --- ..._repo_data__patches__test__patch_purl.snap | 1 - ...patches__test__patch_tracked_features.snap | 1 - ...s__repo_data__patches__test__patching.snap | 1 - ..._repo_data__patches__test__removing_1.snap | 1 - ..._repo_data__patches__test__removing_2.snap | 1 - ...test__deserialize_no_noarch_empty_str.snap | 1 - ...__test__deserialize_no_packages_conda.snap | 1 - ...nda_types__repo_data__test__serialize.snap | 1 - ...repo_data__test__serialize_packages-2.snap | 1 - ...__repo_data__test__serialize_packages.snap | 1 - crates/rattler_index/src/lib.rs | 4 +- 14 files changed, 9 insertions(+), 89 deletions(-) diff --git a/crates/rattler_conda_types/src/repo_data/mod.rs b/crates/rattler_conda_types/src/repo_data/mod.rs index 64b96887e3..0783ab71b6 100644 --- a/crates/rattler_conda_types/src/repo_data/mod.rs +++ b/crates/rattler_conda_types/src/repo_data/mod.rs @@ -57,15 +57,6 @@ pub struct RepoData { )] pub conda_packages: IndexMap, - /// The wheel packages contained in the repodata.json file - #[serde( - default, - rename = "packages.whl", - serialize_with = "sort_index_map_alphabetically" - )] - pub experimental_whl_packages: - IndexMap, - /// removed packages (files are still accessible, but they are not /// installable like regular packages) #[serde( @@ -383,36 +374,6 @@ impl RepoData { }); } - // Determine the base_url of the channel - for ( - identifier, - WhlPackageRecord { - url, - package_record, - }, - ) in self.experimental_whl_packages - { - let url = match url { - UrlOrPath::Path(path) => compute_package_url( - &channel - .base_url - .url() - .join(&package_record.subdir) - .expect("cannot join channel base_url and subdir"), - base_url.as_deref(), - &path, - ), - UrlOrPath::Url(url) => url, - }; - - records.push(RepoDataRecord { - url, - channel: Some(channel.base_url.as_str().to_string()), - package_record, - identifier, - }); - } - records } } @@ -747,7 +708,7 @@ mod test { info: None, packages: IndexMap::default(), conda_packages: IndexMap::default(), - experimental_whl_packages: IndexMap::default(), + removed: [ "xyz-1-py.conda", "foo-1-py.conda", @@ -1004,7 +965,7 @@ mod test { info: None, packages, conda_packages, - experimental_whl_packages: IndexMap::default(), + removed: ahash::HashSet::default(), }; diff --git a/crates/rattler_conda_types/src/repo_data/patches.rs b/crates/rattler_conda_types/src/repo_data/patches.rs index 156dbabf1f..f630cff495 100644 --- a/crates/rattler_conda_types/src/repo_data/patches.rs +++ b/crates/rattler_conda_types/src/repo_data/patches.rs @@ -7,8 +7,8 @@ use serde::{Deserialize, Serialize}; use serde_with::{serde_as, skip_serializing_none}; use crate::{ - package::{CondaArchiveType, DistArchiveIdentifier, DistArchiveType, WheelArchiveType}, - PackageRecord, PackageUrl, RepoData, Shard, WhlPackageRecord, + package::{CondaArchiveType, DistArchiveIdentifier, DistArchiveType}, + PackageRecord, PackageUrl, RepoData, Shard, }; /// Represents a Conda repodata patch. @@ -172,13 +172,6 @@ pub struct PatchInstructions { )] pub conda_packages: ahash::HashMap, - /// Patches for wheel package records (experimental) - #[serde( - default, - rename = "packages.whl", - skip_serializing_if = "ahash::HashMap::is_empty" - )] - pub experimental_whl_packages: ahash::HashMap, } impl PackageRecord { @@ -213,11 +206,6 @@ impl PackageRecord { pub fn apply_patches_impl( packages: &mut IndexMap, conda_packages: &mut IndexMap, - experimental_whl_packages: &mut IndexMap< - DistArchiveIdentifier, - WhlPackageRecord, - ahash::RandomState, - >, removed: &mut ahash::HashSet, instructions: &PatchInstructions, ) { @@ -238,13 +226,6 @@ pub fn apply_patches_impl( } } - // Apply patches to wheel packages - for (identifier, patch) in instructions.experimental_whl_packages.iter() { - if let Some(record) = experimental_whl_packages.get_mut(identifier) { - record.package_record.apply_patch(patch); - } - } - // remove packages that have been removed for identifier in instructions.remove.iter() { match identifier.archive_type { @@ -267,13 +248,9 @@ pub fn apply_patches_impl( removed.insert(identifier.clone()); } } - DistArchiveType::Wheel(WheelArchiveType::Whl) => { - if experimental_whl_packages - .shift_remove_entry(identifier) - .is_some() - { - removed.insert(identifier.clone()); - } + DistArchiveType::Wheel(_) => { + // Wheel packages are stored in v3.whl; removal by + // DistArchiveIdentifier is not currently supported for v3. } } } @@ -286,7 +263,6 @@ impl RepoData { apply_patches_impl( &mut self.packages, &mut self.conda_packages, - &mut self.experimental_whl_packages, &mut self.removed, instructions, ); @@ -300,7 +276,6 @@ impl Shard { apply_patches_impl( &mut self.packages, &mut self.conda_packages, - &mut self.experimental_whl_packages, &mut self.removed, instructions, ); diff --git a/crates/rattler_conda_types/src/repo_data/sharded.rs b/crates/rattler_conda_types/src/repo_data/sharded.rs index c4c869d9f1..28e3e504e5 100644 --- a/crates/rattler_conda_types/src/repo_data/sharded.rs +++ b/crates/rattler_conda_types/src/repo_data/sharded.rs @@ -1,7 +1,6 @@ //! Structs to deal with repodata "shards" which are per-package repodata files. use crate::package::DistArchiveIdentifier; -use crate::repo_data::WhlPackageRecord; use crate::PackageRecord; use chrono::{DateTime, Utc}; use indexmap::IndexMap; @@ -56,11 +55,6 @@ pub struct Shard { #[serde(rename = "packages.conda", default)] pub conda_packages: IndexMap, - /// The records for all `.whl` packages - #[serde(rename = "packages.whl", default)] - pub experimental_whl_packages: - IndexMap, - /// The file names of all removed for this shard #[serde(default)] pub removed: ahash::HashSet, diff --git a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__patch_purl.snap b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__patch_purl.snap index 56942f7647..dc83b937fb 100644 --- a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__patch_purl.snap +++ b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__patch_purl.snap @@ -79,5 +79,4 @@ packages.conda: subdir: linux-64 timestamp: 1679045249592 version: 3.10.1 -packages.whl: {} repodata_version: 1 diff --git a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__patch_tracked_features.snap b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__patch_tracked_features.snap index 4d5bf8a5f4..aa87bf50c0 100644 --- a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__patch_tracked_features.snap +++ b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__patch_tracked_features.snap @@ -22,5 +22,4 @@ packages.conda: subdir: win-64 timestamp: 1744247964462 version: 2.6.0 -packages.whl: {} repodata_version: 1 diff --git a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__patching.snap b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__patching.snap index 8df0ec71b3..c7d18a0b11 100644 --- a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__patching.snap +++ b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__patching.snap @@ -76,5 +76,4 @@ packages.conda: subdir: linux-64 timestamp: 1679045249592 version: 3.10.1 -packages.whl: {} repodata_version: 1 diff --git a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__removing_1.snap b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__removing_1.snap index 4608d8d1c4..4d659c2116 100644 --- a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__removing_1.snap +++ b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__removing_1.snap @@ -25,7 +25,6 @@ packages.conda: subdir: linux-64 timestamp: 1679045249592 version: 3.10.1 -packages.whl: {} removed: - cross-python_emscripten-32-3.10.1-h60d57d3_8.conda - cross-python_emscripten-32-3.10.1-h60d57d3_8.tar.bz2 diff --git a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__removing_2.snap b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__removing_2.snap index 813e8b79a3..ce6c85a2f2 100644 --- a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__removing_2.snap +++ b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__patches__test__removing_2.snap @@ -56,7 +56,6 @@ packages.conda: subdir: linux-64 timestamp: 1679045249592 version: 3.10.1 -packages.whl: {} removed: - cross-python_emscripten-32-3.10.1-h60d57d3_8.conda repodata_version: 1 diff --git a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__deserialize_no_noarch_empty_str.snap b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__deserialize_no_noarch_empty_str.snap index 12855b4556..0fe684c918 100644 --- a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__deserialize_no_noarch_empty_str.snap +++ b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__deserialize_no_noarch_empty_str.snap @@ -31,5 +31,4 @@ packages: subdir: linux-64 version: "1" packages.conda: {} -packages.whl: {} repodata_version: 2 diff --git a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__deserialize_no_packages_conda.snap b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__deserialize_no_packages_conda.snap index 1401f3114d..7ad5d15346 100644 --- a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__deserialize_no_packages_conda.snap +++ b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__deserialize_no_packages_conda.snap @@ -180,5 +180,4 @@ packages: timestamp: 1605110689658 version: "2.1" packages.conda: {} -packages.whl: {} repodata_version: 2 diff --git a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__serialize.snap b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__serialize.snap index f9ba94028b..37e3abf071 100644 --- a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__serialize.snap +++ b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__serialize.snap @@ -5,7 +5,6 @@ expression: repodata info: ~ packages: {} packages.conda: {} -packages.whl: {} removed: - aux-1-py.tar.bz2 - bar-1-py.conda diff --git a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__serialize_packages-2.snap b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__serialize_packages-2.snap index 5cea1a106d..40b48e5a34 100644 --- a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__serialize_packages-2.snap +++ b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__serialize_packages-2.snap @@ -443,6 +443,5 @@ expression: json "version": "2.1" } }, - "packages.whl": {}, "repodata_version": 2 } diff --git a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__serialize_packages.snap b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__serialize_packages.snap index ad66ab2c82..6080eaf871 100644 --- a/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__serialize_packages.snap +++ b/crates/rattler_conda_types/src/repo_data/snapshots/rattler_conda_types__repo_data__test__serialize_packages.snap @@ -400,5 +400,4 @@ packages.conda: subdir: linux-64 timestamp: 1715610974000 version: "2.1" -packages.whl: {} repodata_version: 2 diff --git a/crates/rattler_index/src/lib.rs b/crates/rattler_index/src/lib.rs index 8c938ee310..0cd8c21e5e 100644 --- a/crates/rattler_index/src/lib.rs +++ b/crates/rattler_index/src/lib.rs @@ -806,7 +806,7 @@ async fn index_subdir_inner( }), packages, conda_packages, - experimental_whl_packages: IndexMap::default(), + removed: HashSet::default(), version: Some(2), }; @@ -1295,7 +1295,7 @@ pub async fn ensure_channel_initialized(op: &Operator) -> anyhow::Result<()> { }), packages: IndexMap::default(), conda_packages: IndexMap::default(), - experimental_whl_packages: IndexMap::default(), + removed: HashSet::default(), version: Some(2), }; From 01fda613e5b7e50d6d734f7a095b06a3941aa673 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Fri, 20 Feb 2026 11:16:26 +0100 Subject: [PATCH 2/2] feat: add experimental_v3 top-level key to repodata Add a new `v3` top-level key to repodata that uses extension-less `ArchiveIdentifier` keys (name-version-build) with `tar.bz2`, `conda`, and `whl` sub-keys. This enables a backwards-compatible path for evolving the repodata format. Changes: - Add `ExperimentalV3Packages` struct with tar_bz2, conda, whl sub-maps - Add `experimental_v3` field to `RepoData`, `Shard`, and `PatchInstructions` - Add `DistArchiveIdentifier::new()` constructor - Update sparse loader with `LazyV3Packages` for deferred parsing - Update sharded subdir gateway to chain v3 packages - Update `SubdirRunExportsJson` to look up v3 run exports - Migrate test data from `packages.whl` to `v3.whl` format --- crates/rattler_conda_types/src/lib.rs | 4 +- .../src/package/archive_identifier.rs | 9 + .../rattler_conda_types/src/repo_data/mod.rs | 168 +- .../src/repo_data/patches.rs | 57 +- .../src/repo_data/sharded.rs | 5 + crates/rattler_index/src/lib.rs | 8 +- .../src/gateway/sharded_subdir/mod.rs | 97 +- .../src/sparse/mod.rs | 297 ++- py-rattler/Cargo.lock | 30 +- .../channels/with-wheels/noarch/repodata.json | 1904 +++++++++-------- 10 files changed, 1480 insertions(+), 1099 deletions(-) diff --git a/crates/rattler_conda_types/src/lib.rs b/crates/rattler_conda_types/src/lib.rs index 860dffd1a7..52feabe849 100644 --- a/crates/rattler_conda_types/src/lib.rs +++ b/crates/rattler_conda_types/src/lib.rs @@ -62,8 +62,8 @@ pub use repo_data::{ compute_package_url, patches::{PackageRecordPatch, PatchInstructions, RepoDataPatch}, sharded::{Shard, ShardedRepodata, ShardedSubdirInfo}, - ChannelInfo, ConvertSubdirError, PackageRecord, RecordFromPath, RepoData, SubdirRunExportsJson, - UrlOrPath, ValidatePackageRecordsError, WhlPackageRecord, + ChannelInfo, ConvertSubdirError, ExperimentalV3Packages, PackageRecord, RecordFromPath, + RepoData, SubdirRunExportsJson, UrlOrPath, ValidatePackageRecordsError, WhlPackageRecord, }; pub use repo_data_record::{RepoDataRecord, SolverResult}; pub use run_export::RunExportKind; diff --git a/crates/rattler_conda_types/src/package/archive_identifier.rs b/crates/rattler_conda_types/src/package/archive_identifier.rs index a3e942dc96..c5fe3d4d56 100644 --- a/crates/rattler_conda_types/src/package/archive_identifier.rs +++ b/crates/rattler_conda_types/src/package/archive_identifier.rs @@ -127,6 +127,15 @@ pub struct DistArchiveIdentifier { } impl DistArchiveIdentifier { + /// Constructs a new [`DistArchiveIdentifier`] from an + /// [`ArchiveIdentifier`] and an archive type. + pub fn new(identifier: ArchiveIdentifier, archive_type: impl Into) -> Self { + Self { + identifier, + archive_type: archive_type.into(), + } + } + /// Returns a new version but with a different archive type. pub fn with_archive_type(&self, archive_type: DistArchiveType) -> Self { Self { diff --git a/crates/rattler_conda_types/src/repo_data/mod.rs b/crates/rattler_conda_types/src/repo_data/mod.rs index 0783ab71b6..c70454fe38 100644 --- a/crates/rattler_conda_types/src/repo_data/mod.rs +++ b/crates/rattler_conda_types/src/repo_data/mod.rs @@ -23,7 +23,10 @@ use url::Url; use crate::{ build_spec::BuildNumber, - package::{DistArchiveIdentifier, IndexJson, RunExportsJson}, + package::{ + ArchiveIdentifier, CondaArchiveType, DistArchiveIdentifier, IndexJson, RunExportsJson, + WheelArchiveType, + }, utils::{ serde::{ sort_index_map_alphabetically, sort_map_alphabetically, DeserializeFromStrUnchecked, @@ -57,6 +60,16 @@ pub struct RepoData { )] pub conda_packages: IndexMap, + /// Packages stored under the `v3` top-level key. + /// Uses extension-less `ArchiveIdentifier` keys with sub-maps for each + /// archive type. + #[serde( + default, + rename = "v3", + skip_serializing_if = "ExperimentalV3Packages::is_empty" + )] + pub experimental_v3: ExperimentalV3Packages, + /// removed packages (files are still accessible, but they are not /// installable like regular packages) #[serde( @@ -82,6 +95,45 @@ pub struct ChannelInfo { pub base_url: Option, } +/// Packages stored under the `v3` top-level key. +/// +/// Records in this set of packages can have conditional dependencies, extras +/// and can be whls. +#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone, Default)] +pub struct ExperimentalV3Packages { + /// The tar.bz2 package records + #[serde( + default, + rename = "tar.bz2", + serialize_with = "sort_map_alphabetically", + skip_serializing_if = "ahash::HashMap::is_empty" + )] + pub tar_bz2: ahash::HashMap, + + /// The conda package records + #[serde( + default, + serialize_with = "sort_map_alphabetically", + skip_serializing_if = "ahash::HashMap::is_empty" + )] + pub conda: ahash::HashMap, + + /// The whl package records + #[serde( + default, + serialize_with = "sort_map_alphabetically", + skip_serializing_if = "ahash::HashMap::is_empty" + )] + pub whl: ahash::HashMap, +} + +impl ExperimentalV3Packages { + /// Returns true if all sub-maps are empty. + pub fn is_empty(&self) -> bool { + self.tar_bz2.is_empty() && self.conda.is_empty() && self.whl.is_empty() + } +} + /// Trait to allow for generic deserialization of records from a path. pub trait RecordFromPath { /// Deserialize a record from a path. @@ -355,25 +407,75 @@ impl RepoData { pub fn into_repo_data_records(self, channel: &Channel) -> Vec { let mut records = Vec::with_capacity(self.packages.len() + self.conda_packages.len()); let base_url = self.base_url().map(ToOwned::to_owned); + let channel_str = channel.base_url.as_str().to_string(); + + let subdir_url = |subdir: &str| { + channel + .base_url + .url() + .join(subdir) + .expect("cannot join channel base_url and subdir") + }; - // Determine the base_url of the channel - for (identifier, package_record) in self.packages.into_iter().chain(self.conda_packages) { + // Conda packages: packages, packages.conda, v3.tar.bz2, v3.conda + let v3_tar_bz2 = self.experimental_v3.tar_bz2.into_iter().map(|(id, rec)| { + ( + DistArchiveIdentifier::new(id, CondaArchiveType::TarBz2), + rec, + ) + }); + let v3_conda = self + .experimental_v3 + .conda + .into_iter() + .map(|(id, rec)| (DistArchiveIdentifier::new(id, CondaArchiveType::Conda), rec)); + + for (identifier, package_record) in self + .packages + .into_iter() + .chain(self.conda_packages) + .chain(v3_tar_bz2) + .chain(v3_conda) + { records.push(RepoDataRecord { url: compute_package_url( - &channel - .base_url - .url() - .join(&package_record.subdir) - .expect("cannot join channel base_url and subdir"), + &subdir_url(&package_record.subdir), base_url.as_deref(), &identifier.to_file_name(), ), - channel: Some(channel.base_url.as_str().to_string()), + channel: Some(channel_str.clone()), package_record, identifier, }); } + // Whl packages: v3.whl + for ( + id, + WhlPackageRecord { + url, + package_record, + }, + ) in self.experimental_v3.whl + { + let dist_id = DistArchiveIdentifier::new(id, WheelArchiveType::Whl); + let url = match url { + UrlOrPath::Path(path) => compute_package_url( + &subdir_url(&package_record.subdir), + base_url.as_deref(), + &path, + ), + UrlOrPath::Url(url) => url, + }; + + records.push(RepoDataRecord { + url, + channel: Some(channel_str.clone()), + package_record, + identifier: dist_id, + }); + } + records } } @@ -533,6 +635,42 @@ pub struct SubdirRunExportsJson { serialize_with = "sort_map_alphabetically" )] conda_packages: ahash::HashMap, + + /// Run exports for v3 packages. + #[serde( + default, + rename = "v3", + skip_serializing_if = "ExperimentalV3RunExports::is_empty" + )] + experimental_v3: ExperimentalV3RunExports, +} + +/// Run exports for packages stored under the `v3` top-level key. +#[derive(Debug, Default, Deserialize, Serialize, Eq, PartialEq, Clone)] +struct ExperimentalV3RunExports { + /// Run exports for v3 tar.bz2 packages + #[serde( + default, + rename = "tar.bz2", + serialize_with = "sort_map_alphabetically", + skip_serializing_if = "ahash::HashMap::is_empty" + )] + tar_bz2: ahash::HashMap, + + /// Run exports for v3 conda packages + #[serde( + default, + serialize_with = "sort_map_alphabetically", + skip_serializing_if = "ahash::HashMap::is_empty" + )] + conda: ahash::HashMap, +} + +impl ExperimentalV3RunExports { + /// Returns true if all sub-maps are empty. + pub fn is_empty(&self) -> bool { + self.tar_bz2.is_empty() && self.conda.is_empty() + } } impl SubdirRunExportsJson { @@ -542,6 +680,12 @@ impl SubdirRunExportsJson { self.packages .get(file_name) .or_else(|| self.conda_packages.get(file_name)) + .or_else(|| { + self.experimental_v3 + .tar_bz2 + .get(&file_name.identifier) + .or_else(|| self.experimental_v3.conda.get(&file_name.identifier)) + }) .map(|pre| &pre.run_exports) } @@ -686,7 +830,7 @@ mod test { use crate::{ package::DistArchiveIdentifier, repo_data::{compute_package_url, determine_subdir}, - Channel, ChannelConfig, PackageRecord, RepoData, + Channel, ChannelConfig, ExperimentalV3Packages, PackageRecord, RepoData, }; // isl-0.12.2-1.tar.bz2 @@ -708,7 +852,7 @@ mod test { info: None, packages: IndexMap::default(), conda_packages: IndexMap::default(), - + experimental_v3: ExperimentalV3Packages::default(), removed: [ "xyz-1-py.conda", "foo-1-py.conda", @@ -965,7 +1109,7 @@ mod test { info: None, packages, conda_packages, - + experimental_v3: ExperimentalV3Packages::default(), removed: ahash::HashSet::default(), }; diff --git a/crates/rattler_conda_types/src/repo_data/patches.rs b/crates/rattler_conda_types/src/repo_data/patches.rs index f630cff495..88dbfe0a6a 100644 --- a/crates/rattler_conda_types/src/repo_data/patches.rs +++ b/crates/rattler_conda_types/src/repo_data/patches.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use serde_with::{serde_as, skip_serializing_none}; use crate::{ - package::{CondaArchiveType, DistArchiveIdentifier, DistArchiveType}, + package::{ArchiveIdentifier, CondaArchiveType, DistArchiveIdentifier, DistArchiveType}, PackageRecord, PackageUrl, RepoData, Shard, }; @@ -152,6 +152,34 @@ mod tracked_features { } } +/// Patches for packages stored under the `v3` top-level key. +/// Mirrors [`ExperimentalV3Packages`] but with [`PackageRecordPatch`] values. +#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone, Default)] +pub struct ExperimentalV3PackagePatches { + /// Patches for v3 tar.bz2 package records + #[serde( + default, + rename = "tar.bz2", + skip_serializing_if = "ahash::HashMap::is_empty" + )] + pub tar_bz2: ahash::HashMap, + + /// Patches for v3 conda package records + #[serde(default, skip_serializing_if = "ahash::HashMap::is_empty")] + pub conda: ahash::HashMap, + + /// Patches for v3 whl package records + #[serde(default, skip_serializing_if = "ahash::HashMap::is_empty")] + pub whl: ahash::HashMap, +} + +impl ExperimentalV3PackagePatches { + /// Returns true if all sub-maps are empty. + pub fn is_empty(&self) -> bool { + self.tar_bz2.is_empty() && self.conda.is_empty() && self.whl.is_empty() + } +} + /// Repodata patch instructions for a single subdirectory. See [`RepoDataPatch`] /// for more information. #[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)] @@ -172,6 +200,13 @@ pub struct PatchInstructions { )] pub conda_packages: ahash::HashMap, + /// Patches for v3 packages + #[serde( + default, + rename = "v3", + skip_serializing_if = "ExperimentalV3PackagePatches::is_empty" + )] + pub experimental_v3: ExperimentalV3PackagePatches, } impl PackageRecord { @@ -206,6 +241,7 @@ impl PackageRecord { pub fn apply_patches_impl( packages: &mut IndexMap, conda_packages: &mut IndexMap, + v3: &mut super::ExperimentalV3Packages, removed: &mut ahash::HashSet, instructions: &PatchInstructions, ) { @@ -226,6 +262,23 @@ pub fn apply_patches_impl( } } + // Apply patches to v3 packages + for (identifier, patch) in instructions.experimental_v3.tar_bz2.iter() { + if let Some(record) = v3.tar_bz2.get_mut(identifier) { + record.apply_patch(patch); + } + } + for (identifier, patch) in instructions.experimental_v3.conda.iter() { + if let Some(record) = v3.conda.get_mut(identifier) { + record.apply_patch(patch); + } + } + for (identifier, patch) in instructions.experimental_v3.whl.iter() { + if let Some(record) = v3.whl.get_mut(identifier) { + record.package_record.apply_patch(patch); + } + } + // remove packages that have been removed for identifier in instructions.remove.iter() { match identifier.archive_type { @@ -263,6 +316,7 @@ impl RepoData { apply_patches_impl( &mut self.packages, &mut self.conda_packages, + &mut self.experimental_v3, &mut self.removed, instructions, ); @@ -276,6 +330,7 @@ impl Shard { apply_patches_impl( &mut self.packages, &mut self.conda_packages, + &mut self.experimental_v3, &mut self.removed, instructions, ); diff --git a/crates/rattler_conda_types/src/repo_data/sharded.rs b/crates/rattler_conda_types/src/repo_data/sharded.rs index 28e3e504e5..0ae425ce1a 100644 --- a/crates/rattler_conda_types/src/repo_data/sharded.rs +++ b/crates/rattler_conda_types/src/repo_data/sharded.rs @@ -1,6 +1,7 @@ //! Structs to deal with repodata "shards" which are per-package repodata files. use crate::package::DistArchiveIdentifier; +use crate::repo_data::ExperimentalV3Packages; use crate::PackageRecord; use chrono::{DateTime, Utc}; use indexmap::IndexMap; @@ -55,6 +56,10 @@ pub struct Shard { #[serde(rename = "packages.conda", default)] pub conda_packages: IndexMap, + /// Packages stored under the `v3` top-level key. + #[serde(default, rename = "v3")] + pub experimental_v3: ExperimentalV3Packages, + /// The file names of all removed for this shard #[serde(default)] pub removed: ahash::HashSet, diff --git a/crates/rattler_index/src/lib.rs b/crates/rattler_index/src/lib.rs index 0cd8c21e5e..3450c6992e 100644 --- a/crates/rattler_index/src/lib.rs +++ b/crates/rattler_index/src/lib.rs @@ -34,8 +34,8 @@ use rattler_conda_types::{ CondaArchiveType, DistArchiveIdentifier, DistArchiveType, IndexJson, PackageFile, RunExportsJson, WheelArchiveType, }, - ChannelInfo, PackageRecord, PatchInstructions, Platform, RepoData, Shard, ShardedRepodata, - ShardedSubdirInfo, + ChannelInfo, ExperimentalV3Packages, PackageRecord, PatchInstructions, Platform, RepoData, + Shard, ShardedRepodata, ShardedSubdirInfo, }; use rattler_digest::Sha256Hash; use rattler_package_streaming::{ @@ -806,7 +806,7 @@ async fn index_subdir_inner( }), packages, conda_packages, - + experimental_v3: ExperimentalV3Packages::default(), removed: HashSet::default(), version: Some(2), }; @@ -1295,7 +1295,7 @@ pub async fn ensure_channel_initialized(op: &Operator) -> anyhow::Result<()> { }), packages: IndexMap::default(), conda_packages: IndexMap::default(), - + experimental_v3: ExperimentalV3Packages::default(), removed: HashSet::default(), version: Some(2), }; diff --git a/crates/rattler_repodata_gateway/src/gateway/sharded_subdir/mod.rs b/crates/rattler_repodata_gateway/src/gateway/sharded_subdir/mod.rs index b28532c095..47f3655838 100644 --- a/crates/rattler_repodata_gateway/src/gateway/sharded_subdir/mod.rs +++ b/crates/rattler_repodata_gateway/src/gateway/sharded_subdir/mod.rs @@ -2,7 +2,10 @@ use std::borrow::Cow; use std::sync::Arc; use cfg_if::cfg_if; -use rattler_conda_types::{ChannelUrl, RepoDataRecord, Shard}; +use rattler_conda_types::{ + package::{CondaArchiveType, DistArchiveIdentifier, WheelArchiveType}, + ChannelUrl, RepoDataRecord, Shard, UrlOrPath, WhlPackageRecord, +}; use rattler_redaction::Redact; use url::Url; @@ -82,36 +85,74 @@ async fn parse_records + Send + 'static>( channel_base_url: ChannelUrl, base_url: Url, ) -> Result { - let parse = move || { - // let shard = - // serde_json::from_slice::(bytes.as_ref()). - // map_err(std::io::Error::from)?; - let shard = rmp_serde::from_slice::(bytes.as_ref()) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string())) - .map_err(FetchRepoDataError::IoError)?; - let packages = - itertools::chain(shard.packages.into_iter(), shard.conda_packages.into_iter()) - .filter(|(name, _record)| !shard.removed.contains(name)); - let channel_str = channel_base_url.url().clone().redact().to_string(); - let base_url_str = base_url.as_str(); - let records: Vec> = packages - .map(|(file_name, package_record)| { - let file_name_str = file_name.to_file_name(); - Arc::new(RepoDataRecord { - url: Url::parse(&format!("{base_url_str}{file_name_str}")) - .expect("filename is not a valid url"), + let parse = + move || { + let shard = rmp_serde::from_slice::(bytes.as_ref()) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string())) + .map_err(FetchRepoDataError::IoError)?; + + // Chain v3 tar.bz2/conda packages into the main iteration + let v3_tar_bz2 = shard.experimental_v3.tar_bz2.into_iter().map(|(id, rec)| { + ( + DistArchiveIdentifier::new(id, CondaArchiveType::TarBz2), + rec, + ) + }); + let v3_conda = + shard.experimental_v3.conda.into_iter().map(|(id, rec)| { + (DistArchiveIdentifier::new(id, CondaArchiveType::Conda), rec) + }); + + let packages = + itertools::chain(shard.packages.into_iter(), shard.conda_packages.into_iter()) + .chain(v3_tar_bz2) + .chain(v3_conda) + .filter(|(name, _record)| !shard.removed.contains(name)); + + let channel_str = channel_base_url.url().clone().redact().to_string(); + let base_url_str = base_url.as_str(); + let mut records: Vec> = packages + .map(|(file_name, package_record)| { + let file_name_str = file_name.to_file_name(); + Arc::new(RepoDataRecord { + url: Url::parse(&format!("{base_url_str}{file_name_str}")) + .expect("filename is not a valid url"), + channel: Some(channel_str.clone()), + package_record, + identifier: file_name, + }) + }) + .collect(); + + // Handle v3 whl packages separately (different URL resolution) + for ( + id, + WhlPackageRecord { + url, + package_record, + }, + ) in shard.experimental_v3.whl + { + let dist_id = DistArchiveIdentifier::new(id, WheelArchiveType::Whl); + let url = match url { + UrlOrPath::Path(path) => Url::parse(&format!("{base_url_str}{path}")) + .expect("path is not a valid url"), + UrlOrPath::Url(url) => url, + }; + records.push(Arc::new(RepoDataRecord { + url, channel: Some(channel_str.clone()), package_record, - identifier: file_name, - }) + identifier: dist_id, + })); + } + + let unique_deps = extract_unique_deps(records.iter().map(|r| &**r)); + Ok(PackageRecords { + records, + unique_deps, }) - .collect(); - let unique_deps = extract_unique_deps(records.iter().map(|r| &**r)); - Ok(PackageRecords { - records, - unique_deps, - }) - }; + }; #[cfg(target_arch = "wasm32")] return parse(); diff --git a/crates/rattler_repodata_gateway/src/sparse/mod.rs b/crates/rattler_repodata_gateway/src/sparse/mod.rs index ceb6404420..af2f94170d 100644 --- a/crates/rattler_repodata_gateway/src/sparse/mod.rs +++ b/crates/rattler_repodata_gateway/src/sparse/mod.rs @@ -219,78 +219,109 @@ impl SparseRepoData { let repo_data = self.inner.borrow_repo_data(); let tar_bz2_packages = repo_data.packages.iter().map(select_package_name); let conda_packages = repo_data.conda_packages.iter().map(select_package_name); - let whl_packages = repo_data - .experimental_whl_packages + let v3_tar = repo_data + .experimental_v3 + .tar_bz2 + .iter() + .map(select_package_name); + let v3_conda = repo_data + .experimental_v3 + .conda + .iter() + .map(select_package_name); + let v3_whl = repo_data + .experimental_v3 + .whl .iter() .map(select_package_name); match package_format_selection { PackageFormatSelection::Both | PackageFormatSelection::PreferConda => { itertools::Either::Left(itertools::Either::Left( - tar_bz2_packages.merge(conda_packages).dedup(), + tar_bz2_packages + .merge(v3_tar) + .merge(conda_packages.merge(v3_conda)) + .dedup(), )) } PackageFormatSelection::PreferCondaWithWhl => { itertools::Either::Left(itertools::Either::Right( tar_bz2_packages - .merge(whl_packages) - .merge(conda_packages) + .merge(v3_tar) + .merge(v3_whl) + .merge(conda_packages.merge(v3_conda)) .dedup(), )) } - PackageFormatSelection::OnlyTarBz2 => { - itertools::Either::Right(itertools::Either::Left(tar_bz2_packages.dedup())) - } - PackageFormatSelection::OnlyConda => { - itertools::Either::Right(itertools::Either::Right(conda_packages.dedup())) - } + PackageFormatSelection::OnlyTarBz2 => itertools::Either::Right( + itertools::Either::Left(tar_bz2_packages.merge(v3_tar).dedup()), + ), + PackageFormatSelection::OnlyConda => itertools::Either::Right( + itertools::Either::Right(conda_packages.merge(v3_conda).dedup()), + ), } } /// Returns the number of records in this instance. pub fn record_count(&self, package_format_selection: PackageFormatSelection) -> usize { + let repo_data = self.inner.borrow_repo_data(); match package_format_selection { PackageFormatSelection::PreferConda | PackageFormatSelection::PreferCondaWithWhl => { - let repo_data = self.inner.borrow_repo_data(); let tar_bz2_packages = repo_data.packages.iter().map(|(filename, _)| { filename .filename .strip_suffix(CondaArchiveType::TarBz2.extension()) .unwrap_or(filename.filename) }); + let v3_tar = repo_data + .experimental_v3 + .tar_bz2 + .iter() + .map(|(filename, _)| filename.filename); let conda_packages = repo_data.conda_packages.iter().map(|(filename, _)| { filename .filename .strip_suffix(CondaArchiveType::Conda.extension()) .unwrap_or(filename.filename) }); - // Add wheel files if specified + let v3_conda = repo_data + .experimental_v3 + .conda + .iter() + .map(|(filename, _)| filename.filename); + if package_format_selection == PackageFormatSelection::PreferCondaWithWhl { - let whl_packages = - repo_data - .experimental_whl_packages - .iter() - .map(|(filename, _)| { - filename - .filename - .strip_suffix(WheelArchiveType::Whl.extension()) - .unwrap_or(filename.filename) - }); + let v3_whl = repo_data + .experimental_v3 + .whl + .iter() + .map(|(filename, _)| filename.filename); conda_packages - .merge(tar_bz2_packages) - .merge(whl_packages) + .merge(v3_conda) + .merge(tar_bz2_packages.merge(v3_tar)) + .merge(v3_whl) .dedup() .count() } else { - conda_packages.merge(tar_bz2_packages).dedup().count() + conda_packages + .merge(v3_conda) + .merge(tar_bz2_packages.merge(v3_tar)) + .dedup() + .count() } } PackageFormatSelection::Both => { - self.inner.borrow_repo_data().packages.len() - + self.inner.borrow_repo_data().conda_packages.len() + repo_data.packages.len() + + repo_data.conda_packages.len() + + repo_data.experimental_v3.tar_bz2.len() + + repo_data.experimental_v3.conda.len() + } + PackageFormatSelection::OnlyTarBz2 => { + repo_data.packages.len() + repo_data.experimental_v3.tar_bz2.len() + } + PackageFormatSelection::OnlyConda => { + repo_data.conda_packages.len() + repo_data.experimental_v3.conda.len() } - PackageFormatSelection::OnlyTarBz2 => self.inner.borrow_repo_data().packages.len(), - PackageFormatSelection::OnlyConda => self.inner.borrow_repo_data().conda_packages.len(), } } @@ -310,7 +341,7 @@ impl SparseRepoData { package_name.and_then(Option::::from).as_ref(), &repo_data.packages, &repo_data.conda_packages, - &repo_data.experimental_whl_packages, + &repo_data.experimental_v3, variant_consolidation, base_url, &self.channel, @@ -340,7 +371,7 @@ impl SparseRepoData { Some(package_name), &repo_data.packages, &repo_data.conda_packages, - &repo_data.experimental_whl_packages, + &repo_data.experimental_v3, variant_consolidation, base_url, &self.channel, @@ -361,7 +392,7 @@ impl SparseRepoData { None, &repo_data.packages, &repo_data.conda_packages, - &repo_data.experimental_whl_packages, + &repo_data.experimental_v3, variant_consolidation, base_url, &self.channel, @@ -409,7 +440,7 @@ impl SparseRepoData { Some(&next_package), &repo_data_packages.packages, &repo_data_packages.conda_packages, - &repo_data_packages.experimental_whl_packages, + &repo_data_packages.experimental_v3, variant_consolidation, base_url, &repo_data.channel, @@ -467,16 +498,39 @@ struct LazyRepoData<'i> { )] conda_packages: Vec<(PackageFilename<'i>, &'i RawValue)>, - /// The wheel packages contained in the repodata.json file (under a - /// different key for backwards compatibility with previous conda - /// versions) + /// Packages stored under the `v3` top-level key. + #[serde(borrow, default, rename = "v3")] + experimental_v3: LazyV3Packages<'i>, +} + +/// Lazily parsed `v3` section of repodata containing sub-maps for each archive +/// type. +#[derive(Deserialize, Default)] +struct LazyV3Packages<'i> { + /// v3 tar.bz2 packages #[serde( borrow, default, deserialize_with = "deserialize_filename_and_raw_record", - rename = "packages.whl" + rename = "tar.bz2" + )] + tar_bz2: Vec<(PackageFilename<'i>, &'i RawValue)>, + + /// v3 conda packages + #[serde( + borrow, + default, + deserialize_with = "deserialize_filename_and_raw_record" + )] + conda: Vec<(PackageFilename<'i>, &'i RawValue)>, + + /// v3 whl packages + #[serde( + borrow, + default, + deserialize_with = "deserialize_filename_and_raw_record" )] - experimental_whl_packages: Vec<(PackageFilename<'i>, &'i RawValue)>, + whl: Vec<(PackageFilename<'i>, &'i RawValue)>, } /// Defines the type of record @@ -484,8 +538,12 @@ struct LazyRepoData<'i> { enum RecordKind { /// This is a regular `packages` or `packages.conda` record. CondaOrTarBz2, - /// This is a `packages.whl` record. - Whl, + /// This is a `v3.tar.bz2` record (extension-less key). + V3TarBz2, + /// This is a `v3.conda` record (extension-less key). + V3Conda, + /// This is a `v3.whl` record (extension-less key). + V3Whl, } /// Returns an iterator over the packages in the slice that match the given @@ -532,7 +590,7 @@ fn parse_records<'i, F: Fn(&RepoDataRecord) -> bool>( package_name: Option<&PackageName>, tar_bz2_packages: &[(PackageFilename<'i>, &'i RawValue)], conda_packages: &[(PackageFilename<'i>, &'i RawValue)], - whl_packages: &[(PackageFilename<'i>, &'i RawValue)], + v3: &LazyV3Packages<'i>, variant_consolidation: PackageFormatSelection, base_url: Option<&str>, channel: &Channel, @@ -542,22 +600,28 @@ fn parse_records<'i, F: Fn(&RepoDataRecord) -> bool>( ) -> io::Result> { match variant_consolidation { PackageFormatSelection::PreferConda => { - let tar_bz2_packages = add_stripped_filename( + let tar_bz2 = add_stripped_filename( find_package_in_slice(tar_bz2_packages, package_name, RecordKind::CondaOrTarBz2), DistArchiveType::from(CondaArchiveType::TarBz2), ); - let conda_packages = add_stripped_filename( + let v3_tar = add_stripped_filename( + find_package_in_slice(&v3.tar_bz2, package_name, RecordKind::V3TarBz2), + DistArchiveType::from(CondaArchiveType::TarBz2), + ); + let all_tar = tar_bz2.merge_by(v3_tar, |(_, _, _, a), (_, _, _, b)| a <= b); + + let conda = add_stripped_filename( find_package_in_slice(conda_packages, package_name, RecordKind::CondaOrTarBz2), DistArchiveType::from(CondaArchiveType::Conda), ); - let deduplicated_packages = conda_packages - // Merge the conda and tar.bz2 packages together based on their filename without - // extension. - .merge_by(tar_bz2_packages, |(_, _, _, left), (_, _, _, right)| { - left <= right - }) - // Deduplicate repeated packages based on their filename without extension. (this - // removes the .tar.bz2 in favor of the .conda) + let v3_conda_iter = add_stripped_filename( + find_package_in_slice(&v3.conda, package_name, RecordKind::V3Conda), + DistArchiveType::from(CondaArchiveType::Conda), + ); + let all_conda = conda.merge_by(v3_conda_iter, |(_, _, _, a), (_, _, _, b)| a <= b); + + let deduplicated_packages = all_conda + .merge_by(all_tar, |(_, _, _, left), (_, _, _, right)| left <= right) .dedup_by(|(_, _, _, left), (_, _, _, right)| left == right) .map(|(filename, raw_json, record_kind, _)| (filename, raw_json, record_kind)); parse_records_raw( @@ -570,29 +634,34 @@ fn parse_records<'i, F: Fn(&RepoDataRecord) -> bool>( ) } PackageFormatSelection::PreferCondaWithWhl => { - let tar_bz2_packages = add_stripped_filename( + let tar_bz2 = add_stripped_filename( find_package_in_slice(tar_bz2_packages, package_name, RecordKind::CondaOrTarBz2), DistArchiveType::from(CondaArchiveType::TarBz2), ); - let whl_packages = add_stripped_filename( - find_package_in_slice(whl_packages, package_name, RecordKind::Whl), + let v3_tar = add_stripped_filename( + find_package_in_slice(&v3.tar_bz2, package_name, RecordKind::V3TarBz2), + DistArchiveType::from(CondaArchiveType::TarBz2), + ); + let all_tar = tar_bz2.merge_by(v3_tar, |(_, _, _, a), (_, _, _, b)| a <= b); + + let whl = add_stripped_filename( + find_package_in_slice(&v3.whl, package_name, RecordKind::V3Whl), DistArchiveType::from(WheelArchiveType::Whl), ); - let conda_packages = add_stripped_filename( + + let conda = add_stripped_filename( find_package_in_slice(conda_packages, package_name, RecordKind::CondaOrTarBz2), DistArchiveType::from(CondaArchiveType::Conda), ); - let deduplicated_packages = conda_packages - // Merge conda, whl, and tar.bz2 packages together based on their filename without - // extension. The order ensures .conda packages override both .whl and .tar.bz2. - .merge_by(whl_packages, |(_, _, _, left), (_, _, _, right)| { - left <= right - }) - .merge_by(tar_bz2_packages, |(_, _, _, left), (_, _, _, right)| { - left <= right - }) - // Deduplicate repeated packages based on their filename without extension. - // This removes .whl and .tar.bz2 in favor of .conda packages. + let v3_conda_iter = add_stripped_filename( + find_package_in_slice(&v3.conda, package_name, RecordKind::V3Conda), + DistArchiveType::from(CondaArchiveType::Conda), + ); + let all_conda = conda.merge_by(v3_conda_iter, |(_, _, _, a), (_, _, _, b)| a <= b); + + let deduplicated_packages = all_conda + .merge_by(whl, |(_, _, _, left), (_, _, _, right)| left <= right) + .merge_by(all_tar, |(_, _, _, left), (_, _, _, right)| left <= right) .dedup_by(|(_, _, _, left), (_, _, _, right)| left == right) .map(|(filename, raw_json, kind, _)| (filename, raw_json, kind)); parse_records_raw( @@ -605,12 +674,14 @@ fn parse_records<'i, F: Fn(&RepoDataRecord) -> bool>( ) } PackageFormatSelection::Both => { - let tar_bz2_packages = + let tar_bz2 = find_package_in_slice(tar_bz2_packages, package_name, RecordKind::CondaOrTarBz2); - let conda_packages = + let conda = find_package_in_slice(conda_packages, package_name, RecordKind::CondaOrTarBz2); + let v3_tar = find_package_in_slice(&v3.tar_bz2, package_name, RecordKind::V3TarBz2); + let v3_conda_iter = find_package_in_slice(&v3.conda, package_name, RecordKind::V3Conda); parse_records_raw( - tar_bz2_packages.chain(conda_packages), + tar_bz2.chain(conda).chain(v3_tar).chain(v3_conda_iter), base_url, channel, subdir, @@ -619,10 +690,11 @@ fn parse_records<'i, F: Fn(&RepoDataRecord) -> bool>( ) } PackageFormatSelection::OnlyTarBz2 => { - let tar_bz2_packages = + let tar_bz2 = find_package_in_slice(tar_bz2_packages, package_name, RecordKind::CondaOrTarBz2); + let v3_tar = find_package_in_slice(&v3.tar_bz2, package_name, RecordKind::V3TarBz2); parse_records_raw( - tar_bz2_packages, + tar_bz2.chain(v3_tar), base_url, channel, subdir, @@ -631,10 +703,11 @@ fn parse_records<'i, F: Fn(&RepoDataRecord) -> bool>( ) } PackageFormatSelection::OnlyConda => { - let conda_packages = + let conda = find_package_in_slice(conda_packages, package_name, RecordKind::CondaOrTarBz2); + let v3_conda_iter = find_package_in_slice(&v3.conda, package_name, RecordKind::V3Conda); parse_records_raw( - conda_packages, + conda.chain(v3_conda_iter), base_url, channel, subdir, @@ -683,10 +756,47 @@ fn parse_conda_record_raw<'i>( }) } -/// Wheel and conda package records are very similar except for how the URL -/// is stored. This function parses a wheel package record from its raw json -/// representation. -fn parse_whl_record_raw<'i>( +/// Parses a v3 conda/tar.bz2 record from its raw json representation. +/// The key is an extension-less `ArchiveIdentifier`. +fn parse_v3_conda_record_raw<'i>( + filename: PackageFilename<'i>, + raw_json: &'i RawValue, + archive_type: DistArchiveType, + base_url: Option<&str>, + channel: &Channel, + channel_name: Option, + subdir: &str, +) -> io::Result { + let mut package_record: PackageRecord = serde_json::from_str(raw_json.get())?; + if package_record.subdir.is_empty() { + package_record.subdir = subdir.to_owned(); + } + let archive_id: ArchiveIdentifier = filename.filename.parse().map_err(|e| { + io::Error::new( + io::ErrorKind::InvalidData, + format!("invalid archive identifier '{}': {}", filename.filename, e), + ) + })?; + let identifier = DistArchiveIdentifier::new(archive_id, archive_type); + Ok(RepoDataRecord { + url: compute_package_url( + &channel + .base_url + .url() + .join(&format!("{subdir}/")) + .expect("failed determine repo_base_url"), + base_url, + &identifier.to_file_name(), + ), + channel: channel_name, + package_record, + identifier, + }) +} + +/// Parses a v3 whl record from its raw json representation. +/// The key is an extension-less `ArchiveIdentifier`. +fn parse_v3_whl_record_raw<'i>( filename: PackageFilename<'i>, raw_json: &'i RawValue, base_url: Option<&str>, @@ -698,16 +808,16 @@ fn parse_whl_record_raw<'i>( url, mut package_record, } = serde_json::from_str(raw_json.get())?; - // Overwrite subdir if its empty if package_record.subdir.is_empty() { package_record.subdir = subdir.to_owned(); } - let identifier: ArchiveIdentifier = filename.filename.parse().map_err(|e| { + let archive_id: ArchiveIdentifier = filename.filename.parse().map_err(|e| { io::Error::new( io::ErrorKind::InvalidData, format!("invalid archive identifier '{}': {}", filename.filename, e), ) })?; + let identifier = DistArchiveIdentifier::new(archive_id, WheelArchiveType::Whl); let url = match url { UrlOrPath::Path(path) => compute_package_url( &channel @@ -725,10 +835,7 @@ fn parse_whl_record_raw<'i>( url, channel: channel_name, package_record, - identifier: DistArchiveIdentifier { - identifier, - archive_type: DistArchiveType::Wheel(WheelArchiveType::Whl), - }, + identifier, }) } @@ -744,8 +851,26 @@ fn parse_record_raw<'i>( RecordKind::CondaOrTarBz2 => { parse_conda_record_raw(filename, raw_json, base_url, channel, channel_name, subdir)? } - RecordKind::Whl => { - parse_whl_record_raw(filename, raw_json, base_url, channel, channel_name, subdir)? + RecordKind::V3TarBz2 => parse_v3_conda_record_raw( + filename, + raw_json, + CondaArchiveType::TarBz2.into(), + base_url, + channel, + channel_name, + subdir, + )?, + RecordKind::V3Conda => parse_v3_conda_record_raw( + filename, + raw_json, + CondaArchiveType::Conda.into(), + base_url, + channel, + channel_name, + subdir, + )?, + RecordKind::V3Whl => { + parse_v3_whl_record_raw(filename, raw_json, base_url, channel, channel_name, subdir)? } }; diff --git a/py-rattler/Cargo.lock b/py-rattler/Cargo.lock index dbf7af4bf1..1a9046a6ca 100644 --- a/py-rattler/Cargo.lock +++ b/py-rattler/Cargo.lock @@ -3395,7 +3395,7 @@ dependencies = [ [[package]] name = "path_resolver" -version = "0.2.4" +version = "0.2.5" dependencies = [ "ahash", "fs-err", @@ -3973,7 +3973,7 @@ dependencies = [ [[package]] name = "rattler" -version = "0.39.12" +version = "0.39.13" dependencies = [ "anyhow", "console", @@ -4017,7 +4017,7 @@ dependencies = [ [[package]] name = "rattler_cache" -version = "0.6.11" +version = "0.6.12" dependencies = [ "ahash", "anyhow", @@ -4048,7 +4048,7 @@ dependencies = [ [[package]] name = "rattler_conda_types" -version = "0.43.2" +version = "0.43.3" dependencies = [ "ahash", "chrono", @@ -4088,7 +4088,7 @@ dependencies = [ [[package]] name = "rattler_config" -version = "0.2.26" +version = "0.3.0" dependencies = [ "console", "fs-err", @@ -4121,7 +4121,7 @@ dependencies = [ [[package]] name = "rattler_index" -version = "0.27.13" +version = "0.27.14" dependencies = [ "ahash", "anyhow", @@ -4158,7 +4158,7 @@ dependencies = [ [[package]] name = "rattler_lock" -version = "0.26.13" +version = "0.26.14" dependencies = [ "ahash", "chrono", @@ -4190,7 +4190,7 @@ dependencies = [ [[package]] name = "rattler_menuinst" -version = "0.2.46" +version = "0.2.47" dependencies = [ "chrono", "configparser", @@ -4219,7 +4219,7 @@ dependencies = [ [[package]] name = "rattler_networking" -version = "0.25.33" +version = "0.26.0" dependencies = [ "anyhow", "async-once-cell", @@ -4249,7 +4249,7 @@ dependencies = [ [[package]] name = "rattler_package_streaming" -version = "0.23.26" +version = "0.24.0" dependencies = [ "astral-tokio-tar", "astral_async_zip", @@ -4303,7 +4303,7 @@ dependencies = [ [[package]] name = "rattler_repodata_gateway" -version = "0.25.12" +version = "0.26.0" dependencies = [ "ahash", "anyhow", @@ -4365,7 +4365,7 @@ dependencies = [ [[package]] name = "rattler_s3" -version = "0.1.22" +version = "0.1.23" dependencies = [ "aws-config", "aws-credential-types", @@ -4381,7 +4381,7 @@ dependencies = [ [[package]] name = "rattler_shell" -version = "0.25.19" +version = "0.26.0" dependencies = [ "anyhow", "enum_dispatch", @@ -4399,7 +4399,7 @@ dependencies = [ [[package]] name = "rattler_solve" -version = "4.2.3" +version = "4.2.4" dependencies = [ "chrono", "futures", @@ -4416,7 +4416,7 @@ dependencies = [ [[package]] name = "rattler_virtual_packages" -version = "2.3.8" +version = "2.3.9" dependencies = [ "archspec", "libloading", diff --git a/test-data/channels/with-wheels/noarch/repodata.json b/test-data/channels/with-wheels/noarch/repodata.json index fd2b1658de..de1dfddf2d 100644 --- a/test-data/channels/with-wheels/noarch/repodata.json +++ b/test-data/channels/with-wheels/noarch/repodata.json @@ -81,981 +81,983 @@ "removed": [], "repodata_version": 3, "signatures": {}, - "packages.whl": { - "six-1.9.0-py3_none_any_0": { - "name": "six", - "version": "1.9.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [], - "fn": "six-1.9.0-py2.py3-none-any.whl", - "sha256": "e66d4bb9b165af63ba9efa967d9ab61a7a3c8349788353da711889a53b750d6d", - "size": 9897, - "subdir": "noarch", - "timestamp": 1765457000, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/10/e3/a7f8eea80a9fa8358c1cd89ef489bc03675e69e54ed2982cd6f2a28d8295/six-1.9.0-py2.py3-none-any.whl" - }, - "six-1.8.0-py3_none_any_0": { - "name": "six", - "version": "1.8.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [], - "fn": "six-1.8.0-py2.py3-none-any.whl", - "sha256": "c8fa17ac94d5ccd1b89aa25e7816e30a8815de5c50bedf173e760b8f0c1ac25f", - "size": 9373, - "subdir": "noarch", - "timestamp": 1765457000, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/a2/4b/2b4532b4eba116a02fc0b5e0b3540a073a61c003b7b6293b7b884afa8ff1/six-1.8.0-py2.py3-none-any.whl" - }, - "six-1.7.3-py3_none_any_0": { - "name": "six", - "version": "1.7.3", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [], - "fn": "six-1.7.3-py2.py3-none-any.whl", - "sha256": "b2bca436dc826d6f64371f30b0fe95706f3cabf8e2849b7f9ec468b8c7df6f20", - "size": 9191, - "subdir": "noarch", - "timestamp": 1765457000, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/2e/a4/6dcb84af409b7bc0c258a0d6bd7e14231724d9a46b750c048f09d74d870c/six-1.7.3-py2.py3-none-any.whl" - }, - "six-1.7.2-py3_none_any_0": { - "name": "six", - "version": "1.7.2", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [], - "fn": "six-1.7.2-py2.py3-none-any.whl", - "sha256": "8d82a1d6ebfeacc2ff207e33ad694eb3df0c8e1fc6623b61cd46e35bae232e3f", - "size": 9302, - "subdir": "noarch", - "timestamp": 1765457000, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/6b/30/839b8059d1225dbd3a0dd97c3c370bbb978cde505e160b30515fb7be17d8/six-1.7.2-py2.py3-none-any.whl" - }, - "six-1.7.1-py3_none_any_0": { - "name": "six", - "version": "1.7.1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [], - "fn": "six-1.7.1-py2.py3-none-any.whl", - "sha256": "72a0c3321753a3ee454fca39229e3a7093041f34d2db8f409b6ff1ddb9376b88", - "size": 9297, - "subdir": "noarch", - "timestamp": 1765456999, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/aa/ec/e6954e19e617c1b9860325f0995d456d8416c84a6cb9bce1c10998280b59/six-1.7.1-py2.py3-none-any.whl" - }, - "six-1.7.0-py3_none_any_0": { - "name": "six", - "version": "1.7.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [], - "fn": "six-1.7.0-py2.py3-none-any.whl", - "sha256": "c8ccce78ddb6592760d57789477860924aeacb54401fa5e072a30ba1f9da6a73", - "size": 9036, - "subdir": "noarch", - "timestamp": 1765456999, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/20/0a/617bf23511160454995bf1cbe6789f22beeb8b49c1f3b1bbf4b5cd6a55ca/six-1.7.0-py2.py3-none-any.whl" - }, - "six-1.6.1-py3_none_any_0": { - "name": "six", - "version": "1.6.1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [], - "fn": "six-1.6.1-py2.py3-none-any.whl", - "sha256": "28ee0c506d16a08f2bef34c19f64c72b4227f47cc1f91b7b405d099cc43335fd", - "size": 8252, - "subdir": "noarch", - "timestamp": 1765456999, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/ba/19/aa48edcff9d0c396a4d009d362d0a0a1ac3db6f9d7d5736e0175b94d7ef8/six-1.6.1-py2.py3-none-any.whl" - }, - "six-1.6.0-py3_none_any_0": { - "name": "six", - "version": "1.6.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [], - "fn": "six-1.6.0-py2.py3-none-any.whl", - "sha256": "e753c729fc683ac3d55f8f38c146b4048f0c6679b4aece0cb3eaff1d9ce74cc4", - "size": 8206, - "subdir": "noarch", - "timestamp": 1765456999, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/54/45/8b5eef1ba0b79acbdab01435359f37ae13dc6c4aafaa444b805e3867386d/six-1.6.0-py2.py3-none-any.whl" - }, - "six-1.5.2-py3_none_any_0": { - "name": "six", - "version": "1.5.2", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [], - "fn": "six-1.5.2-py2.py3-none-any.whl", - "sha256": "5d6341c82cb575414c6bc86cc8fec8e7a21b89695f35c8599517a6e7e2369ab9", - "size": 8123, - "subdir": "noarch", - "timestamp": 1765456999, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/c7/a4/35520d20a8e4b3c28c9db705fffd4c7053e0236928951da32167e5078faa/six-1.5.2-py2.py3-none-any.whl" - }, - "six-1.5.1-py3_none_any_0": { - "name": "six", - "version": "1.5.1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [], - "fn": "six-1.5.1-py2.py3-none-any.whl", - "sha256": "951ddd8401562dad2f208be1c86f230936aebba028dffaf61f0c4b57fb21a662", - "size": 8095, - "subdir": "noarch", - "timestamp": 1765456998, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/60/84/72c628d5a4efffb23e2fb46cdbf8ee669046d8208ba5dab08f989b7bfe9c/six-1.5.1-py2.py3-none-any.whl" - }, - "six-1.5.0-py3_none_any_0": { - "name": "six", - "version": "1.5.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [], - "fn": "six-1.5.0-py2.py3-none-any.whl", - "sha256": "ae880000fcfc71aa65385afc2c6b6bc3a5a36b0e893f8db0d296d7efd0f0ac7a", - "size": 7886, - "subdir": "noarch", - "timestamp": 1765456998, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/ca/15/e62a66024dded9640f8d39112330e6b108e7ab21a1b8618c2d1e2eea7e1d/six-1.5.0-py2.py3-none-any.whl" - }, - "six-1.17.0-py3_none_any_0": { - "name": "six", - "version": "1.17.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python >=2.7, !=3.0.*, !=3.1.*, !=3.2.*" - ], - "fn": "six-1.17.0-py2.py3-none-any.whl", - "sha256": "315aa209a1eb65c989611a7062280799bff19941b9818f2d0c2985baf1e7eafc", - "size": 11130, - "subdir": "noarch", - "timestamp": 1765456998, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl" - }, - "six-1.16.0-py3_none_any_0": { - "name": "six", - "version": "1.16.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python >=2.7, !=3.0.*, !=3.1.*, !=3.2.*" - ], - "fn": "six-1.16.0-py2.py3-none-any.whl", - "sha256": "b9bc4f7dfc61c5e40a4e2de2811b114e9210a97125fcef8d8a175c1f9ce1436f", - "size": 11125, - "subdir": "noarch", - "timestamp": 1765456998, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl" - }, - "six-1.13.0-py3_none_any_0": { - "name": "six", - "version": "1.13.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python >=2.6, !=3.0.*, !=3.1.*" - ], - "fn": "six-1.13.0-py2.py3-none-any.whl", - "sha256": "f91c6df4d02a12f493455ada1c0fb4f11cf393e3662a643fc45c6fc4b937b13f", - "size": 10815, - "subdir": "noarch", - "timestamp": 1765456997, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/65/26/32b8464df2a97e6dd1b656ed26b2c194606c16fe163c695a992b36c11cdf/six-1.13.0-py2.py3-none-any.whl" - }, - "six-1.15.0-py3_none_any_0": { - "name": "six", - "version": "1.15.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python >=2.7, !=3.0.*, !=3.1.*, !=3.2.*" - ], - "fn": "six-1.15.0-py2.py3-none-any.whl", - "sha256": "397a9df0830707ee73f8bfc409cf34ceae880cc798345cfed2fc54c760fc10bd", - "size": 11036, - "subdir": "noarch", - "timestamp": 1765456997, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/ee/ff/48bde5c0f013094d729fe4b0316ba2a24774b3ff1c52d924a8a4cb04078a/six-1.15.0-py2.py3-none-any.whl" - }, - "six-1.14.0-py3_none_any_0": { - "name": "six", - "version": "1.14.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python >=2.7, !=3.0.*, !=3.1.*, !=3.2.*" - ], - "fn": "six-1.14.0-py2.py3-none-any.whl", - "sha256": "395b91a5a70015fc476d34d4e8490a0c116488d27336ae8aee641a697c783587", - "size": 11008, - "subdir": "noarch", - "timestamp": 1765456997, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/65/eb/1f97cb97bfc2390a276969c6fae16075da282f5058082d4cb10c6c5c1dba/six-1.14.0-py2.py3-none-any.whl" - }, - "six-1.10.0-py3_none_any_0": { - "name": "six", - "version": "1.10.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [], - "fn": "six-1.10.0-py2.py3-none-any.whl", - "sha256": "3323a0e4ac455ea08cfb21e19c283450bd77f99dfdef3dd58c5bb3b9fa532555", - "size": 10012, - "subdir": "noarch", - "timestamp": 1765456997, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/c8/0a/b6723e1bc4c516cb687841499455a8505b44607ab535be01091c0f24f079/six-1.10.0-py2.py3-none-any.whl" - }, - "six-1.12.0-py3_none_any_0": { - "name": "six", - "version": "1.12.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python >=2.6, !=3.0.*, !=3.1.*" - ], - "fn": "six-1.12.0-py2.py3-none-any.whl", - "sha256": "93e7180169333b5af1e346cb66e8b76d5123474cc1d0afa1dc50ce665fc789fc", - "size": 10655, - "subdir": "noarch", - "timestamp": 1765456997, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/73/fb/00a976f728d0d1fecfe898238ce23f502a721c0ac0ecfedb80e0d88c64e9/six-1.12.0-py2.py3-none-any.whl" - }, - "six-1.11.0-py3_none_any_0": { - "name": "six", - "version": "1.11.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [], - "fn": "six-1.11.0-py2.py3-none-any.whl", - "sha256": "5d081fc97ae89bbe5ade5042bcb633e12f0d24d02d733e7f0278e24852a9e0c8", - "size": 10238, - "subdir": "noarch", - "timestamp": 1765456996, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/67/4b/141a581104b1f6397bfa78ac9d43d8ad29a7ca43ea90a2d863fe3056e86a/six-1.11.0-py2.py3-none-any.whl" - }, - "starlette-0.47.2-py3_none_any_0": { - "record_version": 3, - "name": "starlette", - "version": "0.47.2", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "typing_extensions >=4.10.0", - "python >=3.9" - ], - "fn": "starlette-0.47.2-py3-none-any.whl", - "sha256": "eed9895bba5c1d7f63f6be007c567c288893e791fd865a9ecfa536e89812bf81", - "size": 4412, - "subdir": "noarch", - "timestamp": 1756405214, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/67/4b/895bba5c1d7f63f6be007c567c288893e791fd865a9ecfa536e89812bf81/starlette-0.47.2-py3-none-any.whl" - }, - "typing_extensions-4.14.1-py3_none_any_0": { - "record_version": 3, - "name": "typing_extensions", - "version": "4.14.1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python >=3.9" - ], - "fn": "typing_extensions-4.14.1-py3-none-any.whl", - "sha256": "eefe7d0f85db6c44f9726bbd4c4a540157ea968667fba03037d5a3f120eb8946", - "size": 6741, - "subdir": "noarch", - "timestamp": 1756405213, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/67/4b/895bba5c1d7f63f6be007c567c288893e791fd865a9ecfa536e89812bf81/typing_extensions-4.14.1-py3-none-any.whl" - }, - "unearth-0.17.5-py3_none_any_0": { - "name": "unearth", - "version": "0.17.5", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "packaging >=20", - "httpx <1,>=0.27.0", - "python >=3.8" - ], - "fn": "unearth-0.17.5-py3-none-any.whl", - "sha256": "2fa0375963237751ae83f5306eb0b49574a59d20884946a9af554d89728e1db4", - "size": 47380, - "subdir": "noarch", - "timestamp": 1765456996, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/73/35/b22322e3e69be95cfe6fb9863d9f839d360e8348eb4ec2ea09d7dfc047f8/unearth-0.17.5-py3-none-any.whl", - "extra_depends": { - "keyring": [ - "keyring" + "v3": { + "whl": { + "six-1.9.0-py3_none_any_0": { + "name": "six", + "version": "1.9.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [], + "fn": "six-1.9.0-py2.py3-none-any.whl", + "sha256": "e66d4bb9b165af63ba9efa967d9ab61a7a3c8349788353da711889a53b750d6d", + "size": 9897, + "subdir": "noarch", + "timestamp": 1765457000, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/10/e3/a7f8eea80a9fa8358c1cd89ef489bc03675e69e54ed2982cd6f2a28d8295/six-1.9.0-py2.py3-none-any.whl" + }, + "six-1.8.0-py3_none_any_0": { + "name": "six", + "version": "1.8.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [], + "fn": "six-1.8.0-py2.py3-none-any.whl", + "sha256": "c8fa17ac94d5ccd1b89aa25e7816e30a8815de5c50bedf173e760b8f0c1ac25f", + "size": 9373, + "subdir": "noarch", + "timestamp": 1765457000, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/a2/4b/2b4532b4eba116a02fc0b5e0b3540a073a61c003b7b6293b7b884afa8ff1/six-1.8.0-py2.py3-none-any.whl" + }, + "six-1.7.3-py3_none_any_0": { + "name": "six", + "version": "1.7.3", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [], + "fn": "six-1.7.3-py2.py3-none-any.whl", + "sha256": "b2bca436dc826d6f64371f30b0fe95706f3cabf8e2849b7f9ec468b8c7df6f20", + "size": 9191, + "subdir": "noarch", + "timestamp": 1765457000, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/2e/a4/6dcb84af409b7bc0c258a0d6bd7e14231724d9a46b750c048f09d74d870c/six-1.7.3-py2.py3-none-any.whl" + }, + "six-1.7.2-py3_none_any_0": { + "name": "six", + "version": "1.7.2", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [], + "fn": "six-1.7.2-py2.py3-none-any.whl", + "sha256": "8d82a1d6ebfeacc2ff207e33ad694eb3df0c8e1fc6623b61cd46e35bae232e3f", + "size": 9302, + "subdir": "noarch", + "timestamp": 1765457000, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/6b/30/839b8059d1225dbd3a0dd97c3c370bbb978cde505e160b30515fb7be17d8/six-1.7.2-py2.py3-none-any.whl" + }, + "six-1.7.1-py3_none_any_0": { + "name": "six", + "version": "1.7.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [], + "fn": "six-1.7.1-py2.py3-none-any.whl", + "sha256": "72a0c3321753a3ee454fca39229e3a7093041f34d2db8f409b6ff1ddb9376b88", + "size": 9297, + "subdir": "noarch", + "timestamp": 1765456999, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/aa/ec/e6954e19e617c1b9860325f0995d456d8416c84a6cb9bce1c10998280b59/six-1.7.1-py2.py3-none-any.whl" + }, + "six-1.7.0-py3_none_any_0": { + "name": "six", + "version": "1.7.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [], + "fn": "six-1.7.0-py2.py3-none-any.whl", + "sha256": "c8ccce78ddb6592760d57789477860924aeacb54401fa5e072a30ba1f9da6a73", + "size": 9036, + "subdir": "noarch", + "timestamp": 1765456999, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/20/0a/617bf23511160454995bf1cbe6789f22beeb8b49c1f3b1bbf4b5cd6a55ca/six-1.7.0-py2.py3-none-any.whl" + }, + "six-1.6.1-py3_none_any_0": { + "name": "six", + "version": "1.6.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [], + "fn": "six-1.6.1-py2.py3-none-any.whl", + "sha256": "28ee0c506d16a08f2bef34c19f64c72b4227f47cc1f91b7b405d099cc43335fd", + "size": 8252, + "subdir": "noarch", + "timestamp": 1765456999, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/ba/19/aa48edcff9d0c396a4d009d362d0a0a1ac3db6f9d7d5736e0175b94d7ef8/six-1.6.1-py2.py3-none-any.whl" + }, + "six-1.6.0-py3_none_any_0": { + "name": "six", + "version": "1.6.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [], + "fn": "six-1.6.0-py2.py3-none-any.whl", + "sha256": "e753c729fc683ac3d55f8f38c146b4048f0c6679b4aece0cb3eaff1d9ce74cc4", + "size": 8206, + "subdir": "noarch", + "timestamp": 1765456999, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/54/45/8b5eef1ba0b79acbdab01435359f37ae13dc6c4aafaa444b805e3867386d/six-1.6.0-py2.py3-none-any.whl" + }, + "six-1.5.2-py3_none_any_0": { + "name": "six", + "version": "1.5.2", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [], + "fn": "six-1.5.2-py2.py3-none-any.whl", + "sha256": "5d6341c82cb575414c6bc86cc8fec8e7a21b89695f35c8599517a6e7e2369ab9", + "size": 8123, + "subdir": "noarch", + "timestamp": 1765456999, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/c7/a4/35520d20a8e4b3c28c9db705fffd4c7053e0236928951da32167e5078faa/six-1.5.2-py2.py3-none-any.whl" + }, + "six-1.5.1-py3_none_any_0": { + "name": "six", + "version": "1.5.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [], + "fn": "six-1.5.1-py2.py3-none-any.whl", + "sha256": "951ddd8401562dad2f208be1c86f230936aebba028dffaf61f0c4b57fb21a662", + "size": 8095, + "subdir": "noarch", + "timestamp": 1765456998, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/60/84/72c628d5a4efffb23e2fb46cdbf8ee669046d8208ba5dab08f989b7bfe9c/six-1.5.1-py2.py3-none-any.whl" + }, + "six-1.5.0-py3_none_any_0": { + "name": "six", + "version": "1.5.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [], + "fn": "six-1.5.0-py2.py3-none-any.whl", + "sha256": "ae880000fcfc71aa65385afc2c6b6bc3a5a36b0e893f8db0d296d7efd0f0ac7a", + "size": 7886, + "subdir": "noarch", + "timestamp": 1765456998, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/ca/15/e62a66024dded9640f8d39112330e6b108e7ab21a1b8618c2d1e2eea7e1d/six-1.5.0-py2.py3-none-any.whl" + }, + "six-1.17.0-py3_none_any_0": { + "name": "six", + "version": "1.17.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python >=2.7, !=3.0.*, !=3.1.*, !=3.2.*" ], - "legacy": [ - "requests >=2.25" - ] - } - }, - "unearth-0.17.4-py3_none_any_0": { - "name": "unearth", - "version": "0.17.4", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "packaging >=20", - "httpx <1,>=0.27.0", - "python >=3.8" - ], - "fn": "unearth-0.17.4-py3-none-any.whl", - "sha256": "3c5a4673ea297191fb23dc59a68095b8f6367604cf1dc0c86396abaa444d69bb", - "size": 47440, - "subdir": "noarch", - "timestamp": 1765456996, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/41/22/7aef7df40cef0aee5700ac1576644e4e919477119f580b96a7d11d21bc2a/unearth-0.17.4-py3-none-any.whl", - "extra_depends": { - "keyring": [ - "keyring" + "fn": "six-1.17.0-py2.py3-none-any.whl", + "sha256": "315aa209a1eb65c989611a7062280799bff19941b9818f2d0c2985baf1e7eafc", + "size": 11130, + "subdir": "noarch", + "timestamp": 1765456998, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl" + }, + "six-1.16.0-py3_none_any_0": { + "name": "six", + "version": "1.16.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python >=2.7, !=3.0.*, !=3.1.*, !=3.2.*" ], - "legacy": [ - "requests >=2.25" - ] - } - }, - "unearth-0.17.3-py3_none_any_0": { - "name": "unearth", - "version": "0.17.3", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "packaging >=20", - "httpx <1,>=0.27.0", - "python >=3.8" - ], - "fn": "unearth-0.17.3-py3-none-any.whl", - "sha256": "7198669a82a4e4fceb570ca449052fa0698e64a254a2a07ef434a5c2953ea27e", - "size": 47467, - "subdir": "noarch", - "timestamp": 1765456995, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/79/e8/79bdd5b251b06accb1fc42eb996a6a80e87e24155c21c5b1a5edc7032244/unearth-0.17.3-py3-none-any.whl", - "extra_depends": { - "keyring": [ - "keyring" + "fn": "six-1.16.0-py2.py3-none-any.whl", + "sha256": "b9bc4f7dfc61c5e40a4e2de2811b114e9210a97125fcef8d8a175c1f9ce1436f", + "size": 11125, + "subdir": "noarch", + "timestamp": 1765456998, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl" + }, + "six-1.13.0-py3_none_any_0": { + "name": "six", + "version": "1.13.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python >=2.6, !=3.0.*, !=3.1.*" ], - "legacy": [ - "requests >=2.25" - ] - } - }, - "unearth-0.17.0-py3_none_any_0": { - "name": "unearth", - "version": "0.17.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "packaging >=20", - "httpx <1,>=0.27.0", - "python >=3.8" - ], - "fn": "unearth-0.17.0-py3-none-any.whl", - "sha256": "1d04226b4e7d9bda3571e37c9602dcdb607817f95e6a4215e12d0a3a13308b5a", - "size": 47306, - "subdir": "noarch", - "timestamp": 1765456995, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/03/c5/6c168746280d035e0eb511e20ee8256f11353bc3521ba92d36b27e347a71/unearth-0.17.0-py3-none-any.whl", - "extra_depends": { - "keyring": [ - "keyring" + "fn": "six-1.13.0-py2.py3-none-any.whl", + "sha256": "f91c6df4d02a12f493455ada1c0fb4f11cf393e3662a643fc45c6fc4b937b13f", + "size": 10815, + "subdir": "noarch", + "timestamp": 1765456997, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/65/26/32b8464df2a97e6dd1b656ed26b2c194606c16fe163c695a992b36c11cdf/six-1.13.0-py2.py3-none-any.whl" + }, + "six-1.15.0-py3_none_any_0": { + "name": "six", + "version": "1.15.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python >=2.7, !=3.0.*, !=3.1.*, !=3.2.*" ], - "legacy": [ - "requests >=2.25" - ] - } - }, - "unearth-0.17.2-py3_none_any_0": { - "name": "unearth", - "version": "0.17.2", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "packaging >=20", - "httpx <1,>=0.27.0", - "python >=3.8" - ], - "fn": "unearth-0.17.2-py3-none-any.whl", - "sha256": "ef247e44969bbcd3fa3a212946f2c7cbf3817bdbb061f22b3787d3cb17734590", - "size": 47377, - "subdir": "noarch", - "timestamp": 1765456995, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/40/2f/42e35e6826a8e941979a5a79517d6e15008140eaffe33a8c85c7dc336f6e/unearth-0.17.2-py3-none-any.whl", - "extra_depends": { - "keyring": [ - "keyring" + "fn": "six-1.15.0-py2.py3-none-any.whl", + "sha256": "397a9df0830707ee73f8bfc409cf34ceae880cc798345cfed2fc54c760fc10bd", + "size": 11036, + "subdir": "noarch", + "timestamp": 1765456997, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/ee/ff/48bde5c0f013094d729fe4b0316ba2a24774b3ff1c52d924a8a4cb04078a/six-1.15.0-py2.py3-none-any.whl" + }, + "six-1.14.0-py3_none_any_0": { + "name": "six", + "version": "1.14.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python >=2.7, !=3.0.*, !=3.1.*, !=3.2.*" ], - "legacy": [ - "requests >=2.25" - ] - } - }, - "unearth-0.17.1-py3_none_any_0": { - "name": "unearth", - "version": "0.17.1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "packaging >=20", - "httpx <1,>=0.27.0", - "python >=3.8" - ], - "fn": "unearth-0.17.1-py3-none-any.whl", - "sha256": "9731930f752a4c41a7dcb3bdc739c200b022e718890e563887145a471529ee16", - "size": 47354, - "subdir": "noarch", - "timestamp": 1765456995, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/9b/96/43d4bb32f4bd7c2e0b22ca67b1d23d4b87219a3c69524033a7c95ca73f47/unearth-0.17.1-py3-none-any.whl", - "extra_depends": { - "keyring": [ - "keyring" + "fn": "six-1.14.0-py2.py3-none-any.whl", + "sha256": "395b91a5a70015fc476d34d4e8490a0c116488d27336ae8aee641a697c783587", + "size": 11008, + "subdir": "noarch", + "timestamp": 1765456997, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/65/eb/1f97cb97bfc2390a276969c6fae16075da282f5058082d4cb10c6c5c1dba/six-1.14.0-py2.py3-none-any.whl" + }, + "six-1.10.0-py3_none_any_0": { + "name": "six", + "version": "1.10.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [], + "fn": "six-1.10.0-py2.py3-none-any.whl", + "sha256": "3323a0e4ac455ea08cfb21e19c283450bd77f99dfdef3dd58c5bb3b9fa532555", + "size": 10012, + "subdir": "noarch", + "timestamp": 1765456997, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/c8/0a/b6723e1bc4c516cb687841499455a8505b44607ab535be01091c0f24f079/six-1.10.0-py2.py3-none-any.whl" + }, + "six-1.12.0-py3_none_any_0": { + "name": "six", + "version": "1.12.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python >=2.6, !=3.0.*, !=3.1.*" ], - "legacy": [ - "requests >=2.25" - ] - } - }, - "requests-2.32.2-py3_none_any_0": { - "name": "requests", - "version": "2.32.2", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "charset-normalizer <4,>=2", - "idna <4,>=2.5", - "urllib3 <3,>=1.21.1", - "certifi >=2017.4.17", - "python >=3.8" - ], - "fn": "requests-2.32.2-py3-none-any.whl", - "sha256": "0375f9273b827542a3a03675feb1a4bcd8c064963dabf7b3fd6cfb761e4f7afd", - "size": 63998, - "subdir": "noarch", - "timestamp": 1765456754, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/c3/20/748e38b466e0819491f0ce6e90ebe4184966ee304fe483e2c414b0f4ef07/requests-2.32.2-py3-none-any.whl", - "extra_depends": { - "socks": [ - "pysocks !=1.5.7,>=1.5.6" + "fn": "six-1.12.0-py2.py3-none-any.whl", + "sha256": "93e7180169333b5af1e346cb66e8b76d5123474cc1d0afa1dc50ce665fc789fc", + "size": 10655, + "subdir": "noarch", + "timestamp": 1765456997, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/73/fb/00a976f728d0d1fecfe898238ce23f502a721c0ac0ecfedb80e0d88c64e9/six-1.12.0-py2.py3-none-any.whl" + }, + "six-1.11.0-py3_none_any_0": { + "name": "six", + "version": "1.11.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [], + "fn": "six-1.11.0-py2.py3-none-any.whl", + "sha256": "5d081fc97ae89bbe5ade5042bcb633e12f0d24d02d733e7f0278e24852a9e0c8", + "size": 10238, + "subdir": "noarch", + "timestamp": 1765456996, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/67/4b/141a581104b1f6397bfa78ac9d43d8ad29a7ca43ea90a2d863fe3056e86a/six-1.11.0-py2.py3-none-any.whl" + }, + "starlette-0.47.2-py3_none_any_0": { + "record_version": 3, + "name": "starlette", + "version": "0.47.2", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "typing_extensions >=4.10.0", + "python >=3.9" ], - "use-chardet-on-py3": [ - "chardet <6,>=3.0.2" - ] - } - }, - "requests-2.32.1-py3_none_any_0": { - "name": "requests", - "version": "2.32.1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "charset-normalizer <4,>=2", - "idna <4,>=2.5", - "urllib3 <3,>=1.21.1", - "certifi >=2017.4.17", - "python >=3.8" - ], - "fn": "requests-2.32.1-py3-none-any.whl", - "sha256": "d58df96a8a48ef92c2ff1e3e16dd2b043a1c4955305bf952be6741a2c8328dd8", - "size": 63796, - "subdir": "noarch", - "timestamp": 1765456754, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/9c/a6/b9bf71eeb6dc835311b5c47a7c90df57b08061091161639611252257768d/requests-2.32.1-py3-none-any.whl", - "extra_depends": { - "socks": [ - "pysocks !=1.5.7,>=1.5.6" + "fn": "starlette-0.47.2-py3-none-any.whl", + "sha256": "eed9895bba5c1d7f63f6be007c567c288893e791fd865a9ecfa536e89812bf81", + "size": 4412, + "subdir": "noarch", + "timestamp": 1756405214, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/67/4b/895bba5c1d7f63f6be007c567c288893e791fd865a9ecfa536e89812bf81/starlette-0.47.2-py3-none-any.whl" + }, + "typing_extensions-4.14.1-py3_none_any_0": { + "record_version": 3, + "name": "typing_extensions", + "version": "4.14.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python >=3.9" ], - "use-chardet-on-py3": [ - "chardet <6,>=3.0.2" - ] - } - }, - "requests-2.32.0-py3_none_any_0": { - "name": "requests", - "version": "2.32.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "charset-normalizer <4,>=2", - "idna <4,>=2.5", - "urllib3 <3,>=1.21.1", - "certifi >=2017.4.17", - "python >=3.8" - ], - "fn": "requests-2.32.0-py3-none-any.whl", - "sha256": "02c4b63dff20397729229c280ee4a6d6da1ea1ad917fc2f384bc155c2b013f6a", - "size": 63791, - "subdir": "noarch", - "timestamp": 1765456754, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/24/e8/09e8d662a9675a4e4f5dd7a8e6127b463a091d2703ed931a64aa66d00065/requests-2.32.0-py3-none-any.whl", - "extra_depends": { - "socks": [ - "pysocks !=1.5.7,>=1.5.6" + "fn": "typing_extensions-4.14.1-py3-none-any.whl", + "sha256": "eefe7d0f85db6c44f9726bbd4c4a540157ea968667fba03037d5a3f120eb8946", + "size": 6741, + "subdir": "noarch", + "timestamp": 1756405213, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/67/4b/895bba5c1d7f63f6be007c567c288893e791fd865a9ecfa536e89812bf81/typing_extensions-4.14.1-py3-none-any.whl" + }, + "unearth-0.17.5-py3_none_any_0": { + "name": "unearth", + "version": "0.17.5", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "packaging >=20", + "httpx <1,>=0.27.0", + "python >=3.8" ], - "use-chardet-on-py3": [ - "chardet <6,>=3.0.2" - ] - } - }, - "requests-2.31.0-py3_none_any_0": { - "name": "requests", - "version": "2.31.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "charset-normalizer <4,>=2", - "idna <4,>=2.5", - "urllib3 <3,>=1.21.1", - "certifi >=2017.4.17", - "python >=3.7" - ], - "fn": "requests-2.31.0-py3-none-any.whl", - "sha256": "1f816aa80bb0acdcab5940891bfab0b6ce869d9457e60e5dec48cb32b5c022ff", - "size": 62654, - "subdir": "noarch", - "timestamp": 1765456754, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl", - "extra_depends": { - "socks": [ - "pysocks !=1.5.7,>=1.5.6" + "fn": "unearth-0.17.5-py3-none-any.whl", + "sha256": "2fa0375963237751ae83f5306eb0b49574a59d20884946a9af554d89728e1db4", + "size": 47380, + "subdir": "noarch", + "timestamp": 1765456996, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/73/35/b22322e3e69be95cfe6fb9863d9f839d360e8348eb4ec2ea09d7dfc047f8/unearth-0.17.5-py3-none-any.whl", + "extra_depends": { + "keyring": [ + "keyring" + ], + "legacy": [ + "requests >=2.25" + ] + } + }, + "unearth-0.17.4-py3_none_any_0": { + "name": "unearth", + "version": "0.17.4", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "packaging >=20", + "httpx <1,>=0.27.0", + "python >=3.8" ], - "use-chardet-on-py3": [ - "chardet <6,>=3.0.2" - ] - } - }, - "requests-2.28.2-py3_none_any_0": { - "name": "requests", - "version": "2.28.2", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "charset-normalizer <4,>=2", - "idna <4,>=2.5", - "urllib3 <1.27,>=1.21.1", - "certifi >=2017.4.17", - "python >=3.7, <4" - ], - "fn": "requests-2.28.2-py3-none-any.whl", - "sha256": "a83b301e2604f39dee727cb96197ee5aa58d484dacc000a36526dcf85b12f3f4", - "size": 62913, - "subdir": "noarch", - "timestamp": 1765456753, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/d2/f4/274d1dbe96b41cf4e0efb70cbced278ffd61b5c7bb70338b62af94ccb25b/requests-2.28.2-py3-none-any.whl", - "extra_depends": { - "socks": [ - "pysocks !=1.5.7,>=1.5.6" + "fn": "unearth-0.17.4-py3-none-any.whl", + "sha256": "3c5a4673ea297191fb23dc59a68095b8f6367604cf1dc0c86396abaa444d69bb", + "size": 47440, + "subdir": "noarch", + "timestamp": 1765456996, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/41/22/7aef7df40cef0aee5700ac1576644e4e919477119f580b96a7d11d21bc2a/unearth-0.17.4-py3-none-any.whl", + "extra_depends": { + "keyring": [ + "keyring" + ], + "legacy": [ + "requests >=2.25" + ] + } + }, + "unearth-0.17.3-py3_none_any_0": { + "name": "unearth", + "version": "0.17.3", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "packaging >=20", + "httpx <1,>=0.27.0", + "python >=3.8" ], - "use-chardet-on-py3": [ - "chardet <6,>=3.0.2" - ] - } - }, - "requests-2.30.0-py3_none_any_0": { - "name": "requests", - "version": "2.30.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "charset-normalizer <4,>=2", - "idna <4,>=2.5", - "urllib3 <3,>=1.21.1", - "certifi >=2017.4.17", - "python >=3.7" - ], - "fn": "requests-2.30.0-py3-none-any.whl", - "sha256": "0c0d432d769c2c63e66f91c4f7a7f331458f3d2dd40796ddd61b03372857d608", - "size": 62579, - "subdir": "noarch", - "timestamp": 1765456753, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/96/80/034ffeca15c0f4e01b7b9c6ad0fb704b44e190cde4e757edbd60be404c41/requests-2.30.0-py3-none-any.whl", - "extra_depends": { - "socks": [ - "pysocks !=1.5.7,>=1.5.6" + "fn": "unearth-0.17.3-py3-none-any.whl", + "sha256": "7198669a82a4e4fceb570ca449052fa0698e64a254a2a07ef434a5c2953ea27e", + "size": 47467, + "subdir": "noarch", + "timestamp": 1765456995, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/79/e8/79bdd5b251b06accb1fc42eb996a6a80e87e24155c21c5b1a5edc7032244/unearth-0.17.3-py3-none-any.whl", + "extra_depends": { + "keyring": [ + "keyring" + ], + "legacy": [ + "requests >=2.25" + ] + } + }, + "unearth-0.17.0-py3_none_any_0": { + "name": "unearth", + "version": "0.17.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "packaging >=20", + "httpx <1,>=0.27.0", + "python >=3.8" ], - "use-chardet-on-py3": [ - "chardet <6,>=3.0.2" - ] - } - }, - "requests-2.29.0-py3_none_any_0": { - "name": "requests", - "version": "2.29.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "charset-normalizer <4,>=2", - "idna <4,>=2.5", - "urllib3 <1.27,>=1.21.1", - "certifi >=2017.4.17", - "python >=3.7" - ], - "fn": "requests-2.29.0-py3-none-any.whl", - "sha256": "d5d4296ec2383866e983bab45ca0b587a615b4a84cd9714c14e102acfa2fd51e", - "size": 62588, - "subdir": "noarch", - "timestamp": 1765456753, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/cf/e1/2aa539876d9ed0ddc95882451deb57cfd7aa8dbf0b8dbce68e045549ba56/requests-2.29.0-py3-none-any.whl", - "extra_depends": { - "socks": [ - "pysocks !=1.5.7,>=1.5.6" + "fn": "unearth-0.17.0-py3-none-any.whl", + "sha256": "1d04226b4e7d9bda3571e37c9602dcdb607817f95e6a4215e12d0a3a13308b5a", + "size": 47306, + "subdir": "noarch", + "timestamp": 1765456995, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/03/c5/6c168746280d035e0eb511e20ee8256f11353bc3521ba92d36b27e347a71/unearth-0.17.0-py3-none-any.whl", + "extra_depends": { + "keyring": [ + "keyring" + ], + "legacy": [ + "requests >=2.25" + ] + } + }, + "unearth-0.17.2-py3_none_any_0": { + "name": "unearth", + "version": "0.17.2", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "packaging >=20", + "httpx <1,>=0.27.0", + "python >=3.8" ], - "use-chardet-on-py3": [ - "chardet <6,>=3.0.2" - ] - } - }, - "requests-2.28.1-py3_none_any_0": { - "name": "requests", - "version": "2.28.1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "charset-normalizer <3,>=2", - "idna <4,>=2.5", - "urllib3 <1.27,>=1.21.1", - "certifi >=2017.4.17", - "python >=3.7, <4" - ], - "fn": "requests-2.28.1-py3-none-any.whl", - "sha256": "153827e5b8ac656356646d31f4d1398b560776e38ef84b7f1a868be75f40117b", - "size": 62921, - "subdir": "noarch", - "timestamp": 1765456752, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/ca/91/6d9b8ccacd0412c08820f72cebaa4f0c0441b5cda699c90f618b6f8a1b42/requests-2.28.1-py3-none-any.whl", - "extra_depends": { - "socks": [ - "pysocks !=1.5.7,>=1.5.6" + "fn": "unearth-0.17.2-py3-none-any.whl", + "sha256": "ef247e44969bbcd3fa3a212946f2c7cbf3817bdbb061f22b3787d3cb17734590", + "size": 47377, + "subdir": "noarch", + "timestamp": 1765456995, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/40/2f/42e35e6826a8e941979a5a79517d6e15008140eaffe33a8c85c7dc336f6e/unearth-0.17.2-py3-none-any.whl", + "extra_depends": { + "keyring": [ + "keyring" + ], + "legacy": [ + "requests >=2.25" + ] + } + }, + "unearth-0.17.1-py3_none_any_0": { + "name": "unearth", + "version": "0.17.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "packaging >=20", + "httpx <1,>=0.27.0", + "python >=3.8" ], - "use-chardet-on-py3": [ - "chardet <6,>=3.0.2" - ] - } - }, - "requests-2.28.0-py3_none_any_0": { - "name": "requests", - "version": "2.28.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "charset-normalizer ~=2.0.0", - "idna <4,>=2.5", - "urllib3 <1.27,>=1.21.1", - "certifi >=2017.4.17", - "python >=3.7, <4" - ], - "fn": "requests-2.28.0-py3-none-any.whl", - "sha256": "f446cb09580c793a5b6d497bf446236b67f9daae9d343792c7333878fa1ec25d", - "size": 62929, - "subdir": "noarch", - "timestamp": 1765456752, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/41/5b/2209eba8133fc081d3ffff02e1f6376e3117e52bb16f674721a83e67e68e/requests-2.28.0-py3-none-any.whl", - "extra_depends": { - "socks": [ - "pysocks !=1.5.7,>=1.5.6" + "fn": "unearth-0.17.1-py3-none-any.whl", + "sha256": "9731930f752a4c41a7dcb3bdc739c200b022e718890e563887145a471529ee16", + "size": 47354, + "subdir": "noarch", + "timestamp": 1765456995, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/9b/96/43d4bb32f4bd7c2e0b22ca67b1d23d4b87219a3c69524033a7c95ca73f47/unearth-0.17.1-py3-none-any.whl", + "extra_depends": { + "keyring": [ + "keyring" + ], + "legacy": [ + "requests >=2.25" + ] + } + }, + "requests-2.32.2-py3_none_any_0": { + "name": "requests", + "version": "2.32.2", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "charset-normalizer <4,>=2", + "idna <4,>=2.5", + "urllib3 <3,>=1.21.1", + "certifi >=2017.4.17", + "python >=3.8" ], - "use-chardet-on-py3": [ - "chardet <5,>=3.0.2" - ] - } - }, - "requests-2.27.1-py3_none_any_0": { - "name": "requests", - "version": "2.27.1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "charset-normalizer ~=2.0.0[when=\"python >= 3\"]", - "chardet <5,>=3.0.2[when=\"python < 3\"]", - "idna <3,>=2.5[when=\"python < 3\"]", - "idna <4,>=2.5[when=\"python >= 3\"]", - "urllib3 <1.27,>=1.21.1", - "certifi >=2017.4.17", - "python >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" - ], - "fn": "requests-2.27.1-py2.py3-none-any.whl", - "sha256": "5ae0233ba5b28bda8be353ed1e670a71e59113bfe5ea0b5de0e247a22df89770", - "size": 63214, - "subdir": "noarch", - "timestamp": 1765456752, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/2d/61/08076519c80041bc0ffa1a8af0cbd3bf3e2b62af10435d269a9d0f40564d/requests-2.27.1-py2.py3-none-any.whl", - "extra_depends": { - "socks": [ - "pysocks !=1.5.7,>=1.5.6", - "win-inet-pton" + "fn": "requests-2.32.2-py3-none-any.whl", + "sha256": "0375f9273b827542a3a03675feb1a4bcd8c064963dabf7b3fd6cfb761e4f7afd", + "size": 63998, + "subdir": "noarch", + "timestamp": 1765456754, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/c3/20/748e38b466e0819491f0ce6e90ebe4184966ee304fe483e2c414b0f4ef07/requests-2.32.2-py3-none-any.whl", + "extra_depends": { + "socks": [ + "pysocks !=1.5.7,>=1.5.6" + ], + "use-chardet-on-py3": [ + "chardet <6,>=3.0.2" + ] + } + }, + "requests-2.32.1-py3_none_any_0": { + "name": "requests", + "version": "2.32.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "charset-normalizer <4,>=2", + "idna <4,>=2.5", + "urllib3 <3,>=1.21.1", + "certifi >=2017.4.17", + "python >=3.8" ], - "use-chardet-on-py3": [ - "chardet <5,>=3.0.2" - ] - } - }, - "requests-2.27.0-py3_none_any_0": { - "name": "requests", - "version": "2.27.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "charset-normalizer ~=2.0.0[when=\"python >= 3\"]", - "chardet <5,>=3.0.2[when=\"python < 3\"]", - "idna <3,>=2.5[when=\"python < 3\"]", - "idna <4,>=2.5[when=\"python >= 3\"]", - "urllib3 <1.27,>=1.21.1", - "certifi >=2017.4.17", - "python >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" - ], - "fn": "requests-2.27.0-py2.py3-none-any.whl", - "sha256": "494b58ec33a537c1312ff2c560258f06fb330ea1af7a172f524cb52ee9bba26f", - "size": 63160, - "subdir": "noarch", - "timestamp": 1765456752, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/47/01/f420e7add78110940639a958e5af0e3f8e07a8a8b62049bac55ee117aa91/requests-2.27.0-py2.py3-none-any.whl", - "extra_depends": { - "socks": [ - "pysocks !=1.5.7,>=1.5.6", - "win-inet-pton" + "fn": "requests-2.32.1-py3-none-any.whl", + "sha256": "d58df96a8a48ef92c2ff1e3e16dd2b043a1c4955305bf952be6741a2c8328dd8", + "size": 63796, + "subdir": "noarch", + "timestamp": 1765456754, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/9c/a6/b9bf71eeb6dc835311b5c47a7c90df57b08061091161639611252257768d/requests-2.32.1-py3-none-any.whl", + "extra_depends": { + "socks": [ + "pysocks !=1.5.7,>=1.5.6" + ], + "use-chardet-on-py3": [ + "chardet <6,>=3.0.2" + ] + } + }, + "requests-2.32.0-py3_none_any_0": { + "name": "requests", + "version": "2.32.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "charset-normalizer <4,>=2", + "idna <4,>=2.5", + "urllib3 <3,>=1.21.1", + "certifi >=2017.4.17", + "python >=3.8" ], - "use-chardet-on-py3": [ - "chardet <5,>=3.0.2" - ] - } - }, - "requests-2.26.0-py3_none_any_0": { - "name": "requests", - "version": "2.26.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "charset-normalizer ~=2.0.0[when=\"python >= 3\"]", - "chardet <5,>=3.0.2[when=\"python < 3\"]", - "idna <3,>=2.5[when=\"python < 3\"]", - "idna <4,>=2.5[when=\"python >= 3\"]", - "urllib3 <1.27,>=1.21.1", - "certifi >=2017.4.17", - "python >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" - ], - "fn": "requests-2.26.0-py2.py3-none-any.whl", - "sha256": "e796e3a8b3433b4e5d7b8ce95e2796698ef4e533d022e2641b0d9535ada43476", - "size": 62335, - "subdir": "noarch", - "timestamp": 1765456752, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/92/96/144f70b972a9c0eabbd4391ef93ccd49d0f2747f4f6a2a2738e99e5adc65/requests-2.26.0-py2.py3-none-any.whl", - "extra_depends": { - "socks": [ - "pysocks !=1.5.7,>=1.5.6", - "win-inet-pton" + "fn": "requests-2.32.0-py3-none-any.whl", + "sha256": "02c4b63dff20397729229c280ee4a6d6da1ea1ad917fc2f384bc155c2b013f6a", + "size": 63791, + "subdir": "noarch", + "timestamp": 1765456754, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/24/e8/09e8d662a9675a4e4f5dd7a8e6127b463a091d2703ed931a64aa66d00065/requests-2.32.0-py3-none-any.whl", + "extra_depends": { + "socks": [ + "pysocks !=1.5.7,>=1.5.6" + ], + "use-chardet-on-py3": [ + "chardet <6,>=3.0.2" + ] + } + }, + "requests-2.31.0-py3_none_any_0": { + "name": "requests", + "version": "2.31.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "charset-normalizer <4,>=2", + "idna <4,>=2.5", + "urllib3 <3,>=1.21.1", + "certifi >=2017.4.17", + "python >=3.7" ], - "use-chardet-on-py3": [ - "chardet <5,>=3.0.2" - ] - } - }, - "django-5.1.10-py3_none_any_0": { - "name": "django", - "version": "5.1.10", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "asgiref <4,>=3.8.1", - "sqlparse >=0.3.1", - "python-tzdata[when=\"__win\"]", - "python >=3.10" - ], - "fn": "django-5.1.10-py3-none-any.whl", - "sha256": "eb881725250e3f577785b0c9ba60ef39958f1b9570e8cc7e345470110c7f32ac", - "size": 8277457, - "subdir": "noarch", - "timestamp": 1765456735, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/9e/fc/80dc741ba0acb3241aac1213d7272c573d52d8a62ec2c69e9b3bef1547f2/django-5.1.10-py3-none-any.whl", - "extra_depends": { - "argon2": [ - "argon2-cffi >=19.1.0" + "fn": "requests-2.31.0-py3-none-any.whl", + "sha256": "1f816aa80bb0acdcab5940891bfab0b6ce869d9457e60e5dec48cb32b5c022ff", + "size": 62654, + "subdir": "noarch", + "timestamp": 1765456754, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl", + "extra_depends": { + "socks": [ + "pysocks !=1.5.7,>=1.5.6" + ], + "use-chardet-on-py3": [ + "chardet <6,>=3.0.2" + ] + } + }, + "requests-2.28.2-py3_none_any_0": { + "name": "requests", + "version": "2.28.2", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "charset-normalizer <4,>=2", + "idna <4,>=2.5", + "urllib3 <1.27,>=1.21.1", + "certifi >=2017.4.17", + "python >=3.7, <4" ], - "bcrypt": [ - "bcrypt" - ] - } - }, - "django-5.2rc1-py3_none_any_0": { - "name": "django", - "version": "5.2rc1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "asgiref >=3.8.1", - "sqlparse >=0.3.1", - "python-tzdata[when=\"__win\"]", - "python >=3.10" - ], - "fn": "Django-5.2rc1-py3-none-any.whl", - "sha256": "cb07e5d06fab39d48d2326d7618fa6d508c608ee29ea365b8bdb42ddd0564cb6", - "size": 8295671, - "subdir": "noarch", - "timestamp": 1765456735, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/89/90/cbc3d95cabce6ae81c9b56301c2ba6f22198193edcf8b17d35f91500a344/Django-5.2rc1-py3-none-any.whl", - "extra_depends": { - "argon2": [ - "argon2-cffi >=19.1.0" + "fn": "requests-2.28.2-py3-none-any.whl", + "sha256": "a83b301e2604f39dee727cb96197ee5aa58d484dacc000a36526dcf85b12f3f4", + "size": 62913, + "subdir": "noarch", + "timestamp": 1765456753, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/d2/f4/274d1dbe96b41cf4e0efb70cbced278ffd61b5c7bb70338b62af94ccb25b/requests-2.28.2-py3-none-any.whl", + "extra_depends": { + "socks": [ + "pysocks !=1.5.7,>=1.5.6" + ], + "use-chardet-on-py3": [ + "chardet <6,>=3.0.2" + ] + } + }, + "requests-2.30.0-py3_none_any_0": { + "name": "requests", + "version": "2.30.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "charset-normalizer <4,>=2", + "idna <4,>=2.5", + "urllib3 <3,>=1.21.1", + "certifi >=2017.4.17", + "python >=3.7" ], - "bcrypt": [ - "bcrypt" - ] - } - }, - "django-5.2b1-py3_none_any_0": { - "name": "django", - "version": "5.2b1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "asgiref >=3.8.1", - "sqlparse >=0.3.1", - "python-tzdata[when=\"__win\"]", - "python >=3.10" - ], - "fn": "Django-5.2b1-py3-none-any.whl", - "sha256": "cb8825f2447f0e1b31e3822d47053ec4d688eb132faa7f8f080726dcf0fe3229", - "size": 8295550, - "subdir": "noarch", - "timestamp": 1765456733, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/00/6a/4e53c567ef24e81e17a30c788209396e08b8aaf8011d9cf01b8aa4910fa1/Django-5.2b1-py3-none-any.whl", - "extra_depends": { - "argon2": [ - "argon2-cffi >=19.1.0" + "fn": "requests-2.30.0-py3-none-any.whl", + "sha256": "0c0d432d769c2c63e66f91c4f7a7f331458f3d2dd40796ddd61b03372857d608", + "size": 62579, + "subdir": "noarch", + "timestamp": 1765456753, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/96/80/034ffeca15c0f4e01b7b9c6ad0fb704b44e190cde4e757edbd60be404c41/requests-2.30.0-py3-none-any.whl", + "extra_depends": { + "socks": [ + "pysocks !=1.5.7,>=1.5.6" + ], + "use-chardet-on-py3": [ + "chardet <6,>=3.0.2" + ] + } + }, + "requests-2.29.0-py3_none_any_0": { + "name": "requests", + "version": "2.29.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "charset-normalizer <4,>=2", + "idna <4,>=2.5", + "urllib3 <1.27,>=1.21.1", + "certifi >=2017.4.17", + "python >=3.7" ], - "bcrypt": [ - "bcrypt" - ] - } - }, - "django-5.2a1-py3_none_any_0": { - "name": "django", - "version": "5.2a1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "asgiref >=3.8.1", - "sqlparse >=0.3.1", - "python-tzdata[when=\"__win\"]", - "python >=3.10" - ], - "fn": "Django-5.2a1-py3-none-any.whl", - "sha256": "f75529f8b35597d9488a9234020477cf9d9f5cb36fdbced07e029cf0a35f3eb0", - "size": 8296448, - "subdir": "noarch", - "timestamp": 1765456732, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/14/1a/0d9c26312161a2c4cd393cc6d61edd507b6a99d964e7da56baa743288f3b/Django-5.2a1-py3-none-any.whl", - "extra_depends": { - "argon2": [ - "argon2-cffi >=19.1.0" + "fn": "requests-2.29.0-py3-none-any.whl", + "sha256": "d5d4296ec2383866e983bab45ca0b587a615b4a84cd9714c14e102acfa2fd51e", + "size": 62588, + "subdir": "noarch", + "timestamp": 1765456753, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/cf/e1/2aa539876d9ed0ddc95882451deb57cfd7aa8dbf0b8dbce68e045549ba56/requests-2.29.0-py3-none-any.whl", + "extra_depends": { + "socks": [ + "pysocks !=1.5.7,>=1.5.6" + ], + "use-chardet-on-py3": [ + "chardet <6,>=3.0.2" + ] + } + }, + "requests-2.28.1-py3_none_any_0": { + "name": "requests", + "version": "2.28.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "charset-normalizer <3,>=2", + "idna <4,>=2.5", + "urllib3 <1.27,>=1.21.1", + "certifi >=2017.4.17", + "python >=3.7, <4" ], - "bcrypt": [ - "bcrypt" - ] - } - }, - "django-5.2-py3_none_any_0": { - "name": "django", - "version": "5.2", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "asgiref >=3.8.1", - "sqlparse >=0.3.1", - "python-tzdata[when=\"__win\"]", - "python >=3.10" - ], - "fn": "Django-5.2-py3-none-any.whl", - "sha256": "61fd2ba96e90225bdff30c2b93e81a4bacfc55df50c8a8a0d649f74f90f55237", - "size": 8301361, - "subdir": "noarch", - "timestamp": 1765456730, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/63/e0/6a5b5ea350c5bd63fe94b05e4c146c18facb51229d9dee42aa39f9fc2214/Django-5.2-py3-none-any.whl", - "extra_depends": { - "argon2": [ - "argon2-cffi >=19.1.0" + "fn": "requests-2.28.1-py3-none-any.whl", + "sha256": "153827e5b8ac656356646d31f4d1398b560776e38ef84b7f1a868be75f40117b", + "size": 62921, + "subdir": "noarch", + "timestamp": 1765456752, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/ca/91/6d9b8ccacd0412c08820f72cebaa4f0c0441b5cda699c90f618b6f8a1b42/requests-2.28.1-py3-none-any.whl", + "extra_depends": { + "socks": [ + "pysocks !=1.5.7,>=1.5.6" + ], + "use-chardet-on-py3": [ + "chardet <6,>=3.0.2" + ] + } + }, + "requests-2.28.0-py3_none_any_0": { + "name": "requests", + "version": "2.28.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "charset-normalizer ~=2.0.0", + "idna <4,>=2.5", + "urllib3 <1.27,>=1.21.1", + "certifi >=2017.4.17", + "python >=3.7, <4" ], - "bcrypt": [ - "bcrypt" - ] - } - }, - "django-5.1.7-py3_none_any_0": { - "name": "django", - "version": "5.1.7", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "asgiref <4,>=3.8.1", - "sqlparse >=0.3.1", - "python-tzdata[when=\"__win\"]", - "python >=3.10" - ], - "fn": "Django-5.1.7-py3-none-any.whl", - "sha256": "9b4a961fde52965d5469f1a78a62e402d5d49ffb248793f28dc85cb65417c836", - "size": 8276912, - "subdir": "noarch", - "timestamp": 1765456730, - "noarch": "python", - "url": "https://files.pythonhosted.org/packages/ba/0f/7e042df3d462d39ae01b27a09ee76653692442bc3701fbfa6cb38e12889d/Django-5.1.7-py3-none-any.whl", - "extra_depends": { - "argon2": [ - "argon2-cffi >=19.1.0" + "fn": "requests-2.28.0-py3-none-any.whl", + "sha256": "f446cb09580c793a5b6d497bf446236b67f9daae9d343792c7333878fa1ec25d", + "size": 62929, + "subdir": "noarch", + "timestamp": 1765456752, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/41/5b/2209eba8133fc081d3ffff02e1f6376e3117e52bb16f674721a83e67e68e/requests-2.28.0-py3-none-any.whl", + "extra_depends": { + "socks": [ + "pysocks !=1.5.7,>=1.5.6" + ], + "use-chardet-on-py3": [ + "chardet <5,>=3.0.2" + ] + } + }, + "requests-2.27.1-py3_none_any_0": { + "name": "requests", + "version": "2.27.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "charset-normalizer ~=2.0.0[when=\"python >= 3\"]", + "chardet <5,>=3.0.2[when=\"python < 3\"]", + "idna <3,>=2.5[when=\"python < 3\"]", + "idna <4,>=2.5[when=\"python >= 3\"]", + "urllib3 <1.27,>=1.21.1", + "certifi >=2017.4.17", + "python >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + ], + "fn": "requests-2.27.1-py2.py3-none-any.whl", + "sha256": "5ae0233ba5b28bda8be353ed1e670a71e59113bfe5ea0b5de0e247a22df89770", + "size": 63214, + "subdir": "noarch", + "timestamp": 1765456752, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/2d/61/08076519c80041bc0ffa1a8af0cbd3bf3e2b62af10435d269a9d0f40564d/requests-2.27.1-py2.py3-none-any.whl", + "extra_depends": { + "socks": [ + "pysocks !=1.5.7,>=1.5.6", + "win-inet-pton" + ], + "use-chardet-on-py3": [ + "chardet <5,>=3.0.2" + ] + } + }, + "requests-2.27.0-py3_none_any_0": { + "name": "requests", + "version": "2.27.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "charset-normalizer ~=2.0.0[when=\"python >= 3\"]", + "chardet <5,>=3.0.2[when=\"python < 3\"]", + "idna <3,>=2.5[when=\"python < 3\"]", + "idna <4,>=2.5[when=\"python >= 3\"]", + "urllib3 <1.27,>=1.21.1", + "certifi >=2017.4.17", + "python >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + ], + "fn": "requests-2.27.0-py2.py3-none-any.whl", + "sha256": "494b58ec33a537c1312ff2c560258f06fb330ea1af7a172f524cb52ee9bba26f", + "size": 63160, + "subdir": "noarch", + "timestamp": 1765456752, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/47/01/f420e7add78110940639a958e5af0e3f8e07a8a8b62049bac55ee117aa91/requests-2.27.0-py2.py3-none-any.whl", + "extra_depends": { + "socks": [ + "pysocks !=1.5.7,>=1.5.6", + "win-inet-pton" + ], + "use-chardet-on-py3": [ + "chardet <5,>=3.0.2" + ] + } + }, + "requests-2.26.0-py3_none_any_0": { + "name": "requests", + "version": "2.26.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "charset-normalizer ~=2.0.0[when=\"python >= 3\"]", + "chardet <5,>=3.0.2[when=\"python < 3\"]", + "idna <3,>=2.5[when=\"python < 3\"]", + "idna <4,>=2.5[when=\"python >= 3\"]", + "urllib3 <1.27,>=1.21.1", + "certifi >=2017.4.17", + "python >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + ], + "fn": "requests-2.26.0-py2.py3-none-any.whl", + "sha256": "e796e3a8b3433b4e5d7b8ce95e2796698ef4e533d022e2641b0d9535ada43476", + "size": 62335, + "subdir": "noarch", + "timestamp": 1765456752, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/92/96/144f70b972a9c0eabbd4391ef93ccd49d0f2747f4f6a2a2738e99e5adc65/requests-2.26.0-py2.py3-none-any.whl", + "extra_depends": { + "socks": [ + "pysocks !=1.5.7,>=1.5.6", + "win-inet-pton" + ], + "use-chardet-on-py3": [ + "chardet <5,>=3.0.2" + ] + } + }, + "django-5.1.10-py3_none_any_0": { + "name": "django", + "version": "5.1.10", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "asgiref <4,>=3.8.1", + "sqlparse >=0.3.1", + "python-tzdata[when=\"__win\"]", + "python >=3.10" + ], + "fn": "django-5.1.10-py3-none-any.whl", + "sha256": "eb881725250e3f577785b0c9ba60ef39958f1b9570e8cc7e345470110c7f32ac", + "size": 8277457, + "subdir": "noarch", + "timestamp": 1765456735, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/9e/fc/80dc741ba0acb3241aac1213d7272c573d52d8a62ec2c69e9b3bef1547f2/django-5.1.10-py3-none-any.whl", + "extra_depends": { + "argon2": [ + "argon2-cffi >=19.1.0" + ], + "bcrypt": [ + "bcrypt" + ] + } + }, + "django-5.2rc1-py3_none_any_0": { + "name": "django", + "version": "5.2rc1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "asgiref >=3.8.1", + "sqlparse >=0.3.1", + "python-tzdata[when=\"__win\"]", + "python >=3.10" + ], + "fn": "Django-5.2rc1-py3-none-any.whl", + "sha256": "cb07e5d06fab39d48d2326d7618fa6d508c608ee29ea365b8bdb42ddd0564cb6", + "size": 8295671, + "subdir": "noarch", + "timestamp": 1765456735, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/89/90/cbc3d95cabce6ae81c9b56301c2ba6f22198193edcf8b17d35f91500a344/Django-5.2rc1-py3-none-any.whl", + "extra_depends": { + "argon2": [ + "argon2-cffi >=19.1.0" + ], + "bcrypt": [ + "bcrypt" + ] + } + }, + "django-5.2b1-py3_none_any_0": { + "name": "django", + "version": "5.2b1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "asgiref >=3.8.1", + "sqlparse >=0.3.1", + "python-tzdata[when=\"__win\"]", + "python >=3.10" + ], + "fn": "Django-5.2b1-py3-none-any.whl", + "sha256": "cb8825f2447f0e1b31e3822d47053ec4d688eb132faa7f8f080726dcf0fe3229", + "size": 8295550, + "subdir": "noarch", + "timestamp": 1765456733, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/00/6a/4e53c567ef24e81e17a30c788209396e08b8aaf8011d9cf01b8aa4910fa1/Django-5.2b1-py3-none-any.whl", + "extra_depends": { + "argon2": [ + "argon2-cffi >=19.1.0" + ], + "bcrypt": [ + "bcrypt" + ] + } + }, + "django-5.2a1-py3_none_any_0": { + "name": "django", + "version": "5.2a1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "asgiref >=3.8.1", + "sqlparse >=0.3.1", + "python-tzdata[when=\"__win\"]", + "python >=3.10" + ], + "fn": "Django-5.2a1-py3-none-any.whl", + "sha256": "f75529f8b35597d9488a9234020477cf9d9f5cb36fdbced07e029cf0a35f3eb0", + "size": 8296448, + "subdir": "noarch", + "timestamp": 1765456732, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/14/1a/0d9c26312161a2c4cd393cc6d61edd507b6a99d964e7da56baa743288f3b/Django-5.2a1-py3-none-any.whl", + "extra_depends": { + "argon2": [ + "argon2-cffi >=19.1.0" + ], + "bcrypt": [ + "bcrypt" + ] + } + }, + "django-5.2-py3_none_any_0": { + "name": "django", + "version": "5.2", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "asgiref >=3.8.1", + "sqlparse >=0.3.1", + "python-tzdata[when=\"__win\"]", + "python >=3.10" + ], + "fn": "Django-5.2-py3-none-any.whl", + "sha256": "61fd2ba96e90225bdff30c2b93e81a4bacfc55df50c8a8a0d649f74f90f55237", + "size": 8301361, + "subdir": "noarch", + "timestamp": 1765456730, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/63/e0/6a5b5ea350c5bd63fe94b05e4c146c18facb51229d9dee42aa39f9fc2214/Django-5.2-py3-none-any.whl", + "extra_depends": { + "argon2": [ + "argon2-cffi >=19.1.0" + ], + "bcrypt": [ + "bcrypt" + ] + } + }, + "django-5.1.7-py3_none_any_0": { + "name": "django", + "version": "5.1.7", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "asgiref <4,>=3.8.1", + "sqlparse >=0.3.1", + "python-tzdata[when=\"__win\"]", + "python >=3.10" ], - "bcrypt": [ - "bcrypt" - ] + "fn": "Django-5.1.7-py3-none-any.whl", + "sha256": "9b4a961fde52965d5469f1a78a62e402d5d49ffb248793f28dc85cb65417c836", + "size": 8276912, + "subdir": "noarch", + "timestamp": 1765456730, + "noarch": "python", + "url": "https://files.pythonhosted.org/packages/ba/0f/7e042df3d462d39ae01b27a09ee76653692442bc3701fbfa6cb38e12889d/Django-5.1.7-py3-none-any.whl", + "extra_depends": { + "argon2": [ + "argon2-cffi >=19.1.0" + ], + "bcrypt": [ + "bcrypt" + ] + } } } }