This commit is contained in:
konstin 2025-10-27 16:06:47 +01:00
parent 43b7eb76b6
commit e6949b81e2
5 changed files with 28 additions and 20 deletions

View File

@ -3,6 +3,8 @@ use std::ops::Deref;
use async_http_range_reader::AsyncHttpRangeReaderError; use async_http_range_reader::AsyncHttpRangeReaderError;
use async_zip::error::ZipError; use async_zip::error::ZipError;
use http::StatusCode;
use rustc_hash::FxHashSet;
use serde::Deserialize; use serde::Deserialize;
use uv_distribution_filename::{WheelFilename, WheelFilenameError}; use uv_distribution_filename::{WheelFilename, WheelFilenameError};
@ -278,7 +280,7 @@ pub enum ErrorKind {
/// Make sure the package name is spelled correctly and that you've /// Make sure the package name is spelled correctly and that you've
/// configured the right registry to fetch it from. /// configured the right registry to fetch it from.
#[error("Package `{0}` was not found in the registry")] #[error("Package `{0}` was not found in the registry")]
StatusCodeError(PackageName), StatusCodeError(PackageName, FxHashSet<StatusCode>),
/// The package was not found in the local (file-based) index. /// The package was not found in the local (file-based) index.
#[error("Package `{0}` was not found in the local index")] #[error("Package `{0}` was not found in the local index")]

View File

@ -3,7 +3,6 @@ use std::fmt::Debug;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration; use std::time::Duration;
use async_http_range_reader::AsyncHttpRangeReader; use async_http_range_reader::AsyncHttpRangeReader;
@ -11,7 +10,7 @@ use futures::{FutureExt, StreamExt, TryStreamExt};
use http::{HeaderMap, StatusCode}; use http::{HeaderMap, StatusCode};
use itertools::Either; use itertools::Either;
use reqwest::{Proxy, Response}; use reqwest::{Proxy, Response};
use rustc_hash::FxHashMap; use rustc_hash::{FxHashMap, FxHashSet};
use tokio::sync::{Mutex, Semaphore}; use tokio::sync::{Mutex, Semaphore};
use tracing::{Instrument, debug, info_span, instrument, trace, warn}; use tracing::{Instrument, debug, info_span, instrument, trace, warn};
use url::Url; use url::Url;
@ -325,7 +324,7 @@ impl RegistryClient {
}; };
let mut results = Vec::new(); let mut results = Vec::new();
let any_status_code_error = AtomicBool::new(false); let status_code_errors = Arc::new(Mutex::new(FxHashSet::default()));
match self.index_strategy_for(package_name) { match self.index_strategy_for(package_name) {
// If we're searching for the first index that contains the package, fetch serially. // If we're searching for the first index that contains the package, fetch serially.
@ -357,7 +356,7 @@ impl RegistryClient {
debug!( debug!(
"Indexes search failed because of status code failure: {status_code}" "Indexes search failed because of status code failure: {status_code}"
); );
any_status_code_error.store(true, Ordering::Relaxed); status_code_errors.lock().await.insert(status_code);
break; break;
} }
} }
@ -394,8 +393,8 @@ impl RegistryClient {
{ {
SimpleMetadataSearchOutcome::Found(metadata) => Some(metadata), SimpleMetadataSearchOutcome::Found(metadata) => Some(metadata),
SimpleMetadataSearchOutcome::NotFound => None, SimpleMetadataSearchOutcome::NotFound => None,
SimpleMetadataSearchOutcome::StatusCodeFailure(_) => { SimpleMetadataSearchOutcome::StatusCodeFailure(status_code) => {
any_status_code_error.store(true, Ordering::Relaxed); status_code_errors.lock().await.insert(status_code);
None None
} }
}; };
@ -422,8 +421,13 @@ impl RegistryClient {
if results.is_empty() { if results.is_empty() {
return match self.connectivity { return match self.connectivity {
Connectivity::Online => { Connectivity::Online => {
if any_status_code_error.load(Ordering::Relaxed) { let status_code_errors = status_code_errors.lock().await;
Err(ErrorKind::StatusCodeError(package_name.clone()).into()) if !status_code_errors.is_empty() {
Err(ErrorKind::StatusCodeError(
package_name.clone(),
status_code_errors.clone(),
)
.into())
} else { } else {
Err(ErrorKind::PackageNotFound(package_name.clone()).into()) Err(ErrorKind::PackageNotFound(package_name.clone()).into())
} }

View File

@ -563,16 +563,18 @@ pub async fn check_url(
); );
Ok(false) Ok(false)
} }
uv_client::ErrorKind::StatusCodeError(_) => { uv_client::ErrorKind::StatusCodeError(_, status_code_error) => {
// TODO(konsti): We currently can't track that this must be exactly one status
// error with check URL.
debug_assert!(
status_code_error.len() == 1,
"Check URL must only check a single URL"
);
let status_code = status_code_error.iter().next();
// The package may or may not exist, there was an authentication failure. // The package may or may not exist, there was an authentication failure.
// TODO(konsti): We should show the real index error instead. let status_code_detail = status_code
let status_code_detail = if index_capabilities.unauthorized(index_url) { .map(ToString::to_string)
"401 Unauthorized" .unwrap_or("Status code error".to_string());
} else if index_capabilities.forbidden(index_url) {
"403 Forbidden"
} else {
"Status code error"
};
warn!( warn!(
"Package not found in the registry; skipping upload check for {filename}" "Package not found in the registry; skipping upload check for {filename}"
); );

View File

@ -201,7 +201,7 @@ impl<Context: BuildContext> ResolverProvider for DefaultResolverProvider<'_, Con
)), )),
Err(err) => match err.kind() { Err(err) => match err.kind() {
uv_client::ErrorKind::PackageNotFound(_) uv_client::ErrorKind::PackageNotFound(_)
| uv_client::ErrorKind::StatusCodeError(_) => { | uv_client::ErrorKind::StatusCodeError(..) => {
if let Some(flat_index) = flat_index if let Some(flat_index) = flat_index
.and_then(|flat_index| flat_index.get(package_name)) .and_then(|flat_index| flat_index.get(package_name))
.cloned() .cloned()

View File

@ -47,7 +47,7 @@ impl LatestClient<'_> {
Err(err) => { Err(err) => {
return match err.kind() { return match err.kind() {
uv_client::ErrorKind::PackageNotFound(_) uv_client::ErrorKind::PackageNotFound(_)
| uv_client::ErrorKind::StatusCodeError(_) | uv_client::ErrorKind::StatusCodeError(..)
| uv_client::ErrorKind::NoIndex(_) | uv_client::ErrorKind::NoIndex(_)
| uv_client::ErrorKind::Offline(_) => Ok(None), | uv_client::ErrorKind::Offline(_) => Ok(None),
_ => Err(err), _ => Err(err),