mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-01 20:31:12 +00:00
Propagate URL errors in verbatim parsing (#3720)
## Summary Closes https://github.com/astral-sh/uv/issues/3715. ## Test Plan ``` ❯ echo "/../test" | cargo run pip compile - error: Couldn't parse requirement in `-` at position 0 Caused by: path could not be normalized: /../test /../test ^^^^^^^^ ❯ echo "-e /../test" | cargo run pip compile - error: Invalid URL in `-`: `/../test` Caused by: path could not be normalized: /../test Caused by: cannot normalize a relative path beyond the base directory ```
This commit is contained in:
parent
19df1a4372
commit
558f628ef1
9 changed files with 97 additions and 40 deletions
|
|
@ -236,6 +236,12 @@ fn preprocess_unnamed_url(
|
|||
#[cfg(feature = "non-pep508-extensions")]
|
||||
if let Some(working_dir) = working_dir {
|
||||
let url = VerbatimUrl::parse_path(path.as_ref(), working_dir)
|
||||
.map_err(|err| Pep508Error {
|
||||
message: Pep508ErrorSource::<VerbatimUrl>::UrlError(err),
|
||||
start,
|
||||
len,
|
||||
input: cursor.to_string(),
|
||||
})?
|
||||
.with_given(url.to_string());
|
||||
return Ok((url, extras));
|
||||
}
|
||||
|
|
@ -270,6 +276,12 @@ fn preprocess_unnamed_url(
|
|||
_ => {
|
||||
if let Some(working_dir) = working_dir {
|
||||
let url = VerbatimUrl::parse_path(expanded.as_ref(), working_dir)
|
||||
.map_err(|err| Pep508Error {
|
||||
message: Pep508ErrorSource::<VerbatimUrl>::UrlError(err),
|
||||
start,
|
||||
len,
|
||||
input: cursor.to_string(),
|
||||
})?
|
||||
.with_given(url.to_string());
|
||||
return Ok((url, extras));
|
||||
}
|
||||
|
|
@ -288,8 +300,14 @@ fn preprocess_unnamed_url(
|
|||
} else {
|
||||
// Ex) `../editable/`
|
||||
if let Some(working_dir) = working_dir {
|
||||
let url =
|
||||
VerbatimUrl::parse_path(expanded.as_ref(), working_dir).with_given(url.to_string());
|
||||
let url = VerbatimUrl::parse_path(expanded.as_ref(), working_dir)
|
||||
.map_err(|err| Pep508Error {
|
||||
message: Pep508ErrorSource::<VerbatimUrl>::UrlError(err),
|
||||
start,
|
||||
len,
|
||||
input: cursor.to_string(),
|
||||
})?
|
||||
.with_given(url.to_string());
|
||||
return Ok((url, extras));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,25 +38,26 @@ impl VerbatimUrl {
|
|||
/// Create a [`VerbatimUrl`] from a file path.
|
||||
///
|
||||
/// Assumes that the path is absolute.
|
||||
pub fn from_path(path: impl AsRef<Path>) -> Self {
|
||||
pub fn from_path(path: impl AsRef<Path>) -> Result<Self, VerbatimUrlError> {
|
||||
let path = path.as_ref();
|
||||
|
||||
// Normalize the path.
|
||||
let path = normalize_path(path).expect("path is absolute");
|
||||
let path = normalize_path(path)
|
||||
.map_err(|err| VerbatimUrlError::Normalization(path.to_path_buf(), err))?;
|
||||
|
||||
// Extract the fragment, if it exists.
|
||||
let (path, fragment) = split_fragment(&path);
|
||||
|
||||
// Convert to a URL.
|
||||
let mut url = Url::from_file_path(path.clone())
|
||||
.unwrap_or_else(|_| panic!("path is absolute: {}", path.display()));
|
||||
.map_err(|_| VerbatimUrlError::UrlConversion(path.to_path_buf()))?;
|
||||
|
||||
// Set the fragment, if it exists.
|
||||
if let Some(fragment) = fragment {
|
||||
url.set_fragment(Some(fragment));
|
||||
}
|
||||
|
||||
Self { url, given: None }
|
||||
Ok(Self { url, given: None })
|
||||
}
|
||||
|
||||
/// Parse a URL from a string, expanding any environment variables.
|
||||
|
|
@ -67,7 +68,10 @@ impl VerbatimUrl {
|
|||
|
||||
/// Parse a URL from an absolute or relative path.
|
||||
#[cfg(feature = "non-pep508-extensions")] // PEP 508 arguably only allows absolute file URLs.
|
||||
pub fn parse_path(path: impl AsRef<Path>, working_dir: impl AsRef<Path>) -> Self {
|
||||
pub fn parse_path(
|
||||
path: impl AsRef<Path>,
|
||||
working_dir: impl AsRef<Path>,
|
||||
) -> Result<Self, VerbatimUrlError> {
|
||||
let path = path.as_ref();
|
||||
|
||||
// Convert the path to an absolute path, if necessary.
|
||||
|
|
@ -78,26 +82,22 @@ impl VerbatimUrl {
|
|||
};
|
||||
|
||||
// Normalize the path.
|
||||
let path = normalize_path(&path).expect("path is absolute");
|
||||
let path = normalize_path(&path)
|
||||
.map_err(|err| VerbatimUrlError::Normalization(path.to_path_buf(), err))?;
|
||||
|
||||
// Extract the fragment, if it exists.
|
||||
let (path, fragment) = split_fragment(&path);
|
||||
|
||||
// Convert to a URL.
|
||||
let mut url = Url::from_file_path(path.clone()).unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"path is absolute: {}, {}",
|
||||
path.display(),
|
||||
working_dir.as_ref().display()
|
||||
)
|
||||
});
|
||||
let mut url = Url::from_file_path(path.clone())
|
||||
.map_err(|_| VerbatimUrlError::UrlConversion(path.to_path_buf()))?;
|
||||
|
||||
// Set the fragment, if it exists.
|
||||
if let Some(fragment) = fragment {
|
||||
url.set_fragment(Some(fragment));
|
||||
}
|
||||
|
||||
Self { url, given: None }
|
||||
Ok(Self { url, given: None })
|
||||
}
|
||||
|
||||
/// Parse a URL from an absolute path.
|
||||
|
|
@ -108,12 +108,12 @@ impl VerbatimUrl {
|
|||
let path = if path.is_absolute() {
|
||||
path.to_path_buf()
|
||||
} else {
|
||||
return Err(VerbatimUrlError::RelativePath(path.to_path_buf()));
|
||||
return Err(VerbatimUrlError::WorkingDirectory(path.to_path_buf()));
|
||||
};
|
||||
|
||||
// Normalize the path.
|
||||
let Ok(path) = normalize_path(&path) else {
|
||||
return Err(VerbatimUrlError::RelativePath(path));
|
||||
return Err(VerbatimUrlError::WorkingDirectory(path));
|
||||
};
|
||||
|
||||
// Extract the fragment, if it exists.
|
||||
|
|
@ -226,7 +226,7 @@ impl Pep508Url for VerbatimUrl {
|
|||
|
||||
#[cfg(feature = "non-pep508-extensions")]
|
||||
if let Some(working_dir) = working_dir {
|
||||
return Ok(VerbatimUrl::parse_path(path.as_ref(), working_dir)
|
||||
return Ok(VerbatimUrl::parse_path(path.as_ref(), working_dir)?
|
||||
.with_given(url.to_string()));
|
||||
}
|
||||
|
||||
|
|
@ -245,7 +245,7 @@ impl Pep508Url for VerbatimUrl {
|
|||
_ => {
|
||||
#[cfg(feature = "non-pep508-extensions")]
|
||||
if let Some(working_dir) = working_dir {
|
||||
return Ok(VerbatimUrl::parse_path(expanded.as_ref(), working_dir)
|
||||
return Ok(VerbatimUrl::parse_path(expanded.as_ref(), working_dir)?
|
||||
.with_given(url.to_string()));
|
||||
}
|
||||
|
||||
|
|
@ -257,7 +257,7 @@ impl Pep508Url for VerbatimUrl {
|
|||
// Ex) `../editable/`
|
||||
#[cfg(feature = "non-pep508-extensions")]
|
||||
if let Some(working_dir) = working_dir {
|
||||
return Ok(VerbatimUrl::parse_path(expanded.as_ref(), working_dir)
|
||||
return Ok(VerbatimUrl::parse_path(expanded.as_ref(), working_dir)?
|
||||
.with_given(url.to_string()));
|
||||
}
|
||||
|
||||
|
|
@ -275,7 +275,15 @@ pub enum VerbatimUrlError {
|
|||
|
||||
/// Received a relative path, but no working directory was provided.
|
||||
#[error("relative path without a working directory: {0}")]
|
||||
RelativePath(PathBuf),
|
||||
WorkingDirectory(PathBuf),
|
||||
|
||||
/// Received a path that could not be converted to a URL.
|
||||
#[error("path could not be converted to a URL: {0}")]
|
||||
UrlConversion(PathBuf),
|
||||
|
||||
/// Received a path that could not be normalized.
|
||||
#[error("path could not be normalized: {0}")]
|
||||
Normalization(PathBuf, #[source] std::io::Error),
|
||||
}
|
||||
|
||||
/// Expand all available environment variables.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue