mirror of
https://github.com/astral-sh/uv.git
synced 2025-09-26 12:09:12 +00:00
Only use relative paths in lockfile (#6490)
For users who were using absolute paths in the `pyproject.toml` previously, this is a behavior change: We now convert all absolute paths in `path` entries to relative paths. Since i assume that no-one relies on absolute path in their lockfiles - they are intended to be portable - I'm tagging this as a bugfix. Closes https://github.com/astral-sh/uv/pull/6438 Fixes https://github.com/astral-sh/uv/issues/6371
This commit is contained in:
parent
611a9003c9
commit
f7835243c5
30 changed files with 329 additions and 383 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4828,7 +4828,6 @@ dependencies = [
|
||||||
"insta",
|
"insta",
|
||||||
"install-wheel-rs",
|
"install-wheel-rs",
|
||||||
"nanoid",
|
"nanoid",
|
||||||
"path-absolutize",
|
|
||||||
"pep440_rs",
|
"pep440_rs",
|
||||||
"pep508_rs",
|
"pep508_rs",
|
||||||
"platform-tags",
|
"platform-tags",
|
||||||
|
|
|
@ -172,7 +172,6 @@ impl<'a> From<&'a PathSourceDist> for PathSourceUrl<'a> {
|
||||||
pub struct DirectorySourceUrl<'a> {
|
pub struct DirectorySourceUrl<'a> {
|
||||||
pub url: &'a Url,
|
pub url: &'a Url,
|
||||||
pub install_path: Cow<'a, Path>,
|
pub install_path: Cow<'a, Path>,
|
||||||
pub lock_path: Cow<'a, Path>,
|
|
||||||
pub editable: bool,
|
pub editable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +186,6 @@ impl<'a> From<&'a DirectorySourceDist> for DirectorySourceUrl<'a> {
|
||||||
Self {
|
Self {
|
||||||
url: &dist.url,
|
url: &dist.url,
|
||||||
install_path: Cow::Borrowed(&dist.install_path),
|
install_path: Cow::Borrowed(&dist.install_path),
|
||||||
lock_path: Cow::Borrowed(&dist.lock_path),
|
|
||||||
editable: dist.editable,
|
editable: dist.editable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,8 +122,7 @@ impl CachedDist {
|
||||||
.map_err(|()| anyhow!("Invalid path in file URL"))?;
|
.map_err(|()| anyhow!("Invalid path in file URL"))?;
|
||||||
Ok(Some(ParsedUrl::Directory(ParsedDirectoryUrl {
|
Ok(Some(ParsedUrl::Directory(ParsedDirectoryUrl {
|
||||||
url: dist.url.raw().clone(),
|
url: dist.url.raw().clone(),
|
||||||
install_path: path.clone(),
|
install_path: path,
|
||||||
lock_path: path,
|
|
||||||
editable: dist.editable,
|
editable: dist.editable,
|
||||||
})))
|
})))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
//!
|
//!
|
||||||
//! Since we read this information from [`direct_url.json`](https://packaging.python.org/en/latest/specifications/direct-url-data-structure/), it doesn't match the information [`Dist`] exactly.
|
//! Since we read this information from [`direct_url.json`](https://packaging.python.org/en/latest/specifications/direct-url-data-structure/), it doesn't match the information [`Dist`] exactly.
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::path;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
@ -237,10 +238,6 @@ pub struct PathBuiltDist {
|
||||||
pub filename: WheelFilename,
|
pub filename: WheelFilename,
|
||||||
/// The absolute path to the wheel which we use for installing.
|
/// The absolute path to the wheel which we use for installing.
|
||||||
pub install_path: PathBuf,
|
pub install_path: PathBuf,
|
||||||
/// The absolute path or path relative to the workspace root pointing to the wheel
|
|
||||||
/// which we use for locking. Unlike `given` on the verbatim URL all environment variables
|
|
||||||
/// are resolved, and unlike the install path, we did not yet join it on the base directory.
|
|
||||||
pub lock_path: PathBuf,
|
|
||||||
/// The URL as it was provided by the user.
|
/// The URL as it was provided by the user.
|
||||||
pub url: VerbatimUrl,
|
pub url: VerbatimUrl,
|
||||||
}
|
}
|
||||||
|
@ -298,10 +295,6 @@ pub struct PathSourceDist {
|
||||||
pub name: PackageName,
|
pub name: PackageName,
|
||||||
/// The absolute path to the distribution which we use for installing.
|
/// The absolute path to the distribution which we use for installing.
|
||||||
pub install_path: PathBuf,
|
pub install_path: PathBuf,
|
||||||
/// The absolute path or path relative to the workspace root pointing to the distribution
|
|
||||||
/// which we use for locking. Unlike `given` on the verbatim URL all environment variables
|
|
||||||
/// are resolved, and unlike the install path, we did not yet join it on the base directory.
|
|
||||||
pub lock_path: PathBuf,
|
|
||||||
/// The file extension, e.g. `tar.gz`, `zip`, etc.
|
/// The file extension, e.g. `tar.gz`, `zip`, etc.
|
||||||
pub ext: SourceDistExtension,
|
pub ext: SourceDistExtension,
|
||||||
/// The URL as it was provided by the user.
|
/// The URL as it was provided by the user.
|
||||||
|
@ -314,10 +307,6 @@ pub struct DirectorySourceDist {
|
||||||
pub name: PackageName,
|
pub name: PackageName,
|
||||||
/// The absolute path to the distribution which we use for installing.
|
/// The absolute path to the distribution which we use for installing.
|
||||||
pub install_path: PathBuf,
|
pub install_path: PathBuf,
|
||||||
/// The absolute path or path relative to the workspace root pointing to the distribution
|
|
||||||
/// which we use for locking. Unlike `given` on the verbatim URL all environment variables
|
|
||||||
/// are resolved, and unlike the install path, we did not yet join it on the base directory.
|
|
||||||
pub lock_path: PathBuf,
|
|
||||||
/// Whether the package should be installed in editable mode.
|
/// Whether the package should be installed in editable mode.
|
||||||
pub editable: bool,
|
pub editable: bool,
|
||||||
/// The URL as it was provided by the user.
|
/// The URL as it was provided by the user.
|
||||||
|
@ -369,11 +358,10 @@ impl Dist {
|
||||||
name: PackageName,
|
name: PackageName,
|
||||||
url: VerbatimUrl,
|
url: VerbatimUrl,
|
||||||
install_path: &Path,
|
install_path: &Path,
|
||||||
lock_path: &Path,
|
|
||||||
ext: DistExtension,
|
ext: DistExtension,
|
||||||
) -> Result<Dist, Error> {
|
) -> Result<Dist, Error> {
|
||||||
// Convert to an absolute path.
|
// Convert to an absolute path.
|
||||||
let install_path = std::path::absolute(install_path)?;
|
let install_path = path::absolute(install_path)?;
|
||||||
|
|
||||||
// Normalize the path.
|
// Normalize the path.
|
||||||
let install_path = normalize_absolute_path(&install_path)?;
|
let install_path = normalize_absolute_path(&install_path)?;
|
||||||
|
@ -398,14 +386,12 @@ impl Dist {
|
||||||
Ok(Self::Built(BuiltDist::Path(PathBuiltDist {
|
Ok(Self::Built(BuiltDist::Path(PathBuiltDist {
|
||||||
filename,
|
filename,
|
||||||
install_path,
|
install_path,
|
||||||
lock_path: lock_path.to_path_buf(),
|
|
||||||
url,
|
url,
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
DistExtension::Source(ext) => Ok(Self::Source(SourceDist::Path(PathSourceDist {
|
DistExtension::Source(ext) => Ok(Self::Source(SourceDist::Path(PathSourceDist {
|
||||||
name,
|
name,
|
||||||
install_path,
|
install_path,
|
||||||
lock_path: lock_path.to_path_buf(),
|
|
||||||
ext,
|
ext,
|
||||||
url,
|
url,
|
||||||
}))),
|
}))),
|
||||||
|
@ -417,11 +403,10 @@ impl Dist {
|
||||||
name: PackageName,
|
name: PackageName,
|
||||||
url: VerbatimUrl,
|
url: VerbatimUrl,
|
||||||
install_path: &Path,
|
install_path: &Path,
|
||||||
lock_path: &Path,
|
|
||||||
editable: bool,
|
editable: bool,
|
||||||
) -> Result<Dist, Error> {
|
) -> Result<Dist, Error> {
|
||||||
// Convert to an absolute path.
|
// Convert to an absolute path.
|
||||||
let install_path = std::path::absolute(install_path)?;
|
let install_path = path::absolute(install_path)?;
|
||||||
|
|
||||||
// Normalize the path.
|
// Normalize the path.
|
||||||
let install_path = normalize_absolute_path(&install_path)?;
|
let install_path = normalize_absolute_path(&install_path)?;
|
||||||
|
@ -435,7 +420,6 @@ impl Dist {
|
||||||
Ok(Self::Source(SourceDist::Directory(DirectorySourceDist {
|
Ok(Self::Source(SourceDist::Directory(DirectorySourceDist {
|
||||||
name,
|
name,
|
||||||
install_path,
|
install_path,
|
||||||
lock_path: lock_path.to_path_buf(),
|
|
||||||
editable,
|
editable,
|
||||||
url,
|
url,
|
||||||
})))
|
})))
|
||||||
|
@ -466,18 +450,13 @@ impl Dist {
|
||||||
archive.subdirectory,
|
archive.subdirectory,
|
||||||
archive.ext,
|
archive.ext,
|
||||||
),
|
),
|
||||||
ParsedUrl::Path(file) => Self::from_file_url(
|
ParsedUrl::Path(file) => {
|
||||||
name,
|
Self::from_file_url(name, url.verbatim, &file.install_path, file.ext)
|
||||||
url.verbatim,
|
}
|
||||||
&file.install_path,
|
|
||||||
&file.lock_path,
|
|
||||||
file.ext,
|
|
||||||
),
|
|
||||||
ParsedUrl::Directory(directory) => Self::from_directory_url(
|
ParsedUrl::Directory(directory) => Self::from_directory_url(
|
||||||
name,
|
name,
|
||||||
url.verbatim,
|
url.verbatim,
|
||||||
&directory.install_path,
|
&directory.install_path,
|
||||||
&directory.lock_path,
|
|
||||||
directory.editable,
|
directory.editable,
|
||||||
),
|
),
|
||||||
ParsedUrl::Git(git) => {
|
ParsedUrl::Git(git) => {
|
||||||
|
|
|
@ -185,7 +185,6 @@ impl From<&ResolvedDist> for Requirement {
|
||||||
}
|
}
|
||||||
Dist::Built(BuiltDist::Path(wheel)) => RequirementSource::Path {
|
Dist::Built(BuiltDist::Path(wheel)) => RequirementSource::Path {
|
||||||
install_path: wheel.install_path.clone(),
|
install_path: wheel.install_path.clone(),
|
||||||
lock_path: wheel.lock_path.clone(),
|
|
||||||
url: wheel.url.clone(),
|
url: wheel.url.clone(),
|
||||||
ext: DistExtension::Wheel,
|
ext: DistExtension::Wheel,
|
||||||
},
|
},
|
||||||
|
@ -214,13 +213,11 @@ impl From<&ResolvedDist> for Requirement {
|
||||||
},
|
},
|
||||||
Dist::Source(SourceDist::Path(sdist)) => RequirementSource::Path {
|
Dist::Source(SourceDist::Path(sdist)) => RequirementSource::Path {
|
||||||
install_path: sdist.install_path.clone(),
|
install_path: sdist.install_path.clone(),
|
||||||
lock_path: sdist.lock_path.clone(),
|
|
||||||
url: sdist.url.clone(),
|
url: sdist.url.clone(),
|
||||||
ext: DistExtension::Source(sdist.ext),
|
ext: DistExtension::Source(sdist.ext),
|
||||||
},
|
},
|
||||||
Dist::Source(SourceDist::Directory(sdist)) => RequirementSource::Directory {
|
Dist::Source(SourceDist::Directory(sdist)) => RequirementSource::Directory {
|
||||||
install_path: sdist.install_path.clone(),
|
install_path: sdist.install_path.clone(),
|
||||||
lock_path: sdist.lock_path.clone(),
|
|
||||||
url: sdist.url.clone(),
|
url: sdist.url.clone(),
|
||||||
editable: sdist.editable,
|
editable: sdist.editable,
|
||||||
},
|
},
|
||||||
|
|
|
@ -71,14 +71,12 @@ impl UnnamedRequirementUrl for VerbatimParsedUrl {
|
||||||
ParsedUrl::Directory(ParsedDirectoryUrl {
|
ParsedUrl::Directory(ParsedDirectoryUrl {
|
||||||
url: verbatim.to_url(),
|
url: verbatim.to_url(),
|
||||||
install_path: verbatim.as_path()?,
|
install_path: verbatim.as_path()?,
|
||||||
lock_path: path.as_ref().to_path_buf(),
|
|
||||||
editable: false,
|
editable: false,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
ParsedUrl::Path(ParsedPathUrl {
|
ParsedUrl::Path(ParsedPathUrl {
|
||||||
url: verbatim.to_url(),
|
url: verbatim.to_url(),
|
||||||
install_path: verbatim.as_path()?,
|
install_path: verbatim.as_path()?,
|
||||||
lock_path: path.as_ref().to_path_buf(),
|
|
||||||
ext: DistExtension::from_path(&path).map_err(|err| {
|
ext: DistExtension::from_path(&path).map_err(|err| {
|
||||||
ParsedUrlError::MissingExtensionPath(path.as_ref().to_path_buf(), err)
|
ParsedUrlError::MissingExtensionPath(path.as_ref().to_path_buf(), err)
|
||||||
})?,
|
})?,
|
||||||
|
@ -102,14 +100,12 @@ impl UnnamedRequirementUrl for VerbatimParsedUrl {
|
||||||
ParsedUrl::Directory(ParsedDirectoryUrl {
|
ParsedUrl::Directory(ParsedDirectoryUrl {
|
||||||
url: verbatim.to_url(),
|
url: verbatim.to_url(),
|
||||||
install_path: verbatim.as_path()?,
|
install_path: verbatim.as_path()?,
|
||||||
lock_path: path.as_ref().to_path_buf(),
|
|
||||||
editable: false,
|
editable: false,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
ParsedUrl::Path(ParsedPathUrl {
|
ParsedUrl::Path(ParsedPathUrl {
|
||||||
url: verbatim.to_url(),
|
url: verbatim.to_url(),
|
||||||
install_path: verbatim.as_path()?,
|
install_path: verbatim.as_path()?,
|
||||||
lock_path: path.as_ref().to_path_buf(),
|
|
||||||
ext: DistExtension::from_path(&path).map_err(|err| {
|
ext: DistExtension::from_path(&path).map_err(|err| {
|
||||||
ParsedUrlError::MissingExtensionPath(path.as_ref().to_path_buf(), err)
|
ParsedUrlError::MissingExtensionPath(path.as_ref().to_path_buf(), err)
|
||||||
})?,
|
})?,
|
||||||
|
@ -187,26 +183,16 @@ pub struct ParsedPathUrl {
|
||||||
pub url: Url,
|
pub url: Url,
|
||||||
/// The absolute path to the distribution which we use for installing.
|
/// The absolute path to the distribution which we use for installing.
|
||||||
pub install_path: PathBuf,
|
pub install_path: PathBuf,
|
||||||
/// The absolute path or path relative to the workspace root pointing to the distribution
|
|
||||||
/// which we use for locking. Unlike `given` on the verbatim URL all environment variables
|
|
||||||
/// are resolved, and unlike the install path, we did not yet join it on the base directory.
|
|
||||||
pub lock_path: PathBuf,
|
|
||||||
/// The file extension, e.g. `tar.gz`, `zip`, etc.
|
/// The file extension, e.g. `tar.gz`, `zip`, etc.
|
||||||
pub ext: DistExtension,
|
pub ext: DistExtension,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParsedPathUrl {
|
impl ParsedPathUrl {
|
||||||
/// Construct a [`ParsedPathUrl`] from a path requirement source.
|
/// Construct a [`ParsedPathUrl`] from a path requirement source.
|
||||||
pub fn from_source(
|
pub fn from_source(install_path: PathBuf, ext: DistExtension, url: Url) -> Self {
|
||||||
install_path: PathBuf,
|
|
||||||
lock_path: PathBuf,
|
|
||||||
ext: DistExtension,
|
|
||||||
url: Url,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
url,
|
url,
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
ext,
|
ext,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,25 +207,15 @@ pub struct ParsedDirectoryUrl {
|
||||||
pub url: Url,
|
pub url: Url,
|
||||||
/// The absolute path to the distribution which we use for installing.
|
/// The absolute path to the distribution which we use for installing.
|
||||||
pub install_path: PathBuf,
|
pub install_path: PathBuf,
|
||||||
/// The absolute path or path relative to the workspace root pointing to the distribution
|
|
||||||
/// which we use for locking. Unlike `given` on the verbatim URL all environment variables
|
|
||||||
/// are resolved, and unlike the install path, we did not yet join it on the base directory.
|
|
||||||
pub lock_path: PathBuf,
|
|
||||||
pub editable: bool,
|
pub editable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParsedDirectoryUrl {
|
impl ParsedDirectoryUrl {
|
||||||
/// Construct a [`ParsedDirectoryUrl`] from a path requirement source.
|
/// Construct a [`ParsedDirectoryUrl`] from a path requirement source.
|
||||||
pub fn from_source(
|
pub fn from_source(install_path: PathBuf, editable: bool, url: Url) -> Self {
|
||||||
install_path: PathBuf,
|
|
||||||
lock_path: PathBuf,
|
|
||||||
editable: bool,
|
|
||||||
url: Url,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
url,
|
url,
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
editable,
|
editable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -393,7 +369,6 @@ impl TryFrom<Url> for ParsedUrl {
|
||||||
Ok(Self::Directory(ParsedDirectoryUrl {
|
Ok(Self::Directory(ParsedDirectoryUrl {
|
||||||
url,
|
url,
|
||||||
install_path: path.clone(),
|
install_path: path.clone(),
|
||||||
lock_path: path,
|
|
||||||
editable: false,
|
editable: false,
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
|
@ -402,7 +377,6 @@ impl TryFrom<Url> for ParsedUrl {
|
||||||
ext: DistExtension::from_path(&path)
|
ext: DistExtension::from_path(&path)
|
||||||
.map_err(|err| ParsedUrlError::MissingExtensionPath(path.clone(), err))?,
|
.map_err(|err| ParsedUrlError::MissingExtensionPath(path.clone(), err))?,
|
||||||
install_path: path.clone(),
|
install_path: path.clone(),
|
||||||
lock_path: path,
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
@ -10,7 +11,7 @@ use pep440_rs::VersionSpecifiers;
|
||||||
use pep508_rs::{
|
use pep508_rs::{
|
||||||
marker, MarkerEnvironment, MarkerTree, RequirementOrigin, VerbatimUrl, VersionOrUrl,
|
marker, MarkerEnvironment, MarkerTree, RequirementOrigin, VerbatimUrl, VersionOrUrl,
|
||||||
};
|
};
|
||||||
use uv_fs::{PortablePathBuf, CWD};
|
use uv_fs::{relative_to, PortablePathBuf, CWD};
|
||||||
use uv_git::{GitReference, GitSha, GitUrl};
|
use uv_git::{GitReference, GitSha, GitUrl};
|
||||||
use uv_normalize::{ExtraName, PackageName};
|
use uv_normalize::{ExtraName, PackageName};
|
||||||
|
|
||||||
|
@ -105,6 +106,14 @@ impl Requirement {
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert the requirement to a [`Requirement`] relative to the given path.
|
||||||
|
pub fn relative_to(self, path: &Path) -> Result<Self, io::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
source: self.source.relative_to(path)?,
|
||||||
|
..self
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Requirement> for pep508_rs::Requirement<VerbatimUrl> {
|
impl From<Requirement> for pep508_rs::Requirement<VerbatimUrl> {
|
||||||
|
@ -175,28 +184,24 @@ impl From<Requirement> for pep508_rs::Requirement<VerbatimParsedUrl> {
|
||||||
}
|
}
|
||||||
RequirementSource::Path {
|
RequirementSource::Path {
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
ext,
|
ext,
|
||||||
url,
|
url,
|
||||||
} => Some(VersionOrUrl::Url(VerbatimParsedUrl {
|
} => Some(VersionOrUrl::Url(VerbatimParsedUrl {
|
||||||
parsed_url: ParsedUrl::Path(ParsedPathUrl {
|
parsed_url: ParsedUrl::Path(ParsedPathUrl {
|
||||||
url: url.to_url(),
|
url: url.to_url(),
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
ext,
|
ext,
|
||||||
}),
|
}),
|
||||||
verbatim: url,
|
verbatim: url,
|
||||||
})),
|
})),
|
||||||
RequirementSource::Directory {
|
RequirementSource::Directory {
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
editable,
|
editable,
|
||||||
url,
|
url,
|
||||||
} => Some(VersionOrUrl::Url(VerbatimParsedUrl {
|
} => Some(VersionOrUrl::Url(VerbatimParsedUrl {
|
||||||
parsed_url: ParsedUrl::Directory(ParsedDirectoryUrl {
|
parsed_url: ParsedUrl::Directory(ParsedDirectoryUrl {
|
||||||
url: url.to_url(),
|
url: url.to_url(),
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
editable,
|
editable,
|
||||||
}),
|
}),
|
||||||
verbatim: url,
|
verbatim: url,
|
||||||
|
@ -342,10 +347,6 @@ pub enum RequirementSource {
|
||||||
Path {
|
Path {
|
||||||
/// The absolute path to the distribution which we use for installing.
|
/// The absolute path to the distribution which we use for installing.
|
||||||
install_path: PathBuf,
|
install_path: PathBuf,
|
||||||
/// The absolute path or path relative to the workspace root pointing to the distribution
|
|
||||||
/// which we use for locking. Unlike `given` on the verbatim URL all environment variables
|
|
||||||
/// are resolved, and unlike the install path, we did not yet join it on the base directory.
|
|
||||||
lock_path: PathBuf,
|
|
||||||
/// The file extension, e.g. `tar.gz`, `zip`, etc.
|
/// The file extension, e.g. `tar.gz`, `zip`, etc.
|
||||||
ext: DistExtension,
|
ext: DistExtension,
|
||||||
/// The PEP 508 style URL in the format
|
/// The PEP 508 style URL in the format
|
||||||
|
@ -357,10 +358,6 @@ pub enum RequirementSource {
|
||||||
Directory {
|
Directory {
|
||||||
/// The absolute path to the distribution which we use for installing.
|
/// The absolute path to the distribution which we use for installing.
|
||||||
install_path: PathBuf,
|
install_path: PathBuf,
|
||||||
/// The absolute path or path relative to the workspace root pointing to the distribution
|
|
||||||
/// which we use for locking. Unlike `given` on the verbatim URL all environment variables
|
|
||||||
/// are resolved, and unlike the install path, we did not yet join it on the base directory.
|
|
||||||
lock_path: PathBuf,
|
|
||||||
/// For a source tree (a directory), whether to install as an editable.
|
/// For a source tree (a directory), whether to install as an editable.
|
||||||
editable: bool,
|
editable: bool,
|
||||||
/// The PEP 508 style URL in the format
|
/// The PEP 508 style URL in the format
|
||||||
|
@ -376,13 +373,11 @@ impl RequirementSource {
|
||||||
match parsed_url {
|
match parsed_url {
|
||||||
ParsedUrl::Path(local_file) => RequirementSource::Path {
|
ParsedUrl::Path(local_file) => RequirementSource::Path {
|
||||||
install_path: local_file.install_path.clone(),
|
install_path: local_file.install_path.clone(),
|
||||||
lock_path: local_file.lock_path.clone(),
|
|
||||||
ext: local_file.ext,
|
ext: local_file.ext,
|
||||||
url,
|
url,
|
||||||
},
|
},
|
||||||
ParsedUrl::Directory(directory) => RequirementSource::Directory {
|
ParsedUrl::Directory(directory) => RequirementSource::Directory {
|
||||||
install_path: directory.install_path.clone(),
|
install_path: directory.install_path.clone(),
|
||||||
lock_path: directory.lock_path.clone(),
|
|
||||||
editable: directory.editable,
|
editable: directory.editable,
|
||||||
url,
|
url,
|
||||||
},
|
},
|
||||||
|
@ -427,13 +422,11 @@ impl RequirementSource {
|
||||||
}),
|
}),
|
||||||
Self::Path {
|
Self::Path {
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
ext,
|
ext,
|
||||||
url,
|
url,
|
||||||
} => Some(VerbatimParsedUrl {
|
} => Some(VerbatimParsedUrl {
|
||||||
parsed_url: ParsedUrl::Path(ParsedPathUrl::from_source(
|
parsed_url: ParsedUrl::Path(ParsedPathUrl::from_source(
|
||||||
install_path.clone(),
|
install_path.clone(),
|
||||||
lock_path.clone(),
|
|
||||||
*ext,
|
*ext,
|
||||||
url.to_url(),
|
url.to_url(),
|
||||||
)),
|
)),
|
||||||
|
@ -441,13 +434,11 @@ impl RequirementSource {
|
||||||
}),
|
}),
|
||||||
Self::Directory {
|
Self::Directory {
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
editable,
|
editable,
|
||||||
url,
|
url,
|
||||||
} => Some(VerbatimParsedUrl {
|
} => Some(VerbatimParsedUrl {
|
||||||
parsed_url: ParsedUrl::Directory(ParsedDirectoryUrl::from_source(
|
parsed_url: ParsedUrl::Directory(ParsedDirectoryUrl::from_source(
|
||||||
install_path.clone(),
|
install_path.clone(),
|
||||||
lock_path.clone(),
|
|
||||||
*editable,
|
*editable,
|
||||||
url.to_url(),
|
url.to_url(),
|
||||||
)),
|
)),
|
||||||
|
@ -504,6 +495,33 @@ impl RequirementSource {
|
||||||
| RequirementSource::Directory { .. } => None,
|
| RequirementSource::Directory { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert the source to a [`RequirementSource`] relative to the given path.
|
||||||
|
pub fn relative_to(self, path: &Path) -> Result<Self, io::Error> {
|
||||||
|
match self {
|
||||||
|
RequirementSource::Registry { .. }
|
||||||
|
| RequirementSource::Url { .. }
|
||||||
|
| RequirementSource::Git { .. } => Ok(self),
|
||||||
|
RequirementSource::Path {
|
||||||
|
install_path,
|
||||||
|
ext,
|
||||||
|
url,
|
||||||
|
} => Ok(Self::Path {
|
||||||
|
install_path: relative_to(&install_path, path)?,
|
||||||
|
ext,
|
||||||
|
url,
|
||||||
|
}),
|
||||||
|
RequirementSource::Directory {
|
||||||
|
install_path,
|
||||||
|
editable,
|
||||||
|
url,
|
||||||
|
} => Ok(Self::Directory {
|
||||||
|
install_path: relative_to(&install_path, path)?,
|
||||||
|
editable,
|
||||||
|
url,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for RequirementSource {
|
impl Display for RequirementSource {
|
||||||
|
@ -639,26 +657,24 @@ impl From<RequirementSource> for RequirementSourceWire {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RequirementSource::Path {
|
RequirementSource::Path {
|
||||||
install_path: _,
|
install_path,
|
||||||
lock_path,
|
|
||||||
ext: _,
|
ext: _,
|
||||||
url: _,
|
url: _,
|
||||||
} => Self::Path {
|
} => Self::Path {
|
||||||
path: PortablePathBuf::from(lock_path),
|
path: PortablePathBuf::from(install_path),
|
||||||
},
|
},
|
||||||
RequirementSource::Directory {
|
RequirementSource::Directory {
|
||||||
install_path: _,
|
install_path,
|
||||||
lock_path,
|
|
||||||
editable,
|
editable,
|
||||||
url: _,
|
url: _,
|
||||||
} => {
|
} => {
|
||||||
if editable {
|
if editable {
|
||||||
Self::Editable {
|
Self::Editable {
|
||||||
editable: PortablePathBuf::from(lock_path),
|
editable: PortablePathBuf::from(install_path),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Self::Directory {
|
Self::Directory {
|
||||||
directory: PortablePathBuf::from(lock_path),
|
directory: PortablePathBuf::from(install_path),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -732,8 +748,7 @@ impl TryFrom<RequirementSourceWire> for RequirementSource {
|
||||||
Ok(Self::Path {
|
Ok(Self::Path {
|
||||||
ext: DistExtension::from_path(path.as_path())
|
ext: DistExtension::from_path(path.as_path())
|
||||||
.map_err(|err| ParsedUrlError::MissingExtensionPath(path.clone(), err))?,
|
.map_err(|err| ParsedUrlError::MissingExtensionPath(path.clone(), err))?,
|
||||||
install_path: path.clone(),
|
install_path: path,
|
||||||
lock_path: path,
|
|
||||||
url,
|
url,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -741,8 +756,7 @@ impl TryFrom<RequirementSourceWire> for RequirementSource {
|
||||||
let directory = PathBuf::from(directory);
|
let directory = PathBuf::from(directory);
|
||||||
let url = VerbatimUrl::parse_path(&directory, &*CWD)?;
|
let url = VerbatimUrl::parse_path(&directory, &*CWD)?;
|
||||||
Ok(Self::Directory {
|
Ok(Self::Directory {
|
||||||
install_path: directory.clone(),
|
install_path: directory,
|
||||||
lock_path: directory,
|
|
||||||
editable: false,
|
editable: false,
|
||||||
url,
|
url,
|
||||||
})
|
})
|
||||||
|
@ -751,8 +765,7 @@ impl TryFrom<RequirementSourceWire> for RequirementSource {
|
||||||
let editable = PathBuf::from(editable);
|
let editable = PathBuf::from(editable);
|
||||||
let url = VerbatimUrl::parse_path(&editable, &*CWD)?;
|
let url = VerbatimUrl::parse_path(&editable, &*CWD)?;
|
||||||
Ok(Self::Directory {
|
Ok(Self::Directory {
|
||||||
install_path: editable.clone(),
|
install_path: editable,
|
||||||
lock_path: editable,
|
|
||||||
editable: true,
|
editable: true,
|
||||||
url,
|
url,
|
||||||
})
|
})
|
||||||
|
@ -809,7 +822,6 @@ mod tests {
|
||||||
marker: MarkerTree::TRUE,
|
marker: MarkerTree::TRUE,
|
||||||
source: RequirementSource::Directory {
|
source: RequirementSource::Directory {
|
||||||
install_path: PathBuf::from(path),
|
install_path: PathBuf::from(path),
|
||||||
lock_path: PathBuf::from(path),
|
|
||||||
editable: false,
|
editable: false,
|
||||||
url: VerbatimUrl::from_path(Path::new(path)).unwrap(),
|
url: VerbatimUrl::from_path(Path::new(path)).unwrap(),
|
||||||
},
|
},
|
||||||
|
|
|
@ -1845,7 +1845,6 @@ mod test {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "/foo/bar",
|
install_path: "/foo/bar",
|
||||||
lock_path: "/foo/bar",
|
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -22,7 +22,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "<REQUIREMENTS_DIR>/scripts/packages/black_editable",
|
install_path: "<REQUIREMENTS_DIR>/scripts/packages/black_editable",
|
||||||
lock_path: "./scripts/packages/black_editable",
|
|
||||||
editable: false,
|
editable: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -72,7 +71,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "<REQUIREMENTS_DIR>/scripts/packages/black_editable",
|
install_path: "<REQUIREMENTS_DIR>/scripts/packages/black_editable",
|
||||||
lock_path: "./scripts/packages/black_editable",
|
|
||||||
editable: false,
|
editable: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -126,7 +124,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "/scripts/packages/black_editable",
|
install_path: "/scripts/packages/black_editable",
|
||||||
lock_path: "/scripts/packages/black_editable",
|
|
||||||
editable: false,
|
editable: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -24,7 +24,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
lock_path: "./editable",
|
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -81,7 +80,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
lock_path: "./editable",
|
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -138,7 +136,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
lock_path: "./editable",
|
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -195,7 +192,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
lock_path: "./editable",
|
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -252,7 +248,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
lock_path: "./editable",
|
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -302,7 +297,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "<REQUIREMENTS_DIR>/editable[d",
|
install_path: "<REQUIREMENTS_DIR>/editable[d",
|
||||||
lock_path: "./editable[d",
|
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -22,7 +22,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "<REQUIREMENTS_DIR>/scripts/packages/black_editable",
|
install_path: "<REQUIREMENTS_DIR>/scripts/packages/black_editable",
|
||||||
lock_path: "./scripts/packages/black_editable",
|
|
||||||
editable: false,
|
editable: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -72,7 +71,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "<REQUIREMENTS_DIR>/scripts/packages/black_editable",
|
install_path: "<REQUIREMENTS_DIR>/scripts/packages/black_editable",
|
||||||
lock_path: "./scripts/packages/black_editable",
|
|
||||||
editable: false,
|
editable: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -126,7 +124,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "<REQUIREMENTS_DIR>/scripts/packages/black_editable",
|
install_path: "<REQUIREMENTS_DIR>/scripts/packages/black_editable",
|
||||||
lock_path: "scripts/packages/black_editable",
|
|
||||||
editable: false,
|
editable: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -24,7 +24,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
lock_path: "./editable",
|
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -81,7 +80,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
lock_path: "./editable",
|
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -138,7 +136,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
lock_path: "./editable",
|
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -195,7 +192,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
lock_path: "./editable",
|
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -252,7 +248,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
lock_path: "./editable",
|
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -302,7 +297,6 @@ RequirementsTxt {
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
install_path: "<REQUIREMENTS_DIR>/editable[d",
|
install_path: "<REQUIREMENTS_DIR>/editable[d",
|
||||||
lock_path: "./editable[d",
|
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -35,7 +35,6 @@ anyhow = { workspace = true }
|
||||||
fs-err = { workspace = true }
|
fs-err = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
nanoid = { workspace = true }
|
nanoid = { workspace = true }
|
||||||
path-absolutize = { workspace = true }
|
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
reqwest-middleware = { workspace = true }
|
reqwest-middleware = { workspace = true }
|
||||||
rmp-serde = { workspace = true }
|
rmp-serde = { workspace = true }
|
||||||
|
|
|
@ -2,14 +2,14 @@ use std::collections::BTreeMap;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
use distribution_filename::DistExtension;
|
use distribution_filename::DistExtension;
|
||||||
use path_absolutize::Absolutize;
|
|
||||||
use pep440_rs::VersionSpecifiers;
|
use pep440_rs::VersionSpecifiers;
|
||||||
use pep508_rs::{VerbatimUrl, VersionOrUrl};
|
use pep508_rs::{VerbatimUrl, VersionOrUrl};
|
||||||
use pypi_types::{ParsedUrlError, Requirement, RequirementSource, VerbatimParsedUrl};
|
use pypi_types::{ParsedUrlError, Requirement, RequirementSource, VerbatimParsedUrl};
|
||||||
use thiserror::Error;
|
use uv_fs::Simplified;
|
||||||
use url::Url;
|
|
||||||
use uv_fs::{relative_to, Simplified};
|
|
||||||
use uv_git::GitReference;
|
use uv_git::GitReference;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_warnings::warn_user_once;
|
use uv_warnings::warn_user_once;
|
||||||
|
@ -142,14 +142,15 @@ impl LoweredRequirement {
|
||||||
// relative to workspace: `packages/current_project`
|
// relative to workspace: `packages/current_project`
|
||||||
// workspace lock root: `../current_workspace`
|
// workspace lock root: `../current_workspace`
|
||||||
// relative to main workspace: `../current_workspace/packages/current_project`
|
// relative to main workspace: `../current_workspace/packages/current_project`
|
||||||
let relative_to_workspace = relative_to(member.root(), workspace.install_path())
|
let url = VerbatimUrl::parse_absolute_path(member.root())?;
|
||||||
.map_err(LoweringError::RelativeTo)?;
|
let install_path = url.to_file_path().map_err(|()| {
|
||||||
let relative_to_main_workspace = workspace.lock_path().join(relative_to_workspace);
|
LoweringError::RelativeTo(io::Error::new(
|
||||||
let url = VerbatimUrl::parse_absolute_path(member.root())?
|
io::ErrorKind::Other,
|
||||||
.with_given(relative_to_main_workspace.to_string_lossy());
|
"Invalid path in file URL",
|
||||||
|
))
|
||||||
|
})?;
|
||||||
RequirementSource::Directory {
|
RequirementSource::Directory {
|
||||||
install_path: member.root().clone(),
|
install_path,
|
||||||
lock_path: relative_to_main_workspace,
|
|
||||||
url,
|
url,
|
||||||
editable: true,
|
editable: true,
|
||||||
}
|
}
|
||||||
|
@ -360,28 +361,20 @@ fn path_source(
|
||||||
Origin::Workspace => workspace_root,
|
Origin::Workspace => workspace_root,
|
||||||
};
|
};
|
||||||
let url = VerbatimUrl::parse_path(path, base)?.with_given(path.to_string_lossy());
|
let url = VerbatimUrl::parse_path(path, base)?.with_given(path.to_string_lossy());
|
||||||
let absolute_path = path
|
let install_path = url.to_file_path().map_err(|()| {
|
||||||
.to_path_buf()
|
LoweringError::RelativeTo(io::Error::new(
|
||||||
.absolutize_from(base)
|
io::ErrorKind::Other,
|
||||||
.map_err(|err| LoweringError::Absolutize(path.to_path_buf(), err))?
|
"Invalid path in file URL",
|
||||||
.to_path_buf();
|
))
|
||||||
let relative_to_workspace = if path.is_relative() {
|
})?;
|
||||||
// Relative paths in a project are relative to the project root, but the lockfile is
|
let is_dir = if let Ok(metadata) = install_path.metadata() {
|
||||||
// relative to the workspace root.
|
|
||||||
relative_to(&absolute_path, workspace_root).map_err(LoweringError::RelativeTo)?
|
|
||||||
} else {
|
|
||||||
// If the user gave us an absolute path, we respect that.
|
|
||||||
path.to_path_buf()
|
|
||||||
};
|
|
||||||
let is_dir = if let Ok(metadata) = absolute_path.metadata() {
|
|
||||||
metadata.is_dir()
|
metadata.is_dir()
|
||||||
} else {
|
} else {
|
||||||
absolute_path.extension().is_none()
|
install_path.extension().is_none()
|
||||||
};
|
};
|
||||||
if is_dir {
|
if is_dir {
|
||||||
Ok(RequirementSource::Directory {
|
Ok(RequirementSource::Directory {
|
||||||
install_path: absolute_path,
|
install_path,
|
||||||
lock_path: relative_to_workspace,
|
|
||||||
url,
|
url,
|
||||||
editable,
|
editable,
|
||||||
})
|
})
|
||||||
|
@ -390,10 +383,9 @@ fn path_source(
|
||||||
return Err(LoweringError::EditableFile(url.to_string()));
|
return Err(LoweringError::EditableFile(url.to_string()));
|
||||||
}
|
}
|
||||||
Ok(RequirementSource::Path {
|
Ok(RequirementSource::Path {
|
||||||
install_path: absolute_path,
|
ext: DistExtension::from_path(&install_path)
|
||||||
lock_path: relative_to_workspace,
|
|
||||||
ext: DistExtension::from_path(path)
|
|
||||||
.map_err(|err| ParsedUrlError::MissingExtensionPath(path.to_path_buf(), err))?,
|
.map_err(|err| ParsedUrlError::MissingExtensionPath(path.to_path_buf(), err))?,
|
||||||
|
install_path,
|
||||||
url,
|
url,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,6 @@ impl Metadata {
|
||||||
pub async fn from_workspace(
|
pub async fn from_workspace(
|
||||||
metadata: Metadata23,
|
metadata: Metadata23,
|
||||||
install_path: &Path,
|
install_path: &Path,
|
||||||
lock_path: &Path,
|
|
||||||
sources: SourceStrategy,
|
sources: SourceStrategy,
|
||||||
) -> Result<Self, MetadataError> {
|
) -> Result<Self, MetadataError> {
|
||||||
// Lower the requirements.
|
// Lower the requirements.
|
||||||
|
@ -75,7 +74,6 @@ impl Metadata {
|
||||||
provides_extras: metadata.provides_extras,
|
provides_extras: metadata.provides_extras,
|
||||||
},
|
},
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
sources,
|
sources,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -37,7 +37,6 @@ impl RequiresDist {
|
||||||
pub async fn from_project_maybe_workspace(
|
pub async fn from_project_maybe_workspace(
|
||||||
metadata: pypi_types::RequiresDist,
|
metadata: pypi_types::RequiresDist,
|
||||||
install_path: &Path,
|
install_path: &Path,
|
||||||
lock_path: &Path,
|
|
||||||
sources: SourceStrategy,
|
sources: SourceStrategy,
|
||||||
) -> Result<Self, MetadataError> {
|
) -> Result<Self, MetadataError> {
|
||||||
match sources {
|
match sources {
|
||||||
|
@ -46,7 +45,6 @@ impl RequiresDist {
|
||||||
// TODO(konsti): Cache workspace discovery.
|
// TODO(konsti): Cache workspace discovery.
|
||||||
let Some(project_workspace) = ProjectWorkspace::from_maybe_project_root(
|
let Some(project_workspace) = ProjectWorkspace::from_maybe_project_root(
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
&DiscoveryOptions::default(),
|
&DiscoveryOptions::default(),
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
|
@ -160,7 +158,6 @@ mod test {
|
||||||
let pyproject_toml = PyProjectToml::from_string(contents.to_string())?;
|
let pyproject_toml = PyProjectToml::from_string(contents.to_string())?;
|
||||||
let path = Path::new("pyproject.toml");
|
let path = Path::new("pyproject.toml");
|
||||||
let project_workspace = ProjectWorkspace::from_project(
|
let project_workspace = ProjectWorkspace::from_project(
|
||||||
path,
|
|
||||||
path,
|
path,
|
||||||
pyproject_toml
|
pyproject_toml
|
||||||
.project
|
.project
|
||||||
|
|
|
@ -425,7 +425,6 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
||||||
let requires_dist = RequiresDist::from_project_maybe_workspace(
|
let requires_dist = RequiresDist::from_project_maybe_workspace(
|
||||||
requires_dist,
|
requires_dist,
|
||||||
project_root,
|
project_root,
|
||||||
project_root,
|
|
||||||
self.build_context.sources(),
|
self.build_context.sources(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -1009,7 +1008,6 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
||||||
Metadata::from_workspace(
|
Metadata::from_workspace(
|
||||||
metadata,
|
metadata,
|
||||||
resource.install_path.as_ref(),
|
resource.install_path.as_ref(),
|
||||||
resource.lock_path.as_ref(),
|
|
||||||
self.build_context.sources(),
|
self.build_context.sources(),
|
||||||
)
|
)
|
||||||
.await?,
|
.await?,
|
||||||
|
@ -1024,7 +1022,6 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
||||||
Metadata::from_workspace(
|
Metadata::from_workspace(
|
||||||
metadata,
|
metadata,
|
||||||
resource.install_path.as_ref(),
|
resource.install_path.as_ref(),
|
||||||
resource.lock_path.as_ref(),
|
|
||||||
self.build_context.sources(),
|
self.build_context.sources(),
|
||||||
)
|
)
|
||||||
.await?,
|
.await?,
|
||||||
|
@ -1049,7 +1046,6 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
||||||
Metadata::from_workspace(
|
Metadata::from_workspace(
|
||||||
metadata,
|
metadata,
|
||||||
resource.install_path.as_ref(),
|
resource.install_path.as_ref(),
|
||||||
resource.lock_path.as_ref(),
|
|
||||||
self.build_context.sources(),
|
self.build_context.sources(),
|
||||||
)
|
)
|
||||||
.await?,
|
.await?,
|
||||||
|
@ -1081,7 +1077,6 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
||||||
Metadata::from_workspace(
|
Metadata::from_workspace(
|
||||||
metadata,
|
metadata,
|
||||||
resource.install_path.as_ref(),
|
resource.install_path.as_ref(),
|
||||||
resource.lock_path.as_ref(),
|
|
||||||
self.build_context.sources(),
|
self.build_context.sources(),
|
||||||
)
|
)
|
||||||
.await?,
|
.await?,
|
||||||
|
@ -1252,8 +1247,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
||||||
Self::read_static_metadata(source, fetch.path(), resource.subdirectory).await?
|
Self::read_static_metadata(source, fetch.path(), resource.subdirectory).await?
|
||||||
{
|
{
|
||||||
return Ok(ArchiveMetadata::from(
|
return Ok(ArchiveMetadata::from(
|
||||||
Metadata::from_workspace(metadata, &path, &path, self.build_context.sources())
|
Metadata::from_workspace(metadata, &path, self.build_context.sources()).await?,
|
||||||
.await?,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1276,8 +1270,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
||||||
|
|
||||||
debug!("Using cached metadata for: {source}");
|
debug!("Using cached metadata for: {source}");
|
||||||
return Ok(ArchiveMetadata::from(
|
return Ok(ArchiveMetadata::from(
|
||||||
Metadata::from_workspace(metadata, &path, &path, self.build_context.sources())
|
Metadata::from_workspace(metadata, &path, self.build_context.sources()).await?,
|
||||||
.await?,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1297,8 +1290,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
||||||
.map_err(Error::CacheWrite)?;
|
.map_err(Error::CacheWrite)?;
|
||||||
|
|
||||||
return Ok(ArchiveMetadata::from(
|
return Ok(ArchiveMetadata::from(
|
||||||
Metadata::from_workspace(metadata, &path, &path, self.build_context.sources())
|
Metadata::from_workspace(metadata, &path, self.build_context.sources()).await?,
|
||||||
.await?,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1324,13 +1316,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
||||||
.map_err(Error::CacheWrite)?;
|
.map_err(Error::CacheWrite)?;
|
||||||
|
|
||||||
Ok(ArchiveMetadata::from(
|
Ok(ArchiveMetadata::from(
|
||||||
Metadata::from_workspace(
|
Metadata::from_workspace(metadata, fetch.path(), self.build_context.sources()).await?,
|
||||||
metadata,
|
|
||||||
fetch.path(),
|
|
||||||
fetch.path(),
|
|
||||||
self.build_context.sources(),
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -266,7 +266,6 @@ impl<'a> Planner<'a> {
|
||||||
url,
|
url,
|
||||||
editable,
|
editable,
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
} => {
|
} => {
|
||||||
// Convert to an absolute path.
|
// Convert to an absolute path.
|
||||||
let install_path = std::path::absolute(install_path)?;
|
let install_path = std::path::absolute(install_path)?;
|
||||||
|
@ -283,7 +282,6 @@ impl<'a> Planner<'a> {
|
||||||
name: requirement.name.clone(),
|
name: requirement.name.clone(),
|
||||||
url: url.clone(),
|
url: url.clone(),
|
||||||
install_path,
|
install_path,
|
||||||
lock_path: lock_path.clone(),
|
|
||||||
editable: *editable,
|
editable: *editable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -305,7 +303,6 @@ impl<'a> Planner<'a> {
|
||||||
ext,
|
ext,
|
||||||
url,
|
url,
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
} => {
|
} => {
|
||||||
// Convert to an absolute path.
|
// Convert to an absolute path.
|
||||||
let install_path = std::path::absolute(install_path)?;
|
let install_path = std::path::absolute(install_path)?;
|
||||||
|
@ -335,13 +332,12 @@ impl<'a> Planner<'a> {
|
||||||
filename,
|
filename,
|
||||||
url: url.clone(),
|
url: url.clone(),
|
||||||
install_path,
|
install_path,
|
||||||
lock_path: lock_path.clone(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if !wheel.filename.is_compatible(tags) {
|
if !wheel.filename.is_compatible(tags) {
|
||||||
bail!(
|
bail!(
|
||||||
"A path dependency is incompatible with the current platform: {}",
|
"A path dependency is incompatible with the current platform: {}",
|
||||||
wheel.lock_path.user_display()
|
wheel.install_path.user_display()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,7 +383,6 @@ impl<'a> Planner<'a> {
|
||||||
name: requirement.name.clone(),
|
name: requirement.name.clone(),
|
||||||
url: url.clone(),
|
url: url.clone(),
|
||||||
install_path,
|
install_path,
|
||||||
lock_path: lock_path.clone(),
|
|
||||||
ext: *ext,
|
ext: *ext,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -150,7 +150,6 @@ impl RequirementSatisfaction {
|
||||||
}
|
}
|
||||||
RequirementSource::Path {
|
RequirementSource::Path {
|
||||||
install_path: requested_path,
|
install_path: requested_path,
|
||||||
lock_path: _,
|
|
||||||
ext: _,
|
ext: _,
|
||||||
url: _,
|
url: _,
|
||||||
} => {
|
} => {
|
||||||
|
@ -197,7 +196,6 @@ impl RequirementSatisfaction {
|
||||||
}
|
}
|
||||||
RequirementSource::Directory {
|
RequirementSource::Directory {
|
||||||
install_path: requested_path,
|
install_path: requested_path,
|
||||||
lock_path: _,
|
|
||||||
editable: requested_editable,
|
editable: requested_editable,
|
||||||
url: _,
|
url: _,
|
||||||
} => {
|
} => {
|
||||||
|
|
|
@ -283,26 +283,17 @@ fn required_dist(requirement: &Requirement) -> Result<Option<Dist>, distribution
|
||||||
}
|
}
|
||||||
RequirementSource::Path {
|
RequirementSource::Path {
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
ext,
|
ext,
|
||||||
url,
|
url,
|
||||||
} => Dist::from_file_url(
|
} => Dist::from_file_url(requirement.name.clone(), url.clone(), install_path, *ext)?,
|
||||||
requirement.name.clone(),
|
|
||||||
url.clone(),
|
|
||||||
install_path,
|
|
||||||
lock_path,
|
|
||||||
*ext,
|
|
||||||
)?,
|
|
||||||
RequirementSource::Directory {
|
RequirementSource::Directory {
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
url,
|
url,
|
||||||
editable,
|
editable,
|
||||||
} => Dist::from_directory_url(
|
} => Dist::from_directory_url(
|
||||||
requirement.name.clone(),
|
requirement.name.clone(),
|
||||||
url.clone(),
|
url.clone(),
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
*editable,
|
*editable,
|
||||||
)?,
|
)?,
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -171,7 +171,6 @@ impl<'a, Context: BuildContext> SourceTreeResolver<'a, Context> {
|
||||||
let source = SourceUrl::Directory(DirectorySourceUrl {
|
let source = SourceUrl::Directory(DirectorySourceUrl {
|
||||||
url: &url,
|
url: &url,
|
||||||
install_path: Cow::Borrowed(source_tree),
|
install_path: Cow::Borrowed(source_tree),
|
||||||
lock_path: Cow::Borrowed(source_tree),
|
|
||||||
editable: false,
|
editable: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -256,7 +256,6 @@ impl<'a, Context: BuildContext> NamedRequirementsResolver<'a, Context> {
|
||||||
SourceUrl::Directory(DirectorySourceUrl {
|
SourceUrl::Directory(DirectorySourceUrl {
|
||||||
url: &requirement.url.verbatim,
|
url: &requirement.url.verbatim,
|
||||||
install_path: Cow::Borrowed(&parsed_directory_url.install_path),
|
install_path: Cow::Borrowed(&parsed_directory_url.install_path),
|
||||||
lock_path: Cow::Borrowed(&parsed_directory_url.lock_path),
|
|
||||||
editable: parsed_directory_url.editable,
|
editable: parsed_directory_url.editable,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ use std::borrow::Cow;
|
||||||
use std::collections::{BTreeMap, BTreeSet, VecDeque};
|
use std::collections::{BTreeMap, BTreeSet, VecDeque};
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
@ -30,7 +31,7 @@ use pypi_types::{
|
||||||
};
|
};
|
||||||
use uv_configuration::ExtrasSpecification;
|
use uv_configuration::ExtrasSpecification;
|
||||||
use uv_distribution::DistributionDatabase;
|
use uv_distribution::DistributionDatabase;
|
||||||
use uv_fs::{relative_to, PortablePath, PortablePathBuf, Simplified};
|
use uv_fs::{relative_to, PortablePath, PortablePathBuf};
|
||||||
use uv_git::{GitReference, GitSha, RepositoryReference, ResolvedRepositoryReference};
|
use uv_git::{GitReference, GitSha, RepositoryReference, ResolvedRepositoryReference};
|
||||||
use uv_normalize::{ExtraName, GroupName, PackageName};
|
use uv_normalize::{ExtraName, GroupName, PackageName};
|
||||||
use uv_types::BuildContext;
|
use uv_types::BuildContext;
|
||||||
|
@ -75,7 +76,7 @@ pub struct Lock {
|
||||||
|
|
||||||
impl Lock {
|
impl Lock {
|
||||||
/// Initialize a [`Lock`] from a [`ResolutionGraph`].
|
/// Initialize a [`Lock`] from a [`ResolutionGraph`].
|
||||||
pub fn from_resolution_graph(graph: &ResolutionGraph) -> Result<Self, LockError> {
|
pub fn from_resolution_graph(graph: &ResolutionGraph, root: &Path) -> Result<Self, LockError> {
|
||||||
let mut locked_dists = BTreeMap::new();
|
let mut locked_dists = BTreeMap::new();
|
||||||
|
|
||||||
// Lock all base packages.
|
// Lock all base packages.
|
||||||
|
@ -88,7 +89,7 @@ impl Lock {
|
||||||
.fork_markers(dist.name(), &dist.version, dist.dist.version_or_url().url())
|
.fork_markers(dist.name(), &dist.version, dist.dist.version_or_url().url())
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let mut locked_dist = Package::from_annotated_dist(dist, fork_markers)?;
|
let mut locked_dist = Package::from_annotated_dist(dist, fork_markers, root)?;
|
||||||
|
|
||||||
// Add all dependencies
|
// Add all dependencies
|
||||||
for edge in graph.petgraph.edges(node_index) {
|
for edge in graph.petgraph.edges(node_index) {
|
||||||
|
@ -97,7 +98,7 @@ impl Lock {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let marker = edge.weight().clone();
|
let marker = edge.weight().clone();
|
||||||
locked_dist.add_dependency(dependency_dist, marker);
|
locked_dist.add_dependency(dependency_dist, marker, root)?;
|
||||||
}
|
}
|
||||||
let id = locked_dist.id.clone();
|
let id = locked_dist.id.clone();
|
||||||
if let Some(locked_dist) = locked_dists.insert(id, locked_dist) {
|
if let Some(locked_dist) = locked_dists.insert(id, locked_dist) {
|
||||||
|
@ -115,7 +116,7 @@ impl Lock {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if let Some(extra) = dist.extra.as_ref() {
|
if let Some(extra) = dist.extra.as_ref() {
|
||||||
let id = PackageId::from_annotated_dist(dist);
|
let id = PackageId::from_annotated_dist(dist, root)?;
|
||||||
let Some(locked_dist) = locked_dists.get_mut(&id) else {
|
let Some(locked_dist) = locked_dists.get_mut(&id) else {
|
||||||
return Err(LockErrorKind::MissingExtraBase {
|
return Err(LockErrorKind::MissingExtraBase {
|
||||||
id,
|
id,
|
||||||
|
@ -129,11 +130,16 @@ impl Lock {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let marker = edge.weight().clone();
|
let marker = edge.weight().clone();
|
||||||
locked_dist.add_optional_dependency(extra.clone(), dependency_dist, marker);
|
locked_dist.add_optional_dependency(
|
||||||
|
extra.clone(),
|
||||||
|
dependency_dist,
|
||||||
|
marker,
|
||||||
|
root,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(group) = dist.dev.as_ref() {
|
if let Some(group) = dist.dev.as_ref() {
|
||||||
let id = PackageId::from_annotated_dist(dist);
|
let id = PackageId::from_annotated_dist(dist, root)?;
|
||||||
let Some(locked_dist) = locked_dists.get_mut(&id) else {
|
let Some(locked_dist) = locked_dists.get_mut(&id) else {
|
||||||
return Err(LockErrorKind::MissingDevBase {
|
return Err(LockErrorKind::MissingDevBase {
|
||||||
id,
|
id,
|
||||||
|
@ -147,7 +153,7 @@ impl Lock {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let marker = edge.weight().clone();
|
let marker = edge.weight().clone();
|
||||||
locked_dist.add_dev_dependency(group.clone(), dependency_dist, marker);
|
locked_dist.add_dev_dependency(group.clone(), dependency_dist, marker, root)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -981,6 +987,8 @@ pub struct ResolverManifest {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResolverManifest {
|
impl ResolverManifest {
|
||||||
|
/// Initialize a [`ResolverManifest`] with the given members, requirements, constraints, and
|
||||||
|
/// overrides.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
members: impl IntoIterator<Item = PackageName>,
|
members: impl IntoIterator<Item = PackageName>,
|
||||||
requirements: impl IntoIterator<Item = Requirement>,
|
requirements: impl IntoIterator<Item = Requirement>,
|
||||||
|
@ -994,6 +1002,28 @@ impl ResolverManifest {
|
||||||
overrides: overrides.into_iter().collect(),
|
overrides: overrides.into_iter().collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert the manifest to a relative form using the given workspace.
|
||||||
|
pub fn relative_to(self, workspace: &Workspace) -> Result<Self, io::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
members: self.members,
|
||||||
|
requirements: self
|
||||||
|
.requirements
|
||||||
|
.into_iter()
|
||||||
|
.map(|requirement| requirement.relative_to(workspace.install_path()))
|
||||||
|
.collect::<Result<BTreeSet<_>, _>>()?,
|
||||||
|
constraints: self
|
||||||
|
.constraints
|
||||||
|
.into_iter()
|
||||||
|
.map(|requirement| requirement.relative_to(workspace.install_path()))
|
||||||
|
.collect::<Result<BTreeSet<_>, _>>()?,
|
||||||
|
overrides: self
|
||||||
|
.overrides
|
||||||
|
.into_iter()
|
||||||
|
.map(|requirement| requirement.relative_to(workspace.install_path()))
|
||||||
|
.collect::<Result<BTreeSet<_>, _>>()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Deserialize)]
|
||||||
|
@ -1094,8 +1124,9 @@ impl Package {
|
||||||
fn from_annotated_dist(
|
fn from_annotated_dist(
|
||||||
annotated_dist: &AnnotatedDist,
|
annotated_dist: &AnnotatedDist,
|
||||||
fork_markers: Vec<MarkerTree>,
|
fork_markers: Vec<MarkerTree>,
|
||||||
|
root: &Path,
|
||||||
) -> Result<Self, LockError> {
|
) -> Result<Self, LockError> {
|
||||||
let id = PackageId::from_annotated_dist(annotated_dist);
|
let id = PackageId::from_annotated_dist(annotated_dist, root)?;
|
||||||
let sdist = SourceDist::from_annotated_dist(&id, annotated_dist)?;
|
let sdist = SourceDist::from_annotated_dist(&id, annotated_dist)?;
|
||||||
let wheels = Wheel::from_annotated_dist(annotated_dist)?;
|
let wheels = Wheel::from_annotated_dist(annotated_dist)?;
|
||||||
let requires_dist = if id.source.is_immutable() {
|
let requires_dist = if id.source.is_immutable() {
|
||||||
|
@ -1106,7 +1137,9 @@ impl Package {
|
||||||
.requires_dist
|
.requires_dist
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect()
|
.map(|requirement| requirement.relative_to(root))
|
||||||
|
.collect::<Result<_, _>>()
|
||||||
|
.map_err(LockErrorKind::RequirementRelativePath)?
|
||||||
};
|
};
|
||||||
let requires_dev = if id.source.is_immutable() {
|
let requires_dev = if id.source.is_immutable() {
|
||||||
BTreeMap::default()
|
BTreeMap::default()
|
||||||
|
@ -1115,8 +1148,16 @@ impl Package {
|
||||||
.metadata
|
.metadata
|
||||||
.dev_dependencies
|
.dev_dependencies
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, v)| (k.clone(), v.iter().cloned().collect()))
|
.map(|(group, requirements)| {
|
||||||
.collect()
|
let requirements = requirements
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|requirement| requirement.relative_to(root))
|
||||||
|
.collect::<Result<_, _>>()
|
||||||
|
.map_err(LockErrorKind::RequirementRelativePath)?;
|
||||||
|
Ok::<_, LockError>((group.clone(), requirements))
|
||||||
|
})
|
||||||
|
.collect::<Result<_, _>>()?
|
||||||
};
|
};
|
||||||
Ok(Package {
|
Ok(Package {
|
||||||
id,
|
id,
|
||||||
|
@ -1134,17 +1175,24 @@ impl Package {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add the [`AnnotatedDist`] as a dependency of the [`Package`].
|
/// Add the [`AnnotatedDist`] as a dependency of the [`Package`].
|
||||||
fn add_dependency(&mut self, annotated_dist: &AnnotatedDist, marker: MarkerTree) {
|
fn add_dependency(
|
||||||
let new_dep = Dependency::from_annotated_dist(annotated_dist, marker);
|
&mut self,
|
||||||
|
annotated_dist: &AnnotatedDist,
|
||||||
|
marker: MarkerTree,
|
||||||
|
root: &Path,
|
||||||
|
) -> Result<(), LockError> {
|
||||||
|
let new_dep = Dependency::from_annotated_dist(annotated_dist, marker, root)?;
|
||||||
for existing_dep in &mut self.dependencies {
|
for existing_dep in &mut self.dependencies {
|
||||||
if existing_dep.package_id == new_dep.package_id
|
if existing_dep.package_id == new_dep.package_id
|
||||||
&& existing_dep.marker == new_dep.marker
|
&& existing_dep.marker == new_dep.marker
|
||||||
{
|
{
|
||||||
existing_dep.extra.extend(new_dep.extra);
|
existing_dep.extra.extend(new_dep.extra);
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dependencies.push(new_dep);
|
self.dependencies.push(new_dep);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add the [`AnnotatedDist`] as an optional dependency of the [`Package`].
|
/// Add the [`AnnotatedDist`] as an optional dependency of the [`Package`].
|
||||||
|
@ -1153,16 +1201,19 @@ impl Package {
|
||||||
extra: ExtraName,
|
extra: ExtraName,
|
||||||
annotated_dist: &AnnotatedDist,
|
annotated_dist: &AnnotatedDist,
|
||||||
marker: MarkerTree,
|
marker: MarkerTree,
|
||||||
) {
|
root: &Path,
|
||||||
let dep = Dependency::from_annotated_dist(annotated_dist, marker);
|
) -> Result<(), LockError> {
|
||||||
|
let dep = Dependency::from_annotated_dist(annotated_dist, marker, root)?;
|
||||||
let optional_deps = self.optional_dependencies.entry(extra).or_default();
|
let optional_deps = self.optional_dependencies.entry(extra).or_default();
|
||||||
for existing_dep in &mut *optional_deps {
|
for existing_dep in &mut *optional_deps {
|
||||||
if existing_dep.package_id == dep.package_id && existing_dep.marker == dep.marker {
|
if existing_dep.package_id == dep.package_id && existing_dep.marker == dep.marker {
|
||||||
existing_dep.extra.extend(dep.extra);
|
existing_dep.extra.extend(dep.extra);
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
optional_deps.push(dep);
|
optional_deps.push(dep);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add the [`AnnotatedDist`] as a development dependency of the [`Package`].
|
/// Add the [`AnnotatedDist`] as a development dependency of the [`Package`].
|
||||||
|
@ -1171,16 +1222,19 @@ impl Package {
|
||||||
dev: GroupName,
|
dev: GroupName,
|
||||||
annotated_dist: &AnnotatedDist,
|
annotated_dist: &AnnotatedDist,
|
||||||
marker: MarkerTree,
|
marker: MarkerTree,
|
||||||
) {
|
root: &Path,
|
||||||
let dep = Dependency::from_annotated_dist(annotated_dist, marker);
|
) -> Result<(), LockError> {
|
||||||
|
let dep = Dependency::from_annotated_dist(annotated_dist, marker, root)?;
|
||||||
let dev_deps = self.dev_dependencies.entry(dev).or_default();
|
let dev_deps = self.dev_dependencies.entry(dev).or_default();
|
||||||
for existing_dep in &mut *dev_deps {
|
for existing_dep in &mut *dev_deps {
|
||||||
if existing_dep.package_id == dep.package_id && existing_dep.marker == dep.marker {
|
if existing_dep.package_id == dep.package_id && existing_dep.marker == dep.marker {
|
||||||
existing_dep.extra.extend(dep.extra);
|
existing_dep.extra.extend(dep.extra);
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_deps.push(dep);
|
dev_deps.push(dep);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the [`Package`] to a [`Dist`] that can be used in installation.
|
/// Convert the [`Package`] to a [`Dist`] that can be used in installation.
|
||||||
|
@ -1206,7 +1260,6 @@ impl Package {
|
||||||
filename,
|
filename,
|
||||||
url: verbatim_url(workspace_root.join(path), &self.id)?,
|
url: verbatim_url(workspace_root.join(path), &self.id)?,
|
||||||
install_path: workspace_root.join(path),
|
install_path: workspace_root.join(path),
|
||||||
lock_path: path.clone(),
|
|
||||||
};
|
};
|
||||||
let built_dist = BuiltDist::Path(path_dist);
|
let built_dist = BuiltDist::Path(path_dist);
|
||||||
Ok(Dist::Built(built_dist))
|
Ok(Dist::Built(built_dist))
|
||||||
|
@ -1268,7 +1321,6 @@ impl Package {
|
||||||
name: self.id.name.clone(),
|
name: self.id.name.clone(),
|
||||||
url: verbatim_url(workspace_root.join(path), &self.id)?,
|
url: verbatim_url(workspace_root.join(path), &self.id)?,
|
||||||
install_path: workspace_root.join(path),
|
install_path: workspace_root.join(path),
|
||||||
lock_path: path.clone(),
|
|
||||||
ext: SourceDistExtension::from_path(path)?,
|
ext: SourceDistExtension::from_path(path)?,
|
||||||
};
|
};
|
||||||
distribution_types::SourceDist::Path(path_dist)
|
distribution_types::SourceDist::Path(path_dist)
|
||||||
|
@ -1278,7 +1330,6 @@ impl Package {
|
||||||
name: self.id.name.clone(),
|
name: self.id.name.clone(),
|
||||||
url: verbatim_url(workspace_root.join(path), &self.id)?,
|
url: verbatim_url(workspace_root.join(path), &self.id)?,
|
||||||
install_path: workspace_root.join(path),
|
install_path: workspace_root.join(path),
|
||||||
lock_path: path.clone(),
|
|
||||||
editable: false,
|
editable: false,
|
||||||
};
|
};
|
||||||
distribution_types::SourceDist::Directory(dir_dist)
|
distribution_types::SourceDist::Directory(dir_dist)
|
||||||
|
@ -1288,7 +1339,6 @@ impl Package {
|
||||||
name: self.id.name.clone(),
|
name: self.id.name.clone(),
|
||||||
url: verbatim_url(workspace_root.join(path), &self.id)?,
|
url: verbatim_url(workspace_root.join(path), &self.id)?,
|
||||||
install_path: workspace_root.join(path),
|
install_path: workspace_root.join(path),
|
||||||
lock_path: path.clone(),
|
|
||||||
editable: true,
|
editable: true,
|
||||||
};
|
};
|
||||||
distribution_types::SourceDist::Directory(dir_dist)
|
distribution_types::SourceDist::Directory(dir_dist)
|
||||||
|
@ -1691,15 +1741,18 @@ pub(crate) struct PackageId {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackageId {
|
impl PackageId {
|
||||||
fn from_annotated_dist(annotated_dist: &AnnotatedDist) -> PackageId {
|
fn from_annotated_dist(
|
||||||
|
annotated_dist: &AnnotatedDist,
|
||||||
|
root: &Path,
|
||||||
|
) -> Result<PackageId, LockError> {
|
||||||
let name = annotated_dist.metadata.name.clone();
|
let name = annotated_dist.metadata.name.clone();
|
||||||
let version = annotated_dist.metadata.version.clone();
|
let version = annotated_dist.metadata.version.clone();
|
||||||
let source = Source::from_resolved_dist(&annotated_dist.dist);
|
let source = Source::from_resolved_dist(&annotated_dist.dist, root)?;
|
||||||
PackageId {
|
Ok(Self {
|
||||||
name,
|
name,
|
||||||
version,
|
version,
|
||||||
source,
|
source,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes this package ID inline into the table given.
|
/// Writes this package ID inline into the table given.
|
||||||
|
@ -1792,43 +1845,50 @@ enum Source {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Source {
|
impl Source {
|
||||||
fn from_resolved_dist(resolved_dist: &ResolvedDist) -> Source {
|
fn from_resolved_dist(resolved_dist: &ResolvedDist, root: &Path) -> Result<Source, LockError> {
|
||||||
match *resolved_dist {
|
match *resolved_dist {
|
||||||
// We pass empty installed packages for locking.
|
// We pass empty installed packages for locking.
|
||||||
ResolvedDist::Installed(_) => unreachable!(),
|
ResolvedDist::Installed(_) => unreachable!(),
|
||||||
ResolvedDist::Installable(ref dist) => Source::from_dist(dist),
|
ResolvedDist::Installable(ref dist) => Source::from_dist(dist, root),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_dist(dist: &Dist) -> Source {
|
fn from_dist(dist: &Dist, root: &Path) -> Result<Source, LockError> {
|
||||||
match *dist {
|
match *dist {
|
||||||
Dist::Built(ref built_dist) => Source::from_built_dist(built_dist),
|
Dist::Built(ref built_dist) => Source::from_built_dist(built_dist, root),
|
||||||
Dist::Source(ref source_dist) => Source::from_source_dist(source_dist),
|
Dist::Source(ref source_dist) => Source::from_source_dist(source_dist, root),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_built_dist(built_dist: &BuiltDist) -> Source {
|
fn from_built_dist(built_dist: &BuiltDist, root: &Path) -> Result<Source, LockError> {
|
||||||
match *built_dist {
|
match *built_dist {
|
||||||
BuiltDist::Registry(ref reg_dist) => Source::from_registry_built_dist(reg_dist),
|
BuiltDist::Registry(ref reg_dist) => Ok(Source::from_registry_built_dist(reg_dist)),
|
||||||
BuiltDist::DirectUrl(ref direct_dist) => Source::from_direct_built_dist(direct_dist),
|
BuiltDist::DirectUrl(ref direct_dist) => {
|
||||||
BuiltDist::Path(ref path_dist) => Source::from_path_built_dist(path_dist),
|
Ok(Source::from_direct_built_dist(direct_dist))
|
||||||
|
}
|
||||||
|
BuiltDist::Path(ref path_dist) => Source::from_path_built_dist(path_dist, root),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_source_dist(source_dist: &distribution_types::SourceDist) -> Source {
|
fn from_source_dist(
|
||||||
|
source_dist: &distribution_types::SourceDist,
|
||||||
|
root: &Path,
|
||||||
|
) -> Result<Source, LockError> {
|
||||||
match *source_dist {
|
match *source_dist {
|
||||||
distribution_types::SourceDist::Registry(ref reg_dist) => {
|
distribution_types::SourceDist::Registry(ref reg_dist) => {
|
||||||
Source::from_registry_source_dist(reg_dist)
|
Ok(Source::from_registry_source_dist(reg_dist))
|
||||||
}
|
}
|
||||||
distribution_types::SourceDist::DirectUrl(ref direct_dist) => {
|
distribution_types::SourceDist::DirectUrl(ref direct_dist) => {
|
||||||
Source::from_direct_source_dist(direct_dist)
|
Ok(Source::from_direct_source_dist(direct_dist))
|
||||||
|
}
|
||||||
|
distribution_types::SourceDist::Git(ref git_dist) => {
|
||||||
|
Ok(Source::from_git_dist(git_dist))
|
||||||
}
|
}
|
||||||
distribution_types::SourceDist::Git(ref git_dist) => Source::from_git_dist(git_dist),
|
|
||||||
distribution_types::SourceDist::Path(ref path_dist) => {
|
distribution_types::SourceDist::Path(ref path_dist) => {
|
||||||
Source::from_path_source_dist(path_dist)
|
Source::from_path_source_dist(path_dist, root)
|
||||||
}
|
}
|
||||||
distribution_types::SourceDist::Directory(ref directory) => {
|
distribution_types::SourceDist::Directory(ref directory) => {
|
||||||
Source::from_directory_source_dist(directory)
|
Source::from_directory_source_dist(directory, root)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1861,22 +1921,29 @@ impl Source {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_path_built_dist(path_dist: &PathBuiltDist) -> Source {
|
fn from_path_built_dist(path_dist: &PathBuiltDist, root: &Path) -> Result<Source, LockError> {
|
||||||
let path = path_dist.lock_path.simplified().to_path_buf();
|
let path = relative_to(&path_dist.install_path, root)
|
||||||
Source::Path(path)
|
.map_err(LockErrorKind::DistributionRelativePath)?;
|
||||||
|
Ok(Source::Path(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_path_source_dist(path_dist: &PathSourceDist) -> Source {
|
fn from_path_source_dist(path_dist: &PathSourceDist, root: &Path) -> Result<Source, LockError> {
|
||||||
let path = path_dist.install_path.simplified().to_path_buf();
|
let path = relative_to(&path_dist.install_path, root)
|
||||||
Source::Path(path)
|
.map_err(LockErrorKind::DistributionRelativePath)?;
|
||||||
|
|
||||||
|
Ok(Source::Path(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_directory_source_dist(directory_dist: &DirectorySourceDist) -> Source {
|
fn from_directory_source_dist(
|
||||||
let path = directory_dist.lock_path.simplified().to_path_buf();
|
directory_dist: &DirectorySourceDist,
|
||||||
|
root: &Path,
|
||||||
|
) -> Result<Source, LockError> {
|
||||||
|
let path = relative_to(&directory_dist.install_path, root)
|
||||||
|
.map_err(LockErrorKind::DistributionRelativePath)?;
|
||||||
if directory_dist.editable {
|
if directory_dist.editable {
|
||||||
Source::Editable(path)
|
Ok(Source::Editable(path))
|
||||||
} else {
|
} else {
|
||||||
Source::Directory(path)
|
Ok(Source::Directory(path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2649,14 +2716,18 @@ struct Dependency {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dependency {
|
impl Dependency {
|
||||||
fn from_annotated_dist(annotated_dist: &AnnotatedDist, marker: MarkerTree) -> Dependency {
|
fn from_annotated_dist(
|
||||||
let package_id = PackageId::from_annotated_dist(annotated_dist);
|
annotated_dist: &AnnotatedDist,
|
||||||
|
marker: MarkerTree,
|
||||||
|
root: &Path,
|
||||||
|
) -> Result<Dependency, LockError> {
|
||||||
|
let package_id = PackageId::from_annotated_dist(annotated_dist, root)?;
|
||||||
let extra = annotated_dist.extra.iter().cloned().collect();
|
let extra = annotated_dist.extra.iter().cloned().collect();
|
||||||
Dependency {
|
Ok(Self {
|
||||||
package_id,
|
package_id,
|
||||||
extra,
|
extra,
|
||||||
marker,
|
marker,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the TOML representation of this dependency.
|
/// Returns the TOML representation of this dependency.
|
||||||
|
@ -2838,19 +2909,11 @@ fn normalize_requirement(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
RequirementSource::Path {
|
RequirementSource::Path {
|
||||||
install_path: _,
|
install_path,
|
||||||
lock_path,
|
|
||||||
ext,
|
ext,
|
||||||
url: _,
|
url: _,
|
||||||
} => {
|
} => {
|
||||||
// When a path requirement comes from the lockfile, `install_path` and `lock_path` are
|
let install_path = uv_fs::normalize_path(&workspace.install_path().join(&install_path));
|
||||||
// both relative to the lockfile.
|
|
||||||
//
|
|
||||||
// When a path requirement is deserialized from package metadata, `install_path` is
|
|
||||||
// absolute, and `lock_path` is relative to the lockfile.
|
|
||||||
let install_path = uv_fs::normalize_path(&workspace.install_path().join(&lock_path));
|
|
||||||
let lock_path = relative_to(workspace.install_path(), &lock_path)
|
|
||||||
.map_err(LockErrorKind::RequirementRelativePath)?;
|
|
||||||
let url = VerbatimUrl::from_path(&install_path)
|
let url = VerbatimUrl::from_path(&install_path)
|
||||||
.map_err(LockErrorKind::RequirementVerbatimUrl)?;
|
.map_err(LockErrorKind::RequirementVerbatimUrl)?;
|
||||||
|
|
||||||
|
@ -2860,7 +2923,6 @@ fn normalize_requirement(
|
||||||
marker: requirement.marker,
|
marker: requirement.marker,
|
||||||
source: RequirementSource::Path {
|
source: RequirementSource::Path {
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
ext,
|
ext,
|
||||||
url,
|
url,
|
||||||
},
|
},
|
||||||
|
@ -2868,14 +2930,11 @@ fn normalize_requirement(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
RequirementSource::Directory {
|
RequirementSource::Directory {
|
||||||
install_path: _,
|
install_path,
|
||||||
lock_path,
|
|
||||||
editable,
|
editable,
|
||||||
url: _,
|
url: _,
|
||||||
} => {
|
} => {
|
||||||
let install_path = uv_fs::normalize_path(&workspace.install_path().join(&lock_path));
|
let install_path = uv_fs::normalize_path(&workspace.install_path().join(&install_path));
|
||||||
let lock_path = relative_to(workspace.install_path(), &lock_path)
|
|
||||||
.map_err(LockErrorKind::RequirementRelativePath)?;
|
|
||||||
let url = VerbatimUrl::from_path(&install_path)
|
let url = VerbatimUrl::from_path(&install_path)
|
||||||
.map_err(LockErrorKind::RequirementVerbatimUrl)?;
|
.map_err(LockErrorKind::RequirementVerbatimUrl)?;
|
||||||
|
|
||||||
|
@ -2885,7 +2944,6 @@ fn normalize_requirement(
|
||||||
marker: requirement.marker,
|
marker: requirement.marker,
|
||||||
source: RequirementSource::Directory {
|
source: RequirementSource::Directory {
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
editable,
|
editable,
|
||||||
url,
|
url,
|
||||||
},
|
},
|
||||||
|
@ -3072,6 +3130,13 @@ enum LockErrorKind {
|
||||||
#[source]
|
#[source]
|
||||||
err: VerbatimUrlError,
|
err: VerbatimUrlError,
|
||||||
},
|
},
|
||||||
|
/// An error that occurs when parsing an existing requirement.
|
||||||
|
#[error("could not compute relative path between workspace and distribution")]
|
||||||
|
DistributionRelativePath(
|
||||||
|
/// The inner error we forward.
|
||||||
|
#[source]
|
||||||
|
std::io::Error,
|
||||||
|
),
|
||||||
/// An error that occurs when an ambiguous `package.dependency` is
|
/// An error that occurs when an ambiguous `package.dependency` is
|
||||||
/// missing a `version` field.
|
/// missing a `version` field.
|
||||||
#[error(
|
#[error(
|
||||||
|
|
|
@ -135,11 +135,9 @@ impl PubGrubRequirement {
|
||||||
ext,
|
ext,
|
||||||
url,
|
url,
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
} => {
|
} => {
|
||||||
let parsed_url = ParsedUrl::Path(ParsedPathUrl::from_source(
|
let parsed_url = ParsedUrl::Path(ParsedPathUrl::from_source(
|
||||||
install_path.clone(),
|
install_path.clone(),
|
||||||
lock_path.clone(),
|
|
||||||
*ext,
|
*ext,
|
||||||
url.to_url(),
|
url.to_url(),
|
||||||
));
|
));
|
||||||
|
@ -149,11 +147,9 @@ impl PubGrubRequirement {
|
||||||
editable,
|
editable,
|
||||||
url,
|
url,
|
||||||
install_path,
|
install_path,
|
||||||
lock_path,
|
|
||||||
} => {
|
} => {
|
||||||
let parsed_url = ParsedUrl::Directory(ParsedDirectoryUrl::from_source(
|
let parsed_url = ParsedUrl::Directory(ParsedDirectoryUrl::from_source(
|
||||||
install_path.clone(),
|
install_path.clone(),
|
||||||
lock_path.clone(),
|
|
||||||
*editable,
|
*editable,
|
||||||
url.to_url(),
|
url.to_url(),
|
||||||
));
|
));
|
||||||
|
|
|
@ -332,13 +332,13 @@ impl Source {
|
||||||
|
|
||||||
let source = match source {
|
let source = match source {
|
||||||
RequirementSource::Registry { .. } => return Ok(None),
|
RequirementSource::Registry { .. } => return Ok(None),
|
||||||
RequirementSource::Path { lock_path, .. } => Source::Path {
|
RequirementSource::Path { install_path, .. } => Source::Path {
|
||||||
editable,
|
editable,
|
||||||
path: lock_path.to_string_lossy().into_owned(),
|
path: install_path.to_string_lossy().into_owned(),
|
||||||
},
|
},
|
||||||
RequirementSource::Directory { lock_path, .. } => Source::Path {
|
RequirementSource::Directory { install_path, .. } => Source::Path {
|
||||||
editable,
|
editable,
|
||||||
path: lock_path.to_string_lossy().into_owned(),
|
path: install_path.to_string_lossy().into_owned(),
|
||||||
},
|
},
|
||||||
RequirementSource::Url {
|
RequirementSource::Url {
|
||||||
subdirectory, url, ..
|
subdirectory, url, ..
|
||||||
|
|
|
@ -10,7 +10,7 @@ use tracing::{debug, trace, warn};
|
||||||
|
|
||||||
use pep508_rs::{MarkerTree, RequirementOrigin, VerbatimUrl};
|
use pep508_rs::{MarkerTree, RequirementOrigin, VerbatimUrl};
|
||||||
use pypi_types::{Requirement, RequirementSource};
|
use pypi_types::{Requirement, RequirementSource};
|
||||||
use uv_fs::{absolutize_path, normalize_path, relative_to, Simplified};
|
use uv_fs::{absolutize_path, Simplified};
|
||||||
use uv_normalize::{GroupName, PackageName, DEV_DEPENDENCIES};
|
use uv_normalize::{GroupName, PackageName, DEV_DEPENDENCIES};
|
||||||
use uv_warnings::warn_user;
|
use uv_warnings::warn_user;
|
||||||
|
|
||||||
|
@ -62,11 +62,6 @@ pub struct Workspace {
|
||||||
/// The workspace root is the directory containing the top level `pyproject.toml` with
|
/// The workspace root is the directory containing the top level `pyproject.toml` with
|
||||||
/// the `uv.tool.workspace`, or the `pyproject.toml` in an implicit single workspace project.
|
/// the `uv.tool.workspace`, or the `pyproject.toml` in an implicit single workspace project.
|
||||||
install_path: PathBuf,
|
install_path: PathBuf,
|
||||||
/// The same path as `install_path`, but relative to the main workspace.
|
|
||||||
///
|
|
||||||
/// We use this value to compute relative paths for workspace-to-workspace dependencies. It's an
|
|
||||||
/// empty path for the main workspace.
|
|
||||||
lock_path: PathBuf,
|
|
||||||
/// The members of the workspace.
|
/// The members of the workspace.
|
||||||
packages: BTreeMap<PackageName, WorkspaceMember>,
|
packages: BTreeMap<PackageName, WorkspaceMember>,
|
||||||
/// The sources table from the workspace `pyproject.toml`.
|
/// The sources table from the workspace `pyproject.toml`.
|
||||||
|
@ -176,8 +171,6 @@ impl Workspace {
|
||||||
|
|
||||||
Self::collect_members(
|
Self::collect_members(
|
||||||
workspace_root.clone(),
|
workspace_root.clone(),
|
||||||
// This method supports only absolute paths.
|
|
||||||
workspace_root,
|
|
||||||
workspace_definition,
|
workspace_definition,
|
||||||
workspace_pyproject_toml,
|
workspace_pyproject_toml,
|
||||||
current_project,
|
current_project,
|
||||||
|
@ -286,11 +279,6 @@ impl Workspace {
|
||||||
marker: MarkerTree::TRUE,
|
marker: MarkerTree::TRUE,
|
||||||
source: RequirementSource::Directory {
|
source: RequirementSource::Directory {
|
||||||
install_path: member.root.clone(),
|
install_path: member.root.clone(),
|
||||||
lock_path: member
|
|
||||||
.root
|
|
||||||
.strip_prefix(&self.install_path)
|
|
||||||
.expect("Project must be below workspace root")
|
|
||||||
.to_path_buf(),
|
|
||||||
editable: true,
|
editable: true,
|
||||||
url,
|
url,
|
||||||
},
|
},
|
||||||
|
@ -421,12 +409,6 @@ impl Workspace {
|
||||||
&self.install_path
|
&self.install_path
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The same path as `install_path()`, but relative to the main workspace. We use this value
|
|
||||||
/// to compute relative paths for workspace-to-workspace dependencies.
|
|
||||||
pub fn lock_path(&self) -> &PathBuf {
|
|
||||||
&self.lock_path
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The path to the workspace virtual environment.
|
/// The path to the workspace virtual environment.
|
||||||
pub fn venv(&self) -> PathBuf {
|
pub fn venv(&self) -> PathBuf {
|
||||||
self.install_path.join(".venv")
|
self.install_path.join(".venv")
|
||||||
|
@ -480,7 +462,6 @@ impl Workspace {
|
||||||
/// Collect the workspace member projects from the `members` and `excludes` entries.
|
/// Collect the workspace member projects from the `members` and `excludes` entries.
|
||||||
async fn collect_members(
|
async fn collect_members(
|
||||||
workspace_root: PathBuf,
|
workspace_root: PathBuf,
|
||||||
lock_path: PathBuf,
|
|
||||||
workspace_definition: ToolUvWorkspace,
|
workspace_definition: ToolUvWorkspace,
|
||||||
workspace_pyproject_toml: PyProjectToml,
|
workspace_pyproject_toml: PyProjectToml,
|
||||||
current_project: Option<WorkspaceMember>,
|
current_project: Option<WorkspaceMember>,
|
||||||
|
@ -644,7 +625,6 @@ impl Workspace {
|
||||||
|
|
||||||
Ok(Workspace {
|
Ok(Workspace {
|
||||||
install_path: workspace_root,
|
install_path: workspace_root,
|
||||||
lock_path,
|
|
||||||
packages: workspace_members,
|
packages: workspace_members,
|
||||||
sources: workspace_sources,
|
sources: workspace_sources,
|
||||||
pyproject_toml: workspace_pyproject_toml,
|
pyproject_toml: workspace_pyproject_toml,
|
||||||
|
@ -817,21 +797,13 @@ impl ProjectWorkspace {
|
||||||
.clone()
|
.clone()
|
||||||
.ok_or_else(|| WorkspaceError::MissingProject(pyproject_path.clone()))?;
|
.ok_or_else(|| WorkspaceError::MissingProject(pyproject_path.clone()))?;
|
||||||
|
|
||||||
Self::from_project(
|
Self::from_project(project_root, &project, &pyproject_toml, options).await
|
||||||
project_root,
|
|
||||||
Path::new(""),
|
|
||||||
&project,
|
|
||||||
&pyproject_toml,
|
|
||||||
options,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If the current directory contains a `pyproject.toml` with a `project` table, discover the
|
/// If the current directory contains a `pyproject.toml` with a `project` table, discover the
|
||||||
/// workspace and return it, otherwise it is a dynamic path dependency and we return `Ok(None)`.
|
/// workspace and return it, otherwise it is a dynamic path dependency and we return `Ok(None)`.
|
||||||
pub async fn from_maybe_project_root(
|
pub async fn from_maybe_project_root(
|
||||||
install_path: &Path,
|
install_path: &Path,
|
||||||
lock_path: &Path,
|
|
||||||
options: &DiscoveryOptions<'_>,
|
options: &DiscoveryOptions<'_>,
|
||||||
) -> Result<Option<Self>, WorkspaceError> {
|
) -> Result<Option<Self>, WorkspaceError> {
|
||||||
// Read the `pyproject.toml`.
|
// Read the `pyproject.toml`.
|
||||||
|
@ -849,8 +821,7 @@ impl ProjectWorkspace {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
match Self::from_project(install_path, lock_path, &project, &pyproject_toml, options).await
|
match Self::from_project(install_path, &project, &pyproject_toml, options).await {
|
||||||
{
|
|
||||||
Ok(workspace) => Ok(Some(workspace)),
|
Ok(workspace) => Ok(Some(workspace)),
|
||||||
Err(WorkspaceError::NonWorkspace(_)) => Ok(None),
|
Err(WorkspaceError::NonWorkspace(_)) => Ok(None),
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
|
@ -894,7 +865,6 @@ impl ProjectWorkspace {
|
||||||
/// Find the workspace for a project.
|
/// Find the workspace for a project.
|
||||||
pub async fn from_project(
|
pub async fn from_project(
|
||||||
install_path: &Path,
|
install_path: &Path,
|
||||||
lock_path: &Path,
|
|
||||||
project: &Project,
|
project: &Project,
|
||||||
project_pyproject_toml: &PyProjectToml,
|
project_pyproject_toml: &PyProjectToml,
|
||||||
options: &DiscoveryOptions<'_>,
|
options: &DiscoveryOptions<'_>,
|
||||||
|
@ -954,8 +924,6 @@ impl ProjectWorkspace {
|
||||||
project_name: project.name.clone(),
|
project_name: project.name.clone(),
|
||||||
workspace: Workspace {
|
workspace: Workspace {
|
||||||
install_path: project_path.clone(),
|
install_path: project_path.clone(),
|
||||||
// The workspace and the project are the same, so the relative path is, too.
|
|
||||||
lock_path: lock_path.to_path_buf(),
|
|
||||||
packages: current_project_as_members,
|
packages: current_project_as_members,
|
||||||
// There may be package sources, but we don't need to duplicate them into the
|
// There may be package sources, but we don't need to duplicate them into the
|
||||||
// workspace sources.
|
// workspace sources.
|
||||||
|
@ -970,28 +938,8 @@ impl ProjectWorkspace {
|
||||||
workspace_root.simplified_display()
|
workspace_root.simplified_display()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Say we have:
|
|
||||||
// ```
|
|
||||||
// root
|
|
||||||
// ├── main_workspace <- The reference point
|
|
||||||
// │ ├── pyproject.toml
|
|
||||||
// │ └── uv.lock
|
|
||||||
// └──current_workspace <- We want this relative to the main workspace
|
|
||||||
// └── packages
|
|
||||||
// └── current_package <- We have this relative to the main workspace
|
|
||||||
// └── pyproject.toml
|
|
||||||
// ```
|
|
||||||
// The lock path we need: `../current_workspace`
|
|
||||||
// workspace root: `/root/current_workspace`
|
|
||||||
// project path: `/root/current_workspace/packages/current_project`
|
|
||||||
// relative to workspace: `../..`
|
|
||||||
// lock path: `../current_workspace`
|
|
||||||
let up_to_root = relative_to(&workspace_root, &project_path)?;
|
|
||||||
let lock_path = normalize_path(&lock_path.join(up_to_root));
|
|
||||||
|
|
||||||
let workspace = Workspace::collect_members(
|
let workspace = Workspace::collect_members(
|
||||||
workspace_root,
|
workspace_root,
|
||||||
lock_path,
|
|
||||||
workspace_definition,
|
workspace_definition,
|
||||||
workspace_pyproject_toml,
|
workspace_pyproject_toml,
|
||||||
Some(current_project),
|
Some(current_project),
|
||||||
|
@ -1270,13 +1218,8 @@ impl VirtualProject {
|
||||||
|
|
||||||
if let Some(project) = pyproject_toml.project.as_ref() {
|
if let Some(project) = pyproject_toml.project.as_ref() {
|
||||||
// If the `pyproject.toml` contains a `[project]` table, it's a project.
|
// If the `pyproject.toml` contains a `[project]` table, it's a project.
|
||||||
let project = ProjectWorkspace::from_project(
|
let project =
|
||||||
project_root,
|
ProjectWorkspace::from_project(project_root, project, &pyproject_toml, options)
|
||||||
Path::new(""),
|
|
||||||
project,
|
|
||||||
&pyproject_toml,
|
|
||||||
options,
|
|
||||||
)
|
|
||||||
.await?;
|
.await?;
|
||||||
Ok(Self::Project(project))
|
Ok(Self::Project(project))
|
||||||
} else if let Some(workspace) = pyproject_toml
|
} else if let Some(workspace) = pyproject_toml
|
||||||
|
@ -1294,7 +1237,6 @@ impl VirtualProject {
|
||||||
|
|
||||||
let workspace = Workspace::collect_members(
|
let workspace = Workspace::collect_members(
|
||||||
project_path,
|
project_path,
|
||||||
PathBuf::new(),
|
|
||||||
workspace.clone(),
|
workspace.clone(),
|
||||||
pyproject_toml,
|
pyproject_toml,
|
||||||
None,
|
None,
|
||||||
|
@ -1452,7 +1394,6 @@ mod tests {
|
||||||
"project_name": "bird-feeder",
|
"project_name": "bird-feeder",
|
||||||
"workspace": {
|
"workspace": {
|
||||||
"install_path": "[ROOT]/albatross-in-example/examples/bird-feeder",
|
"install_path": "[ROOT]/albatross-in-example/examples/bird-feeder",
|
||||||
"lock_path": "",
|
|
||||||
"packages": {
|
"packages": {
|
||||||
"bird-feeder": {
|
"bird-feeder": {
|
||||||
"root": "[ROOT]/albatross-in-example/examples/bird-feeder",
|
"root": "[ROOT]/albatross-in-example/examples/bird-feeder",
|
||||||
|
@ -1496,7 +1437,6 @@ mod tests {
|
||||||
"project_name": "bird-feeder",
|
"project_name": "bird-feeder",
|
||||||
"workspace": {
|
"workspace": {
|
||||||
"install_path": "[ROOT]/albatross-project-in-excluded/excluded/bird-feeder",
|
"install_path": "[ROOT]/albatross-project-in-excluded/excluded/bird-feeder",
|
||||||
"lock_path": "",
|
|
||||||
"packages": {
|
"packages": {
|
||||||
"bird-feeder": {
|
"bird-feeder": {
|
||||||
"root": "[ROOT]/albatross-project-in-excluded/excluded/bird-feeder",
|
"root": "[ROOT]/albatross-project-in-excluded/excluded/bird-feeder",
|
||||||
|
@ -1539,7 +1479,6 @@ mod tests {
|
||||||
"project_name": "albatross",
|
"project_name": "albatross",
|
||||||
"workspace": {
|
"workspace": {
|
||||||
"install_path": "[ROOT]/albatross-root-workspace",
|
"install_path": "[ROOT]/albatross-root-workspace",
|
||||||
"lock_path": "",
|
|
||||||
"packages": {
|
"packages": {
|
||||||
"albatross": {
|
"albatross": {
|
||||||
"root": "[ROOT]/albatross-root-workspace",
|
"root": "[ROOT]/albatross-root-workspace",
|
||||||
|
@ -1624,7 +1563,6 @@ mod tests {
|
||||||
"project_name": "albatross",
|
"project_name": "albatross",
|
||||||
"workspace": {
|
"workspace": {
|
||||||
"install_path": "[ROOT]/albatross-virtual-workspace",
|
"install_path": "[ROOT]/albatross-virtual-workspace",
|
||||||
"lock_path": "../..",
|
|
||||||
"packages": {
|
"packages": {
|
||||||
"albatross": {
|
"albatross": {
|
||||||
"root": "[ROOT]/albatross-virtual-workspace/packages/albatross",
|
"root": "[ROOT]/albatross-virtual-workspace/packages/albatross",
|
||||||
|
@ -1696,7 +1634,6 @@ mod tests {
|
||||||
"project_name": "albatross",
|
"project_name": "albatross",
|
||||||
"workspace": {
|
"workspace": {
|
||||||
"install_path": "[ROOT]/albatross-just-project",
|
"install_path": "[ROOT]/albatross-just-project",
|
||||||
"lock_path": "",
|
|
||||||
"packages": {
|
"packages": {
|
||||||
"albatross": {
|
"albatross": {
|
||||||
"root": "[ROOT]/albatross-just-project",
|
"root": "[ROOT]/albatross-just-project",
|
||||||
|
|
|
@ -67,7 +67,6 @@ pub(crate) async fn lock(
|
||||||
frozen: bool,
|
frozen: bool,
|
||||||
python: Option<String>,
|
python: Option<String>,
|
||||||
settings: ResolverSettings,
|
settings: ResolverSettings,
|
||||||
|
|
||||||
python_preference: PythonPreference,
|
python_preference: PythonPreference,
|
||||||
python_downloads: PythonDownloads,
|
python_downloads: PythonDownloads,
|
||||||
connectivity: Connectivity,
|
connectivity: Connectivity,
|
||||||
|
@ -523,14 +522,12 @@ async fn do_lock(
|
||||||
// Notify the user of any resolution diagnostics.
|
// Notify the user of any resolution diagnostics.
|
||||||
pip::operations::diagnose_resolution(resolution.diagnostics(), printer)?;
|
pip::operations::diagnose_resolution(resolution.diagnostics(), printer)?;
|
||||||
|
|
||||||
|
let manifest = ResolverManifest::new(members, requirements, constraints, overrides)
|
||||||
|
.relative_to(workspace)?;
|
||||||
|
|
||||||
let previous = existing_lock.map(ValidatedLock::into_lock);
|
let previous = existing_lock.map(ValidatedLock::into_lock);
|
||||||
let lock = Lock::from_resolution_graph(&resolution)?
|
let lock = Lock::from_resolution_graph(&resolution, workspace.install_path())?
|
||||||
.with_manifest(ResolverManifest::new(
|
.with_manifest(manifest)
|
||||||
members,
|
|
||||||
requirements,
|
|
||||||
constraints,
|
|
||||||
overrides,
|
|
||||||
))
|
|
||||||
.with_supported_environments(
|
.with_supported_environments(
|
||||||
environments
|
environments
|
||||||
.cloned()
|
.cloned()
|
||||||
|
|
|
@ -276,14 +276,14 @@ fn root_package_splits_transitive_too() -> Result<()> {
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "b1", marker = "python_full_version < '3.12'", directory = "../b1" },
|
{ name = "b1", marker = "python_full_version < '3.12'", directory = "b1" },
|
||||||
{ name = "b2", marker = "python_full_version >= '3.12'", directory = "../b2" },
|
{ name = "b2", marker = "python_full_version >= '3.12'", directory = "b2" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "b1"
|
name = "b1"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = { directory = "../b1" }
|
source = { directory = "b1" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "iniconfig", version = "1.1.1", source = { url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl" }, marker = "python_full_version < '3.12'" },
|
{ name = "iniconfig", version = "1.1.1", source = { url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl" }, marker = "python_full_version < '3.12'" },
|
||||||
]
|
]
|
||||||
|
@ -294,7 +294,7 @@ fn root_package_splits_transitive_too() -> Result<()> {
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "b2"
|
name = "b2"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = { directory = "../b2" }
|
source = { directory = "b2" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "iniconfig", version = "2.0.0", source = { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl" }, marker = "python_full_version >= '3.12'" },
|
{ name = "iniconfig", version = "2.0.0", source = { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl" }, marker = "python_full_version >= '3.12'" },
|
||||||
]
|
]
|
||||||
|
@ -804,12 +804,12 @@ fn dont_pre_visit_url_packages() -> Result<()> {
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [{ name = "c", directory = "../c" }]
|
requires-dist = [{ name = "c", directory = "c" }]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "c"
|
name = "c"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = { directory = "../c" }
|
source = { directory = "c" }
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -4159,7 +4159,7 @@ fn lock_relative_and_absolute_paths() -> Result<()> {
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "b", directory = "b" },
|
{ name = "b", directory = "b" },
|
||||||
{ name = "c", directory = "[TEMP_DIR]/c" },
|
{ name = "c", directory = "c" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -4170,7 +4170,7 @@ fn lock_relative_and_absolute_paths() -> Result<()> {
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "c"
|
name = "c"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = { directory = "[TEMP_DIR]/c" }
|
source = { directory = "c" }
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -5074,7 +5074,7 @@ fn lock_same_version_multiple_urls() -> Result<()> {
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dependency"
|
name = "dependency"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = { directory = "[TEMP_DIR]/v1" }
|
source = { directory = "v1" }
|
||||||
resolution-markers = [
|
resolution-markers = [
|
||||||
"sys_platform == 'darwin'",
|
"sys_platform == 'darwin'",
|
||||||
]
|
]
|
||||||
|
@ -5088,7 +5088,7 @@ fn lock_same_version_multiple_urls() -> Result<()> {
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dependency"
|
name = "dependency"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = { directory = "[TEMP_DIR]/v2" }
|
source = { directory = "v2" }
|
||||||
resolution-markers = [
|
resolution-markers = [
|
||||||
"sys_platform != 'darwin'",
|
"sys_platform != 'darwin'",
|
||||||
]
|
]
|
||||||
|
@ -5113,14 +5113,14 @@ fn lock_same_version_multiple_urls() -> Result<()> {
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "dependency", version = "0.0.1", source = { directory = "[TEMP_DIR]/v1" }, marker = "sys_platform == 'darwin'" },
|
{ name = "dependency", version = "0.0.1", source = { directory = "v1" }, marker = "sys_platform == 'darwin'" },
|
||||||
{ name = "dependency", version = "0.0.1", source = { directory = "[TEMP_DIR]/v2" }, marker = "sys_platform != 'darwin'" },
|
{ name = "dependency", version = "0.0.1", source = { directory = "v2" }, marker = "sys_platform != 'darwin'" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "dependency", marker = "sys_platform != 'darwin'", directory = "[TEMP_DIR]/v2" },
|
{ name = "dependency", marker = "sys_platform != 'darwin'", directory = "v2" },
|
||||||
{ name = "dependency", marker = "sys_platform == 'darwin'", directory = "[TEMP_DIR]/v1" },
|
{ name = "dependency", marker = "sys_platform == 'darwin'", directory = "v1" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -7118,7 +7118,7 @@ fn lock_sources_archive() -> Result<()> {
|
||||||
Url::from_file_path(&workspace_archive).unwrap(),
|
Url::from_file_path(&workspace_archive).unwrap(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
uv_snapshot!(context.filters(), context.lock(), @r###"
|
uv_snapshot!( context.lock(), @r###"
|
||||||
success: true
|
success: true
|
||||||
exit_code: 0
|
exit_code: 0
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
@ -7129,9 +7129,9 @@ fn lock_sources_archive() -> Result<()> {
|
||||||
|
|
||||||
let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock")).unwrap();
|
let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock")).unwrap();
|
||||||
|
|
||||||
insta::with_settings!({
|
// insta::with_settings!({
|
||||||
filters => context.filters(),
|
// filters => context.filters(),
|
||||||
}, {
|
// }, {
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
lock, @r###"
|
lock, @r###"
|
||||||
version = 1
|
version = 1
|
||||||
|
@ -7171,7 +7171,7 @@ fn lock_sources_archive() -> Result<()> {
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [{ name = "workspace", path = "[TEMP_DIR]/workspace.zip" }]
|
requires-dist = [{ name = "workspace", path = "workspace.zip" }]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sniffio"
|
name = "sniffio"
|
||||||
|
@ -7185,7 +7185,7 @@ fn lock_sources_archive() -> Result<()> {
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "workspace"
|
name = "workspace"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = { path = "[TEMP_DIR]/workspace.zip" }
|
source = { path = "workspace.zip" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "anyio" },
|
{ name = "anyio" },
|
||||||
]
|
]
|
||||||
|
@ -7194,7 +7194,7 @@ fn lock_sources_archive() -> Result<()> {
|
||||||
requires-dist = [{ name = "anyio" }]
|
requires-dist = [{ name = "anyio" }]
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
});
|
// });
|
||||||
|
|
||||||
// Re-run with `--locked`.
|
// Re-run with `--locked`.
|
||||||
uv_snapshot!(context.filters(), context.lock().arg("--locked"), @r###"
|
uv_snapshot!(context.filters(), context.lock().arg("--locked"), @r###"
|
||||||
|
@ -7293,7 +7293,7 @@ fn lock_sources_source_tree() -> Result<()> {
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyio"
|
name = "anyio"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = { editable = "[TEMP_DIR]/workspace/anyio" }
|
source = { editable = "workspace/anyio" }
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "project"
|
name = "project"
|
||||||
|
@ -7304,18 +7304,18 @@ fn lock_sources_source_tree() -> Result<()> {
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [{ name = "workspace", directory = "[TEMP_DIR]/workspace" }]
|
requires-dist = [{ name = "workspace", directory = "workspace" }]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "workspace"
|
name = "workspace"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = { directory = "[TEMP_DIR]/workspace" }
|
source = { directory = "workspace" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "anyio" },
|
{ name = "anyio" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [{ name = "anyio", editable = "[TEMP_DIR]/workspace/anyio" }]
|
requires-dist = [{ name = "anyio", editable = "workspace/anyio" }]
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1497,3 +1497,61 @@ fn workspace_member_name_shadows_dependencies() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test that path dependencies with path dependencies resolve paths correctly across workspaces.
|
||||||
|
///
|
||||||
|
/// Each package is its own workspace. We put the other projects into a separate directory `libs` so
|
||||||
|
/// the paths don't line up by accident.
|
||||||
|
#[test]
|
||||||
|
fn test_path_hopping() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
// Build the main project ...
|
||||||
|
let deps = indoc! {r#"
|
||||||
|
dependencies = ["foo"]
|
||||||
|
[tool.uv.sources]
|
||||||
|
foo = { path = "../libs/foo", editable = true }
|
||||||
|
"#};
|
||||||
|
let main_project_dir = context.temp_dir.join("project");
|
||||||
|
make_project(&main_project_dir, "project", deps)?;
|
||||||
|
|
||||||
|
// ... that depends on foo ...
|
||||||
|
let deps = indoc! {r#"
|
||||||
|
dependencies = ["bar"]
|
||||||
|
[tool.uv.sources]
|
||||||
|
bar = { path = "../../libs/bar", editable = true }
|
||||||
|
"#};
|
||||||
|
make_project(&context.temp_dir.join("libs").join("foo"), "foo", deps)?;
|
||||||
|
|
||||||
|
// ... that depends on bar, a stub project.
|
||||||
|
make_project(&context.temp_dir.join("libs").join("bar"), "bar", "")?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.lock().arg("--preview").current_dir(&main_project_dir), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||||
|
Resolved 3 packages in [TIME]
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
|
||||||
|
let lock: SourceLock =
|
||||||
|
toml::from_str(&fs_err::read_to_string(main_project_dir.join("uv.lock"))?)?;
|
||||||
|
assert_json_snapshot!(lock.sources(), @r###"
|
||||||
|
{
|
||||||
|
"bar": {
|
||||||
|
"editable": "../libs/bar"
|
||||||
|
},
|
||||||
|
"foo": {
|
||||||
|
"editable": "../libs/foo"
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"editable": "."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue