Migrate from `urlencoding` to `percent-encoding` (#11144)

## Summary

This lets us drop a dependency entirely. `percent-encoding` is used by
`url` and so is already in the graph, whereas `urlencoding` isn't used
by anything else.
This commit is contained in:
Charlie Marsh 2025-01-31 16:29:46 -05:00 committed by GitHub
parent 027db656aa
commit 8adf4a8977
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 30 additions and 32 deletions

16
Cargo.lock generated
View File

@ -4440,12 +4440,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "urlencoding"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]] [[package]]
name = "usvg" name = "usvg"
version = "0.29.0" version = "0.29.0"
@ -4619,6 +4613,7 @@ dependencies = [
"futures", "futures",
"http", "http",
"insta", "insta",
"percent-encoding",
"reqwest", "reqwest",
"reqwest-middleware", "reqwest-middleware",
"rust-netrc", "rust-netrc",
@ -4628,7 +4623,6 @@ dependencies = [
"tokio", "tokio",
"tracing", "tracing",
"url", "url",
"urlencoding",
"uv-once-map", "uv-once-map",
"uv-static", "uv-static",
"wiremock", "wiremock",
@ -4771,9 +4765,9 @@ version = "0.0.1"
dependencies = [ dependencies = [
"hex", "hex",
"memchr", "memchr",
"percent-encoding",
"seahash", "seahash",
"url", "url",
"urlencoding",
] ]
[[package]] [[package]]
@ -4822,6 +4816,7 @@ dependencies = [
"insta", "insta",
"itertools 0.14.0", "itertools 0.14.0",
"jiff", "jiff",
"percent-encoding",
"reqwest", "reqwest",
"reqwest-middleware", "reqwest-middleware",
"reqwest-retry", "reqwest-retry",
@ -4836,7 +4831,6 @@ dependencies = [
"tokio-util", "tokio-util",
"tracing", "tracing",
"url", "url",
"urlencoding",
"uv-auth", "uv-auth",
"uv-cache", "uv-cache",
"uv-cache-key", "uv-cache-key",
@ -5048,6 +5042,7 @@ dependencies = [
"itertools 0.14.0", "itertools 0.14.0",
"jiff", "jiff",
"owo-colors", "owo-colors",
"percent-encoding",
"petgraph", "petgraph",
"rkyv", "rkyv",
"rustc-hash", "rustc-hash",
@ -5057,7 +5052,6 @@ dependencies = [
"thiserror 2.0.11", "thiserror 2.0.11",
"tracing", "tracing",
"url", "url",
"urlencoding",
"uv-auth", "uv-auth",
"uv-cache-info", "uv-cache-info",
"uv-cache-key", "uv-cache-key",
@ -5110,13 +5104,13 @@ dependencies = [
"fs2", "fs2",
"junction", "junction",
"path-slash", "path-slash",
"percent-encoding",
"rustix", "rustix",
"schemars", "schemars",
"serde", "serde",
"tempfile", "tempfile",
"tokio", "tokio",
"tracing", "tracing",
"urlencoding",
"winsafe 0.0.22", "winsafe 0.0.22",
] ]

View File

@ -128,6 +128,7 @@ nix = { version = "0.29.0" }
owo-colors = { version = "4.1.0" } owo-colors = { version = "4.1.0" }
path-slash = { version = "0.2.1" } path-slash = { version = "0.2.1" }
pathdiff = { version = "0.2.1" } pathdiff = { version = "0.2.1" }
percent-encoding = { version = "2.3.1" }
petgraph = { version = "0.7.1" } petgraph = { version = "0.7.1" }
platform-info = { version = "2.0.3" } platform-info = { version = "2.0.3" }
proc-macro2 = { version = "1.0.86" } proc-macro2 = { version = "1.0.86" }
@ -176,7 +177,6 @@ tracing-tree = { version = "0.4.0" }
unicode-width = { version = "0.1.13" } unicode-width = { version = "0.1.13" }
unscanny = { version = "0.1.0" } unscanny = { version = "0.1.0" }
url = { version = "2.5.2", features = ["serde"] } url = { version = "2.5.2", features = ["serde"] }
urlencoding = { version = "2.1.3" }
version-ranges = { git = "https://github.com/astral-sh/pubgrub", rev = "648aa343486e5529953153781fc86025c73c4a61" } version-ranges = { git = "https://github.com/astral-sh/pubgrub", rev = "648aa343486e5529953153781fc86025c73c4a61" }
walkdir = { version = "2.5.0" } walkdir = { version = "2.5.0" }
which = { version = "7.0.0", features = ["regex"] } which = { version = "7.0.0", features = ["regex"] }

View File

@ -11,12 +11,14 @@ workspace = true
[dependencies] [dependencies]
uv-once-map = { workspace = true } uv-once-map = { workspace = true }
uv-static = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
async-trait = { workspace = true } async-trait = { workspace = true }
base64 = { workspace = true } base64 = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
http = { workspace = true } http = { workspace = true }
percent-encoding = { workspace = true }
reqwest = { workspace = true } reqwest = { workspace = true }
reqwest-middleware = { workspace = true } reqwest-middleware = { workspace = true }
rust-netrc = { workspace = true } rust-netrc = { workspace = true }
@ -24,13 +26,10 @@ rustc-hash = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
url = { workspace = true } url = { workspace = true }
urlencoding = { workspace = true }
uv-static = { workspace = true }
[dev-dependencies] [dev-dependencies]
insta = { version = "1.40.0" }
tempfile = { workspace = true } tempfile = { workspace = true }
test-log = { version = "0.2.16", features = ["trace"], default-features = false }
tokio = { workspace = true } tokio = { workspace = true }
wiremock = { workspace = true } wiremock = { workspace = true }
insta = { version = "1.40.0" }
test-log = { version = "0.2.16", features = ["trace"], default-features = false }

View File

@ -127,14 +127,16 @@ impl Credentials {
None None
} else { } else {
Some( Some(
urlencoding::decode(url.username()) percent_encoding::percent_decode_str(url.username())
.decode_utf8()
.expect("An encoded username should always decode") .expect("An encoded username should always decode")
.into_owned(), .into_owned(),
) )
} }
.into(), .into(),
password: url.password().map(|password| { password: url.password().map(|password| {
urlencoding::decode(password) percent_encoding::percent_decode_str(password)
.decode_utf8()
.expect("An encoded password should always decode") .expect("An encoded password should always decode")
.into_owned() .into_owned()
}), }),

View File

@ -19,6 +19,6 @@ workspace = true
[dependencies] [dependencies]
hex = { workspace = true } hex = { workspace = true }
memchr = { workspace = true } memchr = { workspace = true }
percent-encoding = { workspace = true }
seahash = { workspace = true } seahash = { workspace = true }
url = { workspace = true } url = { workspace = true }
urlencoding = { workspace = true }

View File

@ -78,7 +78,8 @@ impl CanonicalUrl {
.path_segments() .path_segments()
.unwrap() .unwrap()
.map(|segment| { .map(|segment| {
urlencoding::decode(segment) percent_encoding::percent_decode_str(segment)
.decode_utf8()
.unwrap_or(Cow::Borrowed(segment)) .unwrap_or(Cow::Borrowed(segment))
.into_owned() .into_owned()
}) })

View File

@ -38,6 +38,7 @@ html-escape = { workspace = true }
http = { workspace = true } http = { workspace = true }
itertools = { workspace = true } itertools = { workspace = true }
jiff = { workspace = true } jiff = { workspace = true }
percent-encoding = { workspace = true }
reqwest = { workspace = true } reqwest = { workspace = true }
reqwest-middleware = { workspace = true } reqwest-middleware = { workspace = true }
reqwest-retry = { workspace = true } reqwest-retry = { workspace = true }
@ -52,7 +53,6 @@ tokio = { workspace = true }
tokio-util = { workspace = true } tokio-util = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
url = { workspace = true } url = { workspace = true }
urlencoding = { workspace = true }
[dev-dependencies] [dev-dependencies]
anyhow = { workspace = true } anyhow = { workspace = true }

View File

@ -93,7 +93,7 @@ impl SimpleHtml {
// Extract the hash, which should be in the fragment. // Extract the hash, which should be in the fragment.
let decoded = html_escape::decode_html_entities(href); let decoded = html_escape::decode_html_entities(href);
let (path, hashes) = if let Some((path, fragment)) = decoded.split_once('#') { let (path, hashes) = if let Some((path, fragment)) = decoded.split_once('#') {
let fragment = urlencoding::decode(fragment)?; let fragment = percent_encoding::percent_decode_str(fragment).decode_utf8()?;
( (
path, path,
if fragment.trim().is_empty() { if fragment.trim().is_empty() {
@ -131,7 +131,8 @@ impl SimpleHtml {
let filename = filename.split('?').next().unwrap_or(filename); let filename = filename.split('?').next().unwrap_or(filename);
// Unquote the filename. // Unquote the filename.
let filename = urlencoding::decode(filename) let filename = percent_encoding::percent_decode_str(filename)
.decode_utf8()
.map_err(|_| Error::UnsupportedFilename(filename.to_string()))?; .map_err(|_| Error::UnsupportedFilename(filename.to_string()))?;
// Extract the `requires-python` value, which should be set on the // Extract the `requires-python` value, which should be set on the

View File

@ -34,6 +34,7 @@ fs-err = { workspace = true }
itertools = { workspace = true } itertools = { workspace = true }
jiff = { workspace = true } jiff = { workspace = true }
owo-colors = { workspace = true } owo-colors = { workspace = true }
percent-encoding = { workspace = true }
petgraph = { workspace = true } petgraph = { workspace = true }
rkyv = { workspace = true } rkyv = { workspace = true }
rustc-hash = { workspace = true } rustc-hash = { workspace = true }
@ -43,5 +44,4 @@ serde_json = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
url = { workspace = true } url = { workspace = true }
urlencoding = { workspace = true }
version-ranges = { workspace = true } version-ranges = { workspace = true }

View File

@ -8,7 +8,7 @@ pub enum Error {
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
#[error(transparent)] #[error(transparent)]
Utf8(#[from] std::string::FromUtf8Error), Utf8(#[from] std::str::Utf8Error),
#[error(transparent)] #[error(transparent)]
WheelFilename(#[from] uv_distribution_filename::WheelFilenameError), WheelFilename(#[from] uv_distribution_filename::WheelFilenameError),

View File

@ -923,7 +923,7 @@ impl RemoteSource for Url {
let last = path_segments.last().expect("path segments is non-empty"); let last = path_segments.last().expect("path segments is non-empty");
// Decode the filename, which may be percent-encoded. // Decode the filename, which may be percent-encoded.
let filename = urlencoding::decode(last)?; let filename = percent_encoding::percent_decode_str(last).decode_utf8()?;
Ok(filename) Ok(filename)
} }
@ -943,7 +943,7 @@ impl RemoteSource for UrlString {
.ok_or_else(|| Error::MissingPathSegments(self.to_string()))?; .ok_or_else(|| Error::MissingPathSegments(self.to_string()))?;
// Decode the filename, which may be percent-encoded. // Decode the filename, which may be percent-encoded.
let filename = urlencoding::decode(last)?; let filename = percent_encoding::percent_decode_str(last).decode_utf8()?;
Ok(filename) Ok(filename)
} }

View File

@ -16,7 +16,6 @@ doctest = false
workspace = true workspace = true
[dependencies] [dependencies]
cachedir = { workspace = true } cachedir = { workspace = true }
dunce = { workspace = true } dunce = { workspace = true }
either = { workspace = true } either = { workspace = true }
@ -24,12 +23,12 @@ encoding_rs_io = { workspace = true }
fs-err = { workspace = true } fs-err = { workspace = true }
fs2 = { workspace = true } fs2 = { workspace = true }
path-slash = { workspace = true } path-slash = { workspace = true }
percent-encoding = { workspace = true }
schemars = { workspace = true, optional = true } schemars = { workspace = true, optional = true }
serde = { workspace = true, optional = true } serde = { workspace = true, optional = true }
tokio = { workspace = true, optional = true}
tempfile = { workspace = true } tempfile = { workspace = true }
tokio = { workspace = true, optional = true}
tracing = { workspace = true } tracing = { workspace = true }
urlencoding = { workspace = true }
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
winsafe = { workspace = true } winsafe = { workspace = true }

View File

@ -139,7 +139,9 @@ impl<T: AsRef<Path>> PythonExt for T {
/// On other platforms, this is a no-op. /// On other platforms, this is a no-op.
pub fn normalize_url_path(path: &str) -> Cow<'_, str> { pub fn normalize_url_path(path: &str) -> Cow<'_, str> {
// Apply percent-decoding to the URL. // Apply percent-decoding to the URL.
let path = urlencoding::decode(path).unwrap_or(Cow::Borrowed(path)); let path = percent_encoding::percent_decode_str(path)
.decode_utf8()
.unwrap_or(Cow::Borrowed(path));
// Return the path. // Return the path.
if cfg!(windows) { if cfg!(windows) {