Skip to content

Commit

Permalink
Allow path with fragment
Browse files Browse the repository at this point in the history
  • Loading branch information
eth3lbert committed Mar 18, 2024
1 parent 653327b commit 4105519
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 3 deletions.
24 changes: 24 additions & 0 deletions crates/pep508-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1744,4 +1744,28 @@ mod tests {
let requirement = Requirement::from_str("pytest;'4.0'>=python_version").unwrap();
assert_eq!(requirement.to_string(), "pytest ; '4.0' >= python_version");
}

#[test]
fn path_with_fragment() {
let requirements = &[
"foo @ file:///foo-3.0.0-py3-none-any.whl#hash=somehash",
"foo @ /foo-3.0.0-py3-none-any.whl#hash=somehash",
];

for requirement in requirements {
if let VersionOrUrl::Url(url) = Requirement::from_str(requirement)
.unwrap()
.version_or_url
.unwrap()
{
assert_eq!(url.path(), "/foo-3.0.0-py3-none-any.whl");
assert_eq!(url.fragment(), Some("hash=somehash"));
assert!(url.given().unwrap().ends_with(&format!(
"{}#{}",
url.path(),
url.fragment().unwrap_or("")
)));
}
}
}
}
65 changes: 62 additions & 3 deletions crates/pep508-rs/src/verbatim_url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,17 @@ impl VerbatimUrl {

/// Create a [`VerbatimUrl`] from a file path.
pub fn from_path(path: impl AsRef<Path>) -> Self {
// Split fragment from path
let path_str = path.as_ref().to_string_lossy();
let (path, fragment) = split_fragment(path_str.as_ref());

let path = normalize_path(path.as_ref());
let url = Url::from_file_path(path).expect("path is absolute");
let mut url = Url::from_file_path(path).expect("path is absolute");

// Set fragment for file url if exists
if fragment.is_some() {
url.set_fragment(fragment);
}
Self { url, given: None }
}

Expand All @@ -51,6 +60,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 {
// Split fragment from path
let path_str = path.as_ref().to_string_lossy();
let (path, fragment) = split_fragment(path_str.as_ref());

// Convert the path to an absolute path, if necessary.
let path = if path.as_ref().is_absolute() {
path.as_ref().to_path_buf()
Expand All @@ -62,13 +75,22 @@ impl VerbatimUrl {
let path = normalize_path(path);

// Convert to a URL.
let url = Url::from_file_path(path).expect("path is absolute");
let mut url = Url::from_file_path(path).expect("path is absolute");

// Set fragment for file url if exists
if fragment.is_some() {
url.set_fragment(fragment);
}

Self { url, given: None }
}

/// Parse a URL from an absolute path.
pub fn parse_absolute_path(path: impl AsRef<Path>) -> Result<Self, VerbatimUrlError> {
// Split fragment from path
let path_str = path.as_ref().to_string_lossy();
let (path, fragment) = split_fragment(path_str.as_ref());

// Convert the path to an absolute path, if necessary.
let path = if path.as_ref().is_absolute() {
path.as_ref().to_path_buf()
Expand All @@ -80,7 +102,12 @@ impl VerbatimUrl {
let path = normalize_path(path);

// Convert to a URL.
let url = Url::from_file_path(path).expect("path is absolute");
let mut url = Url::from_file_path(path).expect("path is absolute");

// Set fragment for file url if exists
if fragment.is_some() {
url.set_fragment(fragment);
}

Ok(Self { url, given: None })
}
Expand Down Expand Up @@ -222,6 +249,12 @@ pub fn split_scheme(s: &str) -> Option<(&str, &str)> {
Some((scheme, rest))
}

// split fragment from string
fn split_fragment(s: &str) -> (impl AsRef<Path> + '_, Option<&str>) {
s.split_once('#')
.map_or_else(|| (s, None), |(path, fragment)| (path, Some(fragment)))
}

/// A supported URL scheme for PEP 508 direct-URL requirements.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Scheme {
Expand Down Expand Up @@ -363,4 +396,30 @@ mod tests {
);
assert_eq!(split_scheme("https:"), Some(("https", "")));
}

#[test]
fn fragment() {
for (s, expected) in [
(
"file:///home/ferris/project/scripts#hash=somehash",
("file:///home/ferris/project/scripts", Some("hash=somehash")),
),
(
"file:home/ferris/project/scripts#hash=somehash",
("file:home/ferris/project/scripts", Some("hash=somehash")),
),
(
"/home/ferris/project/scripts#hash=somehash",
("/home/ferris/project/scripts", Some("hash=somehash")),
),
(
"file:///home/ferris/project/scripts",
("file:///home/ferris/project/scripts", None),
),
("", ("", None)),
] {
let (path, frag) = split_fragment(s);
assert_eq!((path.as_ref(), frag), (Path::new(expected.0), expected.1));
}
}
}

0 comments on commit 4105519

Please sign in to comment.