diff --git a/crates/uv-pep508/src/verbatim_url.rs b/crates/uv-pep508/src/verbatim_url.rs index 817675dc8..31e099bca 100644 --- a/crates/uv-pep508/src/verbatim_url.rs +++ b/crates/uv-pep508/src/verbatim_url.rs @@ -112,6 +112,34 @@ impl VerbatimUrl { Ok(Self { url, given: None }) } + /// Parse a URL from a normalized path. + /// + /// Like [`VerbatimUrl::from_absolute_path`], but skips the normalization step. + pub fn from_normalized_path(path: impl AsRef) -> Result { + let path = path.as_ref(); + + // Error if the path is relative. + let path = if path.is_absolute() { + path + } else { + return Err(VerbatimUrlError::WorkingDirectory(path.to_path_buf())); + }; + + // 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())); + + // Set the fragment, if it exists. + if let Some(fragment) = fragment { + url.set_fragment(Some(fragment)); + } + + Ok(Self { url, given: None }) + } + /// Set the verbatim representation of the URL. #[must_use] pub fn with_given(self, given: impl AsRef) -> Self { diff --git a/crates/uv-pypi-types/src/requirement.rs b/crates/uv-pypi-types/src/requirement.rs index ee8ef0e4a..44ad317b1 100644 --- a/crates/uv-pypi-types/src/requirement.rs +++ b/crates/uv-pypi-types/src/requirement.rs @@ -870,10 +870,12 @@ impl TryFrom for RequirementSource { } // TODO(charlie): The use of `CWD` here is incorrect. These should be resolved relative // to the workspace root, but we don't have access to it here. When comparing these - // sources in the lockfile, we replace the URL anyway. + // sources in the lockfile, we replace the URL anyway. Ideally, we'd either remove the + // URL field or make it optional. RequirementSourceWire::Path { path } => { let path = PathBuf::from(path); - let url = VerbatimUrl::from_path(&path, &*CWD)?; + let url = + VerbatimUrl::from_normalized_path(uv_fs::normalize_path_buf(CWD.join(&path)))?; Ok(Self::Path { ext: DistExtension::from_path(path.as_path()) .map_err(|err| ParsedUrlError::MissingExtensionPath(path.clone(), err))?, @@ -883,7 +885,9 @@ impl TryFrom for RequirementSource { } RequirementSourceWire::Directory { directory } => { let directory = PathBuf::from(directory); - let url = VerbatimUrl::from_path(&directory, &*CWD)?; + let url = VerbatimUrl::from_normalized_path(uv_fs::normalize_path_buf( + CWD.join(&directory), + ))?; Ok(Self::Directory { install_path: directory, editable: false, @@ -893,7 +897,9 @@ impl TryFrom for RequirementSource { } RequirementSourceWire::Editable { editable } => { let editable = PathBuf::from(editable); - let url = VerbatimUrl::from_path(&editable, &*CWD)?; + let url = VerbatimUrl::from_normalized_path(uv_fs::normalize_path_buf( + CWD.join(&editable), + ))?; Ok(Self::Directory { install_path: editable, editable: true, @@ -903,7 +909,9 @@ impl TryFrom for RequirementSource { } RequirementSourceWire::Virtual { r#virtual } => { let r#virtual = PathBuf::from(r#virtual); - let url = VerbatimUrl::from_path(&r#virtual, &*CWD)?; + let url = VerbatimUrl::from_normalized_path(uv_fs::normalize_path_buf( + CWD.join(&r#virtual), + ))?; Ok(Self::Directory { install_path: r#virtual, editable: false, diff --git a/crates/uv-resolver/src/lock/mod.rs b/crates/uv-resolver/src/lock/mod.rs index dd38ec2c0..eab76925f 100644 --- a/crates/uv-resolver/src/lock/mod.rs +++ b/crates/uv-resolver/src/lock/mod.rs @@ -2489,12 +2489,13 @@ impl Package { } } -/// Attempts to construct a `VerbatimUrl` from the given `Path`. +/// Attempts to construct a `VerbatimUrl` from the given normalized `Path`. fn verbatim_url(path: &Path, id: &PackageId) -> Result { - let url = VerbatimUrl::from_absolute_path(path).map_err(|err| LockErrorKind::VerbatimUrl { - id: id.clone(), - err, - })?; + let url = + VerbatimUrl::from_normalized_path(path).map_err(|err| LockErrorKind::VerbatimUrl { + id: id.clone(), + err, + })?; Ok(url) } @@ -4146,7 +4147,7 @@ fn normalize_requirement(requirement: Requirement, root: &Path) -> Result { let install_path = uv_fs::normalize_path_buf(root.join(&install_path)); - let url = VerbatimUrl::from_absolute_path(&install_path) + let url = VerbatimUrl::from_normalized_path(&install_path) .map_err(LockErrorKind::RequirementVerbatimUrl)?; Ok(Requirement { @@ -4169,7 +4170,7 @@ fn normalize_requirement(requirement: Requirement, root: &Path) -> Result { let install_path = uv_fs::normalize_path_buf(root.join(&install_path)); - let url = VerbatimUrl::from_absolute_path(&install_path) + let url = VerbatimUrl::from_normalized_path(&install_path) .map_err(LockErrorKind::RequirementVerbatimUrl)?; Ok(Requirement {