Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use relative paths for --find-links and local registries #6566

Merged
merged 4 commits into from
Aug 25, 2024
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
20 changes: 18 additions & 2 deletions crates/distribution-types/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,6 @@ impl Display for FileLocation {
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
rkyv::Archive,
rkyv::Deserialize,
rkyv::Serialize,
Expand All @@ -153,6 +151,24 @@ impl Display for FileLocation {
#[archive_attr(derive(Debug))]
pub struct UrlString(String);

impl serde::Serialize for UrlString {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
String::serialize(&self.0, serializer)
}
}

impl<'de> serde::de::Deserialize<'de> for UrlString {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
String::deserialize(deserializer).map(UrlString)
}
}

Comment on lines +154 to +171
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these serde(transparent), or am i missing something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes thank you! #6633

impl UrlString {
/// Converts a [`UrlString`] to a [`Url`].
pub fn to_url(&self) -> Url {
Expand Down
8 changes: 4 additions & 4 deletions crates/distribution-types/src/index_url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ impl FromStr for IndexUrl {
type Err = IndexUrlError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let url = if let Ok(path) = Path::new(s).canonicalize() {
VerbatimUrl::from_path(path)?
let url = if Path::new(s).exists() {
VerbatimUrl::from_absolute_path(std::path::absolute(s)?)?
} else {
VerbatimUrl::parse_url(s)?
};
Expand Down Expand Up @@ -247,8 +247,8 @@ impl FromStr for FlatIndexLocation {
type Err = IndexUrlError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let url = if let Ok(path) = Path::new(s).canonicalize() {
VerbatimUrl::from_path(path)?
let url = if Path::new(s).exists() {
VerbatimUrl::from_absolute_path(std::path::absolute(s)?)?
} else {
VerbatimUrl::parse_url(s)?
};
Expand Down
4 changes: 2 additions & 2 deletions crates/pep508-rs/src/unnamed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ impl UnnamedRequirementUrl for VerbatimUrl {
path: impl AsRef<Path>,
working_dir: impl AsRef<Path>,
) -> Result<Self, VerbatimUrlError> {
Self::parse_path(path, working_dir)
Self::from_path(path, working_dir)
}

fn parse_absolute_path(path: impl AsRef<Path>) -> Result<Self, Self::Err> {
Self::parse_absolute_path(path)
Self::from_absolute_path(path)
}

fn parse_unnamed_url(given: impl AsRef<str>) -> Result<Self, Self::Err> {
Expand Down
58 changes: 15 additions & 43 deletions crates/pep508-rs/src/verbatim_url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,31 +40,6 @@ impl VerbatimUrl {
Self { url, given: None }
}

/// Create a [`VerbatimUrl`] from a file path.
///
/// Assumes that the path is absolute.
pub fn from_path(path: impl AsRef<Path>) -> Result<Self, VerbatimUrlError> {
let path = path.as_ref();

// Normalize the path.
let path = normalize_absolute_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())
.map_err(|()| VerbatimUrlError::UrlConversion(path.to_path_buf()))?;

// Set the fragment, if it exists.
if let Some(fragment) = fragment {
url.set_fragment(Some(fragment));
}

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

/// Parse a URL from a string, expanding any environment variables.
pub fn parse_url(given: impl AsRef<str>) -> Result<Self, ParseError> {
let url = Url::parse(given.as_ref())?;
Expand All @@ -73,7 +48,7 @@ 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(
pub fn from_path(
path: impl AsRef<Path>,
base_dir: impl AsRef<Path>,
) -> Result<Self, VerbatimUrlError> {
Expand All @@ -82,13 +57,13 @@ impl VerbatimUrl {

// Convert the path to an absolute path, if necessary.
let path = if path.is_absolute() {
path.to_path_buf()
Cow::Borrowed(path)
} else {
base_dir.as_ref().join(path)
Cow::Owned(base_dir.as_ref().join(path))
};

let path = normalize_absolute_path(&path)
.map_err(|err| VerbatimUrlError::Normalization(path.clone(), err))?;
.map_err(|err| VerbatimUrlError::Normalization(path.to_path_buf(), err))?;

// Extract the fragment, if it exists.
let (path, fragment) = split_fragment(&path);
Expand All @@ -106,19 +81,19 @@ impl VerbatimUrl {
}

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

// Convert the path to an absolute path, if necessary.
// Error if the path is relative.
let path = if path.is_absolute() {
path.to_path_buf()
path
} else {
return Err(VerbatimUrlError::WorkingDirectory(path.to_path_buf()));
};

// Normalize the path.
let path = normalize_absolute_path(&path)
.map_err(|err| VerbatimUrlError::Normalization(path.clone(), err))?;
let path = normalize_absolute_path(path)
.map_err(|err| VerbatimUrlError::Normalization(path.to_path_buf(), err))?;

// Extract the fragment, if it exists.
let (path, fragment) = split_fragment(&path);
Expand Down Expand Up @@ -252,14 +227,11 @@ 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::from_path(path.as_ref(), working_dir)?
.with_given(url.to_string()));
}

Ok(
VerbatimUrl::parse_absolute_path(path.as_ref())?
.with_given(url.to_string()),
)
Ok(VerbatimUrl::from_absolute_path(path.as_ref())?.with_given(url.to_string()))
}

// Ex) `https://download.pytorch.org/whl/torch_stable.html`
Expand All @@ -272,23 +244,23 @@ 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::from_path(expanded.as_ref(), working_dir)?
.with_given(url.to_string()));
}

Ok(VerbatimUrl::parse_absolute_path(expanded.as_ref())?
Ok(VerbatimUrl::from_absolute_path(expanded.as_ref())?
.with_given(url.to_string()))
}
}
} else {
// 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::from_path(expanded.as_ref(), working_dir)?
.with_given(url.to_string()));
}

Ok(VerbatimUrl::parse_absolute_path(expanded.as_ref())?.with_given(url.to_string()))
Ok(VerbatimUrl::from_absolute_path(expanded.as_ref())?.with_given(url.to_string()))
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/pypi-types/src/parsed_url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl UnnamedRequirementUrl for VerbatimParsedUrl {
path: impl AsRef<Path>,
working_dir: impl AsRef<Path>,
) -> Result<Self, Self::Err> {
let verbatim = VerbatimUrl::parse_path(&path, &working_dir)?;
let verbatim = VerbatimUrl::from_path(&path, &working_dir)?;
let verbatim_path = verbatim.as_path()?;
let is_dir = if let Ok(metadata) = verbatim_path.metadata() {
metadata.is_dir()
Expand Down Expand Up @@ -89,7 +89,7 @@ impl UnnamedRequirementUrl for VerbatimParsedUrl {
}

fn parse_absolute_path(path: impl AsRef<Path>) -> Result<Self, Self::Err> {
let verbatim = VerbatimUrl::parse_absolute_path(&path)?;
let verbatim = VerbatimUrl::from_absolute_path(&path)?;
let verbatim_path = verbatim.as_path()?;
let is_dir = if let Ok(metadata) = verbatim_path.metadata() {
metadata.is_dir()
Expand Down
16 changes: 9 additions & 7 deletions crates/pypi-types/src/requirement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,8 @@ impl RequirementSource {
ext,
url,
} => Ok(Self::Path {
install_path: relative_to(&install_path, path)?,
install_path: relative_to(&install_path, path)
.or_else(|_| std::path::absolute(install_path))?,
ext,
url,
}),
Expand All @@ -516,7 +517,8 @@ impl RequirementSource {
editable,
url,
} => Ok(Self::Directory {
install_path: relative_to(&install_path, path)?,
install_path: relative_to(&install_path, path)
.or_else(|_| std::path::absolute(install_path))?,
editable,
url,
}),
Expand Down Expand Up @@ -744,7 +746,7 @@ impl TryFrom<RequirementSourceWire> for RequirementSource {
// sources in the lockfile, we replace the URL anyway.
RequirementSourceWire::Path { path } => {
let path = PathBuf::from(path);
let url = VerbatimUrl::parse_path(&path, &*CWD)?;
let url = VerbatimUrl::from_path(&path, &*CWD)?;
Ok(Self::Path {
ext: DistExtension::from_path(path.as_path())
.map_err(|err| ParsedUrlError::MissingExtensionPath(path.clone(), err))?,
Expand All @@ -754,7 +756,7 @@ impl TryFrom<RequirementSourceWire> for RequirementSource {
}
RequirementSourceWire::Directory { directory } => {
let directory = PathBuf::from(directory);
let url = VerbatimUrl::parse_path(&directory, &*CWD)?;
let url = VerbatimUrl::from_path(&directory, &*CWD)?;
Ok(Self::Directory {
install_path: directory,
editable: false,
Expand All @@ -763,7 +765,7 @@ impl TryFrom<RequirementSourceWire> for RequirementSource {
}
RequirementSourceWire::Editable { editable } => {
let editable = PathBuf::from(editable);
let url = VerbatimUrl::parse_path(&editable, &*CWD)?;
let url = VerbatimUrl::from_path(&editable, &*CWD)?;
Ok(Self::Directory {
install_path: editable,
editable: true,
Expand All @@ -788,7 +790,7 @@ pub fn redact_git_credentials(url: &mut Url) {

#[cfg(test)]
mod tests {
use std::path::{Path, PathBuf};
use std::path::PathBuf;

use pep508_rs::{MarkerTree, VerbatimUrl};

Expand Down Expand Up @@ -823,7 +825,7 @@ mod tests {
source: RequirementSource::Directory {
install_path: PathBuf::from(path),
editable: false,
url: VerbatimUrl::from_path(Path::new(path)).unwrap(),
url: VerbatimUrl::from_absolute_path(path).unwrap(),
},
origin: None,
};
Expand Down
51 changes: 33 additions & 18 deletions crates/requirements-txt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,12 +484,17 @@ fn parse_entry(
} else if s.eat_if("-i") || s.eat_if("--index-url") {
let given = parse_value(content, s, |c: char| !['\n', '\r', '#'].contains(&c))?;
let expanded = expand_env_vars(given);
let url = if let Ok(path) = Path::new(expanded.as_ref()).canonicalize() {
VerbatimUrl::from_path(path).map_err(|err| RequirementsTxtParserError::VerbatimUrl {
source: err,
url: given.to_string(),
start,
end: s.cursor(),
let url = if let Some(path) = std::path::absolute(expanded.as_ref())
.ok()
.filter(|path| path.exists())
{
VerbatimUrl::from_absolute_path(path).map_err(|err| {
RequirementsTxtParserError::VerbatimUrl {
source: err,
url: given.to_string(),
start,
end: s.cursor(),
}
})?
} else {
VerbatimUrl::parse_url(expanded.as_ref()).map_err(|err| {
Expand All @@ -505,12 +510,17 @@ fn parse_entry(
} else if s.eat_if("--extra-index-url") {
let given = parse_value(content, s, |c: char| !['\n', '\r', '#'].contains(&c))?;
let expanded = expand_env_vars(given);
let url = if let Ok(path) = Path::new(expanded.as_ref()).canonicalize() {
VerbatimUrl::from_path(path).map_err(|err| RequirementsTxtParserError::VerbatimUrl {
source: err,
url: given.to_string(),
start,
end: s.cursor(),
let url = if let Some(path) = std::path::absolute(expanded.as_ref())
.ok()
.filter(|path| path.exists())
{
VerbatimUrl::from_absolute_path(path).map_err(|err| {
RequirementsTxtParserError::VerbatimUrl {
source: err,
url: given.to_string(),
start,
end: s.cursor(),
}
})?
} else {
VerbatimUrl::parse_url(expanded.as_ref()).map_err(|err| {
Expand All @@ -528,12 +538,17 @@ fn parse_entry(
} else if s.eat_if("--find-links") || s.eat_if("-f") {
let given = parse_value(content, s, |c: char| !['\n', '\r', '#'].contains(&c))?;
let expanded = expand_env_vars(given);
let url = if let Ok(path) = Path::new(expanded.as_ref()).canonicalize() {
VerbatimUrl::from_path(path).map_err(|err| RequirementsTxtParserError::VerbatimUrl {
source: err,
url: given.to_string(),
start,
end: s.cursor(),
let url = if let Some(path) = std::path::absolute(expanded.as_ref())
.ok()
.filter(|path| path.exists())
{
VerbatimUrl::from_absolute_path(path).map_err(|err| {
RequirementsTxtParserError::VerbatimUrl {
source: err,
url: given.to_string(),
start,
end: s.cursor(),
}
})?
} else {
VerbatimUrl::parse_url(expanded.as_ref()).map_err(|err| {
Expand Down
4 changes: 2 additions & 2 deletions crates/uv-distribution/src/metadata/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ impl LoweredRequirement {
// relative to workspace: `packages/current_project`
// workspace lock root: `../current_workspace`
// relative to main workspace: `../current_workspace/packages/current_project`
let url = VerbatimUrl::parse_absolute_path(member.root())?;
let url = VerbatimUrl::from_absolute_path(member.root())?;
let install_path = url.to_file_path().map_err(|()| {
LoweringError::RelativeTo(io::Error::new(
io::ErrorKind::Other,
Expand Down Expand Up @@ -360,7 +360,7 @@ fn path_source(
Origin::Project => project_dir,
Origin::Workspace => workspace_root,
};
let url = VerbatimUrl::parse_path(path, base)?.with_given(path.to_string_lossy());
let url = VerbatimUrl::from_path(path, base)?.with_given(path.to_string_lossy());
let install_path = url.to_file_path().map_err(|()| {
LoweringError::RelativeTo(io::Error::new(
io::ErrorKind::Other,
Expand Down
3 changes: 3 additions & 0 deletions crates/uv-fs/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,9 @@ pub fn canonicalize_executable(path: impl AsRef<Path>) -> std::io::Result<PathBu
/// `lib/python/site-packages/foo/__init__.py` and `lib/python/site-packages` -> `foo/__init__.py`
/// `lib/marker.txt` and `lib/python/site-packages` -> `../../marker.txt`
/// `bin/foo_launcher` and `lib/python/site-packages` -> `../../../bin/foo_launcher`
///
/// Returns `Err` if there is no relative path between `path` and `base` (for example, if the paths
/// are on different drives on Windows).
pub fn relative_to(
path: impl AsRef<Path>,
base: impl AsRef<Path>,
Expand Down
Loading
Loading