mirror of https://github.com/astral-sh/uv
Normalize trailing slashes only during lockfile validation
This commit is contained in:
parent
ddb1577a93
commit
1f8217f7bb
|
|
@ -176,6 +176,10 @@ impl UrlString {
|
|||
/// it's the only path segment, e.g., `https://example.com/` would be unchanged.
|
||||
#[must_use]
|
||||
pub fn without_trailing_slash(&self) -> Cow<'_, Self> {
|
||||
if !self.as_ref().ends_with('/') {
|
||||
return Cow::Borrowed(self);
|
||||
}
|
||||
|
||||
self.as_ref()
|
||||
.strip_suffix('/')
|
||||
.filter(|path| {
|
||||
|
|
|
|||
|
|
@ -258,20 +258,13 @@ impl<'de> serde::de::Deserialize<'de> for IndexUrl {
|
|||
}
|
||||
|
||||
impl From<VerbatimUrl> for IndexUrl {
|
||||
fn from(mut url: VerbatimUrl) -> Self {
|
||||
fn from(url: VerbatimUrl) -> Self {
|
||||
if url.scheme() == "file" {
|
||||
Self::Path(Arc::new(url))
|
||||
} else if *url.raw() == *PYPI_URL {
|
||||
Self::Pypi(Arc::new(url))
|
||||
} else {
|
||||
// Remove trailing slashes for consistency. They'll be re-added if necessary when
|
||||
// querying the Simple API.
|
||||
if let Ok(mut path_segments) = url.raw_mut().path_segments_mut() {
|
||||
path_segments.pop_if_empty();
|
||||
}
|
||||
if *url.raw() == *PYPI_URL {
|
||||
Self::Pypi(Arc::new(url))
|
||||
} else {
|
||||
Self::Url(Arc::new(url))
|
||||
}
|
||||
Self::Url(Arc::new(url))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,11 +51,8 @@ impl VerbatimUrl {
|
|||
|
||||
/// Parse a URL from a string.
|
||||
pub fn parse_url(given: impl AsRef<str>) -> Result<Self, ParseError> {
|
||||
let url = Url::parse(given.as_ref())?;
|
||||
Ok(Self {
|
||||
url: DisplaySafeUrl::from(url),
|
||||
given: None,
|
||||
})
|
||||
let url = DisplaySafeUrl::parse(given.as_ref())?;
|
||||
Ok(Self { url, given: None })
|
||||
}
|
||||
|
||||
/// Parse a URL from an absolute or relative path.
|
||||
|
|
@ -193,6 +190,32 @@ impl VerbatimUrl {
|
|||
.to_file_path()
|
||||
.map_err(|()| VerbatimUrlError::UrlConversion(self.url.to_file_path().unwrap()))
|
||||
}
|
||||
|
||||
/// Return the [`VerbatimUrl`] (as a [`Cow`]) with trailing slash removed.
|
||||
///
|
||||
/// This matches the semantics of [`Url::pop_if_empty`], which will not trim a trailing slash if
|
||||
/// it's the only path segment, e.g., `https://example.com/` would be unchanged.
|
||||
#[must_use]
|
||||
pub fn without_trailing_slash(&self) -> Cow<'_, Self> {
|
||||
if !self.as_ref().ends_with('/') {
|
||||
return Cow::Borrowed(self);
|
||||
}
|
||||
|
||||
self.as_ref()
|
||||
.strip_suffix('/')
|
||||
.filter(|path| {
|
||||
// Only strip the trailing slash if there's _another_ trailing slash that isn't a
|
||||
// part of the scheme.
|
||||
path.split_once("://")
|
||||
.map(|(_scheme, rest)| rest)
|
||||
.unwrap_or(path)
|
||||
.contains('/')
|
||||
})
|
||||
.map(|path| {
|
||||
Cow::Owned(VerbatimUrl::parse_url(path).expect("URL should still be parseable"))
|
||||
})
|
||||
.unwrap_or(Cow::Borrowed(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for VerbatimUrl {
|
||||
|
|
|
|||
|
|
@ -1433,7 +1433,8 @@ impl Lock {
|
|||
.into_iter()
|
||||
.filter_map(|index| match index.url() {
|
||||
IndexUrl::Pypi(_) | IndexUrl::Url(_) => {
|
||||
Some(UrlString::from(index.url().without_credentials().as_ref()))
|
||||
let url = UrlString::from(index.url().without_credentials().as_ref());
|
||||
Some(url.without_trailing_slash().into_owned())
|
||||
}
|
||||
IndexUrl::Path(_) => None,
|
||||
})
|
||||
|
|
@ -4825,7 +4826,13 @@ fn normalize_requirement(
|
|||
index.remove_credentials();
|
||||
index
|
||||
})
|
||||
.map(|index| IndexMetadata::from(IndexUrl::from(VerbatimUrl::from_url(index))));
|
||||
.map(|index| {
|
||||
IndexMetadata::from(IndexUrl::from(
|
||||
VerbatimUrl::from_url(index)
|
||||
.without_trailing_slash()
|
||||
.into_owned(),
|
||||
))
|
||||
});
|
||||
Ok(Requirement {
|
||||
name: requirement.name,
|
||||
extras: requirement.extras,
|
||||
|
|
@ -4867,7 +4874,9 @@ fn normalize_requirement(
|
|||
location,
|
||||
subdirectory,
|
||||
ext,
|
||||
url: VerbatimUrl::from_url(url),
|
||||
url: VerbatimUrl::from_url(url)
|
||||
.without_trailing_slash()
|
||||
.into_owned(),
|
||||
},
|
||||
origin: None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -4384,7 +4384,7 @@ fn add_lower_bound_local() -> Result<()> {
|
|||
]
|
||||
|
||||
[[tool.uv.index]]
|
||||
url = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html"
|
||||
url = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/"
|
||||
"#
|
||||
);
|
||||
});
|
||||
|
|
@ -4403,7 +4403,7 @@ fn add_lower_bound_local() -> Result<()> {
|
|||
[[package]]
|
||||
name = "local-simple-a"
|
||||
version = "1.2.3+foo"
|
||||
source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html" }
|
||||
source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }
|
||||
sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/local_simple_a-1.2.3+foo.tar.gz", hash = "sha256:ebd55c4a79d0a5759126657cb289ff97558902abcfb142e036b993781497edac" }
|
||||
wheels = [
|
||||
{ url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/local_simple_a-1.2.3+foo-py3-none-any.whl", hash = "sha256:6f30e2e709b3e171cd734bb58705229a582587c29e0a7041227435583c7224cc" },
|
||||
|
|
@ -9272,7 +9272,7 @@ fn add_index_with_trailing_slash() -> Result<()> {
|
|||
constraint-dependencies = ["markupsafe<3"]
|
||||
|
||||
[[tool.uv.index]]
|
||||
url = "https://pypi.org/simple"
|
||||
url = "https://pypi.org/simple/"
|
||||
"#
|
||||
);
|
||||
});
|
||||
|
|
@ -9297,7 +9297,7 @@ fn add_index_with_trailing_slash() -> Result<()> {
|
|||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
source = { registry = "https://pypi.org/simple/" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646, upload-time = "2023-01-07T11:08:11.254Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892, upload-time = "2023-01-07T11:08:09.864Z" },
|
||||
|
|
@ -11204,7 +11204,7 @@ fn repeated_index_cli_reversed() -> Result<()> {
|
|||
]
|
||||
|
||||
[[tool.uv.index]]
|
||||
url = "https://test.pypi.org/simple"
|
||||
url = "https://test.pypi.org/simple/"
|
||||
"#
|
||||
);
|
||||
});
|
||||
|
|
@ -11226,7 +11226,7 @@ fn repeated_index_cli_reversed() -> Result<()> {
|
|||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.0.0"
|
||||
source = { registry = "https://test.pypi.org/simple" }
|
||||
source = { registry = "https://test.pypi.org/simple/" }
|
||||
sdist = { url = "https://test-files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646, upload-time = "2023-01-07T11:08:16.826Z" }
|
||||
wheels = [
|
||||
{ url = "https://test-files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892, upload-time = "2023-01-07T11:08:14.843Z" },
|
||||
|
|
|
|||
|
|
@ -15543,7 +15543,7 @@ fn lock_trailing_slash() -> Result<()> {
|
|||
[[package]]
|
||||
name = "anyio"
|
||||
version = "3.7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
source = { registry = "https://pypi.org/simple/" }
|
||||
dependencies = [
|
||||
{ name = "idna" },
|
||||
{ name = "sniffio" },
|
||||
|
|
@ -15556,7 +15556,7 @@ fn lock_trailing_slash() -> Result<()> {
|
|||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
source = { registry = "https://pypi.org/simple/" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426, upload-time = "2023-11-25T15:40:54.902Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567, upload-time = "2023-11-25T15:40:52.604Z" },
|
||||
|
|
@ -15576,7 +15576,7 @@ fn lock_trailing_slash() -> Result<()> {
|
|||
[[package]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
source = { registry = "https://pypi.org/simple/" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -9939,7 +9939,7 @@ fn sync_required_environment_hint() -> Result<()> {
|
|||
|
||||
----- stderr -----
|
||||
Resolved 2 packages in [TIME]
|
||||
error: Distribution `no-sdist-no-wheels-with-matching-platform-a==1.0.0 @ registry+https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html` can't be installed because it doesn't have a source distribution or wheel for the current platform
|
||||
error: Distribution `no-sdist-no-wheels-with-matching-platform-a==1.0.0 @ registry+https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/` can't be installed because it doesn't have a source distribution or wheel for the current platform
|
||||
|
||||
hint: You're on [PLATFORM] (`[TAG]`), but `no-sdist-no-wheels-with-matching-platform-a` (v1.0.0) only has wheels for the following platform: `macosx_10_0_ppc64`; consider adding your platform to `tool.uv.required-environments` to ensure uv resolves to a version with compatible wheels
|
||||
");
|
||||
|
|
|
|||
Loading…
Reference in New Issue