Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 78 additions & 18 deletions crates/uv-pep508/src/verbatim_url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub struct VerbatimUrl {
/// Even if originally set, this will be [`None`] after
/// serialization/deserialization.
given: Option<ArcStr>,
/// Given value is a [`Pep508Url`] which contained variable references which were successfully
/// expanded.
expanded: bool,
}

impl Hash for VerbatimUrl {
Expand All @@ -53,15 +56,23 @@ impl PartialEq for VerbatimUrl {
impl VerbatimUrl {
/// Create a [`VerbatimUrl`] from a [`Url`].
pub fn from_url(url: DisplaySafeUrl) -> Self {
Self { url, given: None }
Self {
url,
given: None,
expanded: false,
}
}

/// Parse a URL from a string.
pub fn parse_url(given: impl AsRef<str>) -> Result<Self, VerbatimUrlError> {
let given = given.as_ref();
let url = DisplaySafeUrl::parse(given)?;

Ok(Self { url, given: None })
Ok(Self {
url,
given: None,
expanded: false,
})
}

/// Convert a [`VerbatimUrl`] from a path or a URL.
Expand Down Expand Up @@ -138,7 +149,11 @@ impl VerbatimUrl {
url.set_fragment(Some(fragment));
}

Ok(Self { url, given: None })
Ok(Self {
url,
given: None,
expanded: false,
})
}

/// Parse a URL from an absolute path.
Expand Down Expand Up @@ -168,7 +183,11 @@ impl VerbatimUrl {
url.set_fragment(Some(fragment));
}

Ok(Self { url, given: None })
Ok(Self {
url,
given: None,
expanded: false,
})
}

/// Parse a URL from a normalized path.
Expand Down Expand Up @@ -196,7 +215,11 @@ impl VerbatimUrl {
url.set_fragment(Some(fragment));
}

Ok(Self { url, given: None })
Ok(Self {
url,
given: None,
expanded: false,
})
}

/// Set the verbatim representation of the URL.
Expand All @@ -214,20 +237,35 @@ impl VerbatimUrl {
}

