Rename to uv (#1302)

First, replace all usages in files in-place. I used my editor for this.
If someone wants to add a one-liner that'd be fun.

Then, update directory and file names:

```
# Run twice for nested directories
find . -type d -print0 | xargs -0 rename s/puffin/uv/g
find . -type d -print0 | xargs -0 rename s/puffin/uv/g

# Update files
find . -type f -print0 | xargs -0 rename s/puffin/uv/g
```

Then add all the files again

```
# Add all the files again
git add crates
git add python/uv

# This one needs a force-add
git add -f crates/uv-trampoline
```
This commit is contained in:
Zanie Blue 2024-02-15 11:19:46 -06:00 committed by GitHub
parent 328b116d5d
commit 2586f655bb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
229 changed files with 1796 additions and 1818 deletions

View file

@ -0,0 +1,45 @@
//! Git support is derived from Cargo's implementation.
//! Cargo is dual-licensed under either Apache 2.0 or MIT, at the user's choice.
//! Source: <https://github.com/rust-lang/cargo/blob/23eb492cf920ce051abfc56bbaf838514dc8365c/src/cargo/util/errors.rs>
use std::fmt::{self, Write};
use super::truncate_with_ellipsis;
#[derive(Debug)]
pub(crate) struct HttpNotSuccessful {
pub(crate) code: u32,
pub(crate) url: String,
pub(crate) ip: Option<String>,
pub(crate) body: Vec<u8>,
}
impl HttpNotSuccessful {
fn render(&self) -> String {
let mut result = String::new();
let body = std::str::from_utf8(&self.body).map_or_else(
|_| format!("[{} non-utf8 bytes]", self.body.len()),
|s| truncate_with_ellipsis(s, 512),
);
write!(
result,
"failed to get successful HTTP response from `{}`",
self.url
)
.unwrap();
if let Some(ip) = &self.ip {
write!(result, " ({ip})").unwrap();
}
writeln!(result, ", got {}", self.code).unwrap();
write!(result, "body:\n{body}").unwrap();
result
}
}
impl fmt::Display for HttpNotSuccessful {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.render())
}
}
impl std::error::Error for HttpNotSuccessful {}

View file

@ -0,0 +1,17 @@
//! Git support is derived from Cargo's implementation.
//! Cargo is dual-licensed under either Apache 2.0 or MIT, at the user's choice.
//! Source: <https://github.com/rust-lang/cargo/blob/23eb492cf920ce051abfc56bbaf838514dc8365c/src/cargo/util/mod.rs>
pub(crate) mod errors;
pub(crate) mod retry;
pub(crate) fn truncate_with_ellipsis(s: &str, max_width: usize) -> String {
// We should truncate at grapheme-boundary and compute character-widths,
// yet the dependencies on unicode-segmentation and unicode-width are
// not worth it.
let mut chars = s.chars();
let mut prefix = (&mut chars).take(max_width - 1).collect::<String>();
if chars.next().is_some() {
prefix.push('…');
}
prefix
}

View file

@ -0,0 +1,188 @@
//! Utilities for retrying a network operation.
//!
//! Some network errors are considered "spurious", meaning it is not a real
//! error (such as a 404 not found) and is likely a transient error (like a
//! bad network connection) that we can hope will resolve itself shortly. The
//! [`Retry`] type offers a way to repeatedly perform some kind of network
//! operation with a delay if it detects one of these possibly transient
//! errors.
//!
//! This supports errors from [`git2`], [`reqwest`], and [`HttpNotSuccessful`]
//! 5xx HTTP errors.
//!
//! The number of retries can be configured by the user via the `net.retry`
//! config option. This indicates the number of times to retry the operation
//! (default 3 times for a total of 4 attempts).
//!
//! There are hard-coded constants that indicate how long to sleep between
//! retries. The constants are tuned to balance a few factors, such as the
//! responsiveness to the user (we don't want cargo to hang for too long
//! retrying things), and accommodating things like Cloudfront's default
//! negative TTL of 10 seconds (if Cloudfront gets a 5xx error for whatever
//! reason it won't try to fetch again for 10 seconds).
//!
//! The timeout also implements a primitive form of random jitter. This is so
//! that if multiple requests fail at the same time that they don't all flood
//! the server at the same time when they are retried. This jitter still has
//! some clumping behavior, but should be good enough.
//!
//! [`Retry`] is the core type for implementing retry logic. The
//! [`Retry::try`] method can be called with a callback, and it will
//! indicate if it needs to be called again sometime in the future if there
//! was a possibly transient error. The caller is responsible for sleeping the
//! appropriate amount of time and then calling [`Retry::try`] again.
//!
//! [`with_retry`] is a convenience function that will create a [`Retry`] and
//! handle repeatedly running a callback until it succeeds, or it runs out of
//! retries.
//!
//! Some interesting resources about retries:
//! - <https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/>
//! - <https://en.wikipedia.org/wiki/Exponential_backoff>
//! - <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After>
//! Git support is derived from Cargo's implementation.
//! Cargo is dual-licensed under either Apache 2.0 or MIT, at the user's choice.
//! Source: <https://github.com/rust-lang/cargo/blob/23eb492cf920ce051abfc56bbaf838514dc8365c/src/cargo/util/network/retry.rs>
use std::cmp::min;
use std::time::Duration;
use anyhow::{Error, Result};
use rand::Rng;
use tracing::warn;
use crate::util::errors::HttpNotSuccessful;
/// State for managing retrying a network operation.
pub(crate) struct Retry {
/// The number of failed attempts that have been done so far.
///
/// Starts at 0, and increases by one each time an attempt fails.
retries: u64,
/// The maximum number of times the operation should be retried.
///
/// 0 means it should never retry.
max_retries: u64,
}
/// The result of attempting some operation via [`Retry::try`].
pub(crate) enum RetryResult<T> {
/// The operation was successful.
///
/// The wrapped value is the return value of the callback function.
Success(T),
/// The operation was an error, and it should not be tried again.
Err(Error),
/// The operation failed, and should be tried again in the future.
///
/// The wrapped value is the number of milliseconds to wait before trying
/// again. The caller is responsible for waiting this long and then
/// calling [`Retry::try`] again.
Retry(u64),
}
/// Maximum amount of time a single retry can be delayed (milliseconds).
const MAX_RETRY_SLEEP_MS: u64 = 10 * 1000;
/// The minimum initial amount of time a retry will be delayed (milliseconds).
///
/// The actual amount of time will be a random value above this.
const INITIAL_RETRY_SLEEP_BASE_MS: u64 = 500;
/// The maximum amount of additional time the initial retry will take (milliseconds).
///
/// The initial delay will be [`INITIAL_RETRY_SLEEP_BASE_MS`] plus a random range
/// from 0 to this value.
const INITIAL_RETRY_JITTER_MS: u64 = 1000;
impl Retry {
pub(crate) fn new() -> Retry {
Retry {
retries: 0,
max_retries: 3,
}
}
/// Calls the given callback, and returns a [`RetryResult`] which
/// indicates whether or not this needs to be called again at some point
/// in the future to retry the operation if it failed.
pub(crate) fn r#try<T>(&mut self, f: impl FnOnce() -> Result<T>) -> RetryResult<T> {
match f() {
Err(ref err) if maybe_spurious(err) && self.retries < self.max_retries => {
let err_msg = err.downcast_ref::<HttpNotSuccessful>().map_or_else(
|| err.root_cause().to_string(),
HttpNotSuccessful::to_string,
);
warn!(
"Spurious network error ({} tries remaining): {err_msg}",
self.max_retries - self.retries,
);
self.retries += 1;
RetryResult::Retry(self.next_sleep_ms())
}
Err(e) => RetryResult::Err(e),
Ok(r) => RetryResult::Success(r),
}
}
/// Gets the next sleep duration in milliseconds.
fn next_sleep_ms(&self) -> u64 {
if self.retries == 1 {
let mut rng = rand::thread_rng();
INITIAL_RETRY_SLEEP_BASE_MS + rng.gen_range(0..INITIAL_RETRY_JITTER_MS)
} else {
min(
((self.retries - 1) * 3) * 1000 + INITIAL_RETRY_SLEEP_BASE_MS,
MAX_RETRY_SLEEP_MS,
)
}
}
}
fn maybe_spurious(err: &Error) -> bool {
if let Some(git_err) = err.downcast_ref::<git2::Error>() {
match git_err.class() {
git2::ErrorClass::Net
| git2::ErrorClass::Os
| git2::ErrorClass::Zlib
| git2::ErrorClass::Ssl
| git2::ErrorClass::Http => return git_err.code() != git2::ErrorCode::Certificate,
_ => (),
}
}
if let Some(reqwest_err) = err.downcast_ref::<reqwest::Error>() {
if reqwest_err.is_timeout()
|| reqwest_err.is_connect()
|| reqwest_err
.status()
.map_or(false, |status| status.is_server_error())
{
return true;
}
}
if let Some(not_200) = err.downcast_ref::<HttpNotSuccessful>() {
if 500 <= not_200.code && not_200.code < 600 {
return true;
}
}
false
}
/// Wrapper method for network call retry logic.
///
/// Retry counts provided by Config object `net.retry`. Config shell outputs
/// a warning on per retry.
///
/// Closure must return a `Result`.
pub(crate) fn with_retry<T, F>(mut callback: F) -> Result<T>
where
F: FnMut() -> Result<T>,
{
let mut retry = Retry::new();
loop {
match retry.r#try(&mut callback) {
RetryResult::Success(r) => return Ok(r),
RetryResult::Err(e) => return Err(e),
RetryResult::Retry(sleep) => std::thread::sleep(Duration::from_millis(sleep)),
}
}
}