Skip to content

Commit 751f7fa

Browse files
authored
Improve PEP 691 compatibility (#428)
[PEP 691](https://peps.python.org/pep-0691/#project-detail) has slightly different, more relaxed rules around file metadata. These changes are now reflected in the `File` struct. This will make it easier to support alternative indices. I had expected that i need to introduce a separate type for that, so i'm happy it's two `Option`s more and an alias. Part of #412
1 parent 3a4988f commit 751f7fa

File tree

8 files changed

+53
-27
lines changed

8 files changed

+53
-27
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/puffin-cli/src/commands/pip_sync.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,16 +156,16 @@ pub(crate) async fn sync_requirements(
156156
continue;
157157
};
158158
match &file.yanked {
159-
Yanked::Bool(false) => {}
160-
Yanked::Bool(true) => {
159+
None | Some(Yanked::Bool(false)) => {}
160+
Some(Yanked::Bool(true)) => {
161161
writeln!(
162162
printer,
163163
"{}{} {dist} is yanked. Refresh your lockfile to pin an un-yanked version.",
164164
"warning".yellow().bold(),
165165
":".bold(),
166166
)?;
167167
}
168-
Yanked::Reason(reason) => {
168+
Some(Yanked::Reason(reason)) => {
169169
writeln!(
170170
printer,
171171
"{}{} {dist} is yanked (reason: \"{reason}\"). Refresh your lockfile to pin an un-yanked version.",

crates/puffin-client/src/client.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,10 @@ impl RegistryClient {
206206
// If the metadata file is available at its own url (PEP 658), download it from there
207207
let url = Url::parse(&file.url)?;
208208
let filename = WheelFilename::from_str(&file.filename)?;
209-
if file.data_dist_info_metadata.is_available() {
209+
if file
210+
.dist_info_metadata
211+
.is_some_and(|dist_info_metadata| dist_info_metadata.is_available())
212+
{
210213
let url = Url::parse(&format!("{}.metadata", file.url))?;
211214

212215
let cache_dir = self.cache.join(WHEEL_METADATA_FROM_INDEX).join("pypi");

crates/puffin-distribution/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ pypi-types = { path = "../pypi-types" }
1818

1919
anyhow = { workspace = true }
2020
fs-err = { workspace = true }
21+
serde = { workspace = true, features = ["derive"] }
2122
serde_json = { workspace = true }
2223
url = { workspace = true }

crates/puffin-distribution/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ impl RemoteSource for RegistryBuiltDist {
263263
}
264264

265265
fn size(&self) -> Option<usize> {
266-
Some(self.file.size)
266+
self.file.size
267267
}
268268
}
269269

@@ -273,7 +273,7 @@ impl RemoteSource for RegistrySourceDist {
273273
}
274274

275275
fn size(&self) -> Option<usize> {
276-
Some(self.file.size)
276+
self.file.size
277277
}
278278
}
279279

crates/puffin-installer/src/downloader.rs

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,34 @@ async fn fetch(
116116
let reader = client.stream_external(&url).await?;
117117

118118
// If the file is greater than 5MB, write it to disk; otherwise, keep it in memory.
119-
let file_size = ByteSize::b(wheel.file.size as u64);
120-
if file_size >= ByteSize::mb(5) {
121-
debug!("Fetching disk-based wheel from registry: {dist} ({file_size})");
119+
let small_size = if let Some(size) = wheel.file.size {
120+
let byte_size = ByteSize::b(size as u64);
121+
if byte_size < ByteSize::mb(5) {
122+
Some(size)
123+
} else {
124+
None
125+
}
126+
} else {
127+
None
128+
};
129+
if let Some(small_size) = small_size {
130+
debug!(
131+
"Fetching in-memory wheel from registry: {dist} ({})",
132+
ByteSize::b(small_size as u64)
133+
);
134+
135+
// Read into a buffer.
136+
let mut buffer = Vec::with_capacity(small_size);
137+
let mut reader = tokio::io::BufReader::new(reader.compat());
138+
tokio::io::copy(&mut reader, &mut buffer).await?;
139+
140+
Ok(Download::Wheel(WheelDownload::InMemory(InMemoryWheel {
141+
dist,
142+
buffer,
143+
})))
144+
} else {
145+
let size = small_size.map_or("unknown size".to_string(), |size| size.to_string());
146+
debug!("Fetching disk-based wheel from registry: {dist} ({size})");
122147

123148
// Download the wheel to a temporary file.
124149
let temp_dir = tempfile::tempdir_in(cache)?.into_path();
@@ -131,18 +156,6 @@ async fn fetch(
131156
dist,
132157
path: wheel_file,
133158
})))
134-
} else {
135-
debug!("Fetching in-memory wheel from registry: {dist} ({file_size})");
136-
137-
// Read into a buffer.
138-
let mut buffer = Vec::with_capacity(wheel.file.size);
139-
let mut reader = tokio::io::BufReader::new(reader.compat());
140-
tokio::io::copy(&mut reader, &mut buffer).await?;
141-
142-
Ok(Download::Wheel(WheelDownload::InMemory(InMemoryWheel {
143-
dist,
144-
buffer,
145-
})))
146159
}
147160
}
148161

crates/puffin-resolver/src/version_map.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use distribution_filename::{SourceDistFilename, WheelFilename};
66
use pep440_rs::Version;
77
use platform_tags::{TagPriority, Tags};
88
use puffin_normalize::PackageName;
9-
use pypi_types::SimpleJson;
9+
use pypi_types::{SimpleJson, Yanked};
1010

1111
use crate::file::{DistFile, SdistFile, WheelFile};
1212
use crate::pubgrub::PubGrubVersion;
@@ -45,7 +45,7 @@ impl VersionMap {
4545
// When resolving, exclude yanked files.
4646
// TODO(konstin): When we fail resolving due to a dependency locked to yanked version,
4747
// we should tell the user.
48-
if file.yanked.is_yanked() {
48+
if file.yanked.as_ref().is_some_and(Yanked::is_yanked) {
4949
continue;
5050
}
5151

crates/pypi-types/src/simple_json.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,25 @@ pub struct SimpleJson {
1212
pub versions: Vec<String>,
1313
}
1414

15+
/// A single (remote) file belonging to a package, generally either a wheel or a source dist.
16+
///
17+
/// <https://peps.python.org/pep-0691/#project-detail>
1518
#[derive(Debug, Clone, Serialize, Deserialize)]
1619
#[serde(rename_all = "kebab-case")]
1720
pub struct File {
18-
pub core_metadata: Metadata,
19-
pub data_dist_info_metadata: Metadata,
21+
// Not PEP 691 compliant alias used by pypi
22+
#[serde(alias = "data_dist_info_metadata")]
23+
pub dist_info_metadata: Option<Metadata>,
2024
pub filename: String,
2125
pub hashes: Hashes,
2226
/// Note: Deserialized with [`LenientVersionSpecifiers`] since there are a number of invalid
2327
/// versions on pypi
2428
#[serde(deserialize_with = "deserialize_version_specifiers_lenient")]
2529
pub requires_python: Option<VersionSpecifiers>,
26-
pub size: usize,
30+
pub size: Option<usize>,
2731
pub upload_time: String,
2832
pub url: String,
29-
pub yanked: Yanked,
33+
pub yanked: Option<Yanked>,
3034
}
3135

3236
fn deserialize_version_specifiers_lenient<'de, D>(
@@ -75,6 +79,10 @@ impl Yanked {
7579
}
7680
}
7781

82+
/// A dictionary mapping a hash name to a hex encoded digest of the file.
83+
///
84+
/// PEP 691 says multiple hashes can be included and the interpretation is left to the client, we
85+
/// only support SHA 256 atm.
7886
#[derive(Debug, Clone, Serialize, Deserialize)]
7987
pub struct Hashes {
8088
pub sha256: String,

0 commit comments

Comments
 (0)