/// Returns `true` if the `given` input was an absolute path or file URL.
///
/// If the URL was a PEP 508 URL which contained environment variable references which were
/// expanded. This function returns false to preserve existing usecases which may rely on
/// things like `${PWD}` or `${PROJECT_ROOT}`.
pub fn was_given_absolute(&self) -> bool {
let Some(given) = &self.given else {
return false;
};
if self.expanded {
return false;
}

if let Some((scheme, _)) = split_scheme(given) {
if let Some(parsed_scheme) = Scheme::parse(scheme) {
return parsed_scheme.is_file();
}
if let Some((scheme, _)) = split_scheme(given)
&& let Some(parsed_scheme) = Scheme::parse(scheme)
{
return parsed_scheme.is_file();
}

Path::new(given.as_str()).is_absolute()
}

/// Set the "given value contained variables which were expanded" flag.
///
/// Intended to only be used by the [`Pep508Url`] impl.
#[must_use]
fn with_expanded(self, expanded: bool) -> Self {
Self { expanded, ..self }
}

/// Return the underlying [`DisplaySafeUrl`].
pub fn raw(&self) -> &DisplaySafeUrl {
&self.url
Expand Down Expand Up @@ -336,6 +374,16 @@ impl Pep508Url for VerbatimUrl {
// Expand environment variables in the URL.
let expanded = expand_env_vars(url);

// Since `expand_env_vars` can return `Cow::Owned` even when variables were not expanded,
// the check needs to fall back to comparison for that case.
//
// Note: If a variable named `FOO` expands to `${FOO}` then this will produce a false
// negative. This seems like too much of a corner case to justify trying to fix it.
let vars_expanded = match &expanded {
Cow::Owned(owned) => owned != url,
Cow::Borrowed(_) => false,
};

if let Some((scheme, path)) = split_scheme(&expanded) {
match Scheme::parse(scheme) {
// Ex) `file:///home/ferris/project/scripts/...`, `file://localhost/home/ferris/project/scripts/...`, or `file:../ferris/`
Expand All @@ -350,13 +398,19 @@ impl Pep508Url for VerbatimUrl {
let path = normalize_url_path(path);

if let Some(working_dir) = working_dir {
return Ok(Self::from_path(path.as_ref(), working_dir)?.with_given(url));
return Ok(Self::from_path(path.as_ref(), working_dir)?
.with_given(url)
.with_expanded(vars_expanded));
}

Ok(Self::from_absolute_path(path.as_ref())?.with_given(url))
Ok(Self::from_absolute_path(path.as_ref())?
.with_given(url)
.with_expanded(vars_expanded))
}
#[cfg(not(feature = "non-pep508-extensions"))]
Ok(Self::parse_url(expanded)?.with_given(url))
Ok(Self::parse_url(expanded)?
.with_given(url)
.with_expanded(vars_expanded))
}

// Ex) `https://download.pytorch.org/whl/torch_stable.html`
Expand All @@ -370,12 +424,14 @@ impl Pep508Url for VerbatimUrl {
#[cfg(feature = "non-pep508-extensions")]
{
if let Some(working_dir) = working_dir {
return Ok(
Self::from_path(expanded.as_ref(), working_dir)?.with_given(url)
);
return Ok(Self::from_path(expanded.as_ref(), working_dir)?
.with_given(url)
.with_expanded(vars_expanded));
}

Ok(Self::from_absolute_path(expanded.as_ref())?.with_given(url))
Ok(Self::from_absolute_path(expanded.as_ref())?
.with_given(url)
.with_expanded(vars_expanded))
}
#[cfg(not(feature = "non-pep508-extensions"))]
Err(Self::Err::NotAUrl(expanded.to_string()))
Expand All @@ -386,10 +442,14 @@ impl Pep508Url for VerbatimUrl {
#[cfg(feature = "non-pep508-extensions")]
{
if let Some(working_dir) = working_dir {
return Ok(Self::from_path(expanded.as_ref(), working_dir)?.with_given(url));
return Ok(Self::from_path(expanded.as_ref(), working_dir)?
.with_given(url)
.with_expanded(vars_expanded));
}

Ok(Self::from_absolute_path(expanded.as_ref())?.with_given(url))
Ok(Self::from_absolute_path(expanded.as_ref())?
.with_given(url)
.with_expanded(vars_expanded))
}

#[cfg(not(feature = "non-pep508-extensions"))]
Expand Down
8 changes: 8 additions & 0 deletions crates/uv-requirements-txt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2201,6 +2201,7 @@ mod test {
given: Some(
"/foo/bar",
),
expanded: false,
},
},
extras: [],
Expand Down Expand Up @@ -2463,6 +2464,7 @@ mod test {
given: Some(
"https://test.pypi.org/simple/",
),
expanded: false,
},
),
extra_index_urls: [],
Expand Down Expand Up @@ -2549,6 +2551,7 @@ mod test {
given: Some(
"importlib_metadata-8.3.0-py3-none-any.whl",
),
expanded: false,
},
},
extras: [],
Expand Down Expand Up @@ -2598,6 +2601,7 @@ mod test {
given: Some(
"importlib_metadata-8.2.0-py3-none-any.whl",
),
expanded: false,
},
},
extras: [],
Expand Down Expand Up @@ -2647,6 +2651,7 @@ mod test {
given: Some(
"importlib_metadata-8.2.0-py3-none-any.whl",
),
expanded: false,
},
},
extras: [
Expand Down Expand Up @@ -2700,6 +2705,7 @@ mod test {
given: Some(
"importlib_metadata-8.2.0+local-py3-none-any.whl",
),
expanded: false,
},
},
extras: [],
Expand Down Expand Up @@ -2749,6 +2755,7 @@ mod test {
given: Some(
"importlib_metadata-8.2.0+local-py3-none-any.whl",
),
expanded: false,
},
},
extras: [],
Expand Down Expand Up @@ -2798,6 +2805,7 @@ mod test {
given: Some(
"importlib_metadata-8.2.0+local-py3-none-any.whl",
),
expanded: false,
},
},
extras: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ RequirementsTxt {
given: Some(
"git+https://github.com/pandas-dev/pandas.git",
),
expanded: false,
},
},
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ RequirementsTxt {
given: Some(
"./test/packages/black_editable",
),
expanded: false,
},
},
extras: [],
Expand Down Expand Up @@ -91,6 +92,7 @@ RequirementsTxt {
given: Some(
"./test/packages/black_editable",
),
expanded: false,
},
},
extras: [
Expand Down Expand Up @@ -145,6 +147,7 @@ RequirementsTxt {
given: Some(
"file:///test/packages/black_editable",
),
expanded: false,
},
},
extras: [],
Expand Down Expand Up @@ -195,6 +198,7 @@ RequirementsTxt {
given: Some(
"./test/packages/black editable",
),
expanded: false,
},
},
extras: [],
Expand Down Expand Up @@ -245,6 +249,7 @@ RequirementsTxt {
given: Some(
"./test/packages/black editable",
),
expanded: false,
},
},
extras: [],
Expand Down Expand Up @@ -295,6 +300,7 @@ RequirementsTxt {
given: Some(
"./test/packages/black editable",
),
expanded: false,
},
},
extras: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ RequirementsTxt {
given: Some(
"./editable",
),
expanded: false,
},
},
extras: [
Expand Down Expand Up @@ -104,6 +105,7 @@ RequirementsTxt {
given: Some(
"./editable",
),
expanded: false,
},
},
extras: [
Expand Down Expand Up @@ -163,6 +165,7 @@ RequirementsTxt {
given: Some(
"./editable",
),
expanded: false,
},
},
extras: [
Expand Down Expand Up @@ -222,6 +225,7 @@ RequirementsTxt {
given: Some(
"./editable",
),
expanded: false,
},
},
extras: [
Expand Down Expand Up @@ -281,6 +285,7 @@ RequirementsTxt {
given: Some(
"./editable",
),
expanded: false,
},
},
extras: [],
Expand Down Expand Up @@ -333,6 +338,7 @@ RequirementsTxt {
given: Some(
"./editable[d",
),
expanded: false,
},
},
extras: [],
Expand Down Expand Up @@ -385,6 +391,7 @@ RequirementsTxt {
given: Some(
"./editable",
),
expanded: false,
},
},
extras: [],
Expand Down Expand Up @@ -437,6 +444,7 @@ RequirementsTxt {
given: Some(
"./editable",
),
expanded: false,
},
},
extras: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ RequirementsTxt {
given: Some(
"git+https://github.com/pandas-dev/pandas.git",
),
expanded: false,
},
},
),
Expand Down
Loading
Loading