mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-01 12:24:15 +00:00
Support locking relative paths (#4205)
By splitting `path` into a lockable, relative (or absolute) and an absolute installable path and by splitting between urls and paths by dist type, we can store relative paths in the lockfile.
This commit is contained in:
parent
33cf47182f
commit
44833801b3
28 changed files with 533 additions and 364 deletions
|
|
@ -158,7 +158,7 @@ impl<'a> From<&'a PathSourceDist> for PathSourceUrl<'a> {
|
||||||
fn from(dist: &'a PathSourceDist) -> Self {
|
fn from(dist: &'a PathSourceDist) -> Self {
|
||||||
Self {
|
Self {
|
||||||
url: &dist.url,
|
url: &dist.url,
|
||||||
path: Cow::Borrowed(&dist.path),
|
path: Cow::Borrowed(&dist.install_path),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -180,7 +180,7 @@ impl<'a> From<&'a DirectorySourceDist> for DirectorySourceUrl<'a> {
|
||||||
fn from(dist: &'a DirectorySourceDist) -> Self {
|
fn from(dist: &'a DirectorySourceDist) -> Self {
|
||||||
Self {
|
Self {
|
||||||
url: &dist.url,
|
url: &dist.url,
|
||||||
path: Cow::Borrowed(&dist.path),
|
path: Cow::Borrowed(&dist.install_path),
|
||||||
editable: dist.editable,
|
editable: dist.editable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -116,12 +116,14 @@ impl CachedDist {
|
||||||
Self::Url(dist) => {
|
Self::Url(dist) => {
|
||||||
if dist.editable {
|
if dist.editable {
|
||||||
assert_eq!(dist.url.scheme(), "file", "{}", dist.url);
|
assert_eq!(dist.url.scheme(), "file", "{}", dist.url);
|
||||||
|
let path = dist
|
||||||
|
.url
|
||||||
|
.to_file_path()
|
||||||
|
.map_err(|()| anyhow!("Invalid path in file URL"))?;
|
||||||
Ok(Some(ParsedUrl::Path(ParsedPathUrl {
|
Ok(Some(ParsedUrl::Path(ParsedPathUrl {
|
||||||
url: dist.url.raw().clone(),
|
url: dist.url.raw().clone(),
|
||||||
path: dist
|
install_path: path.clone(),
|
||||||
.url
|
lock_path: path,
|
||||||
.to_file_path()
|
|
||||||
.map_err(|()| anyhow!("Invalid path in file URL"))?,
|
|
||||||
editable: dist.editable,
|
editable: dist.editable,
|
||||||
})))
|
})))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -259,8 +259,12 @@ pub struct GitSourceDist {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PathSourceDist {
|
pub struct PathSourceDist {
|
||||||
pub name: PackageName,
|
pub name: PackageName,
|
||||||
/// The path to the archive.
|
/// The resolved, absolute path to the distribution which we use for installing.
|
||||||
pub 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 URL as it was provided by the user.
|
/// The URL as it was provided by the user.
|
||||||
pub url: VerbatimUrl,
|
pub url: VerbatimUrl,
|
||||||
}
|
}
|
||||||
|
|
@ -269,8 +273,12 @@ pub struct PathSourceDist {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DirectorySourceDist {
|
pub struct DirectorySourceDist {
|
||||||
pub name: PackageName,
|
pub name: PackageName,
|
||||||
/// The path to the directory.
|
/// The resolved, absolute path to the distribution which we use for installing.
|
||||||
pub 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.
|
||||||
|
|
@ -319,11 +327,12 @@ impl Dist {
|
||||||
pub fn from_file_url(
|
pub fn from_file_url(
|
||||||
name: PackageName,
|
name: PackageName,
|
||||||
url: VerbatimUrl,
|
url: VerbatimUrl,
|
||||||
path: &Path,
|
install_path: &Path,
|
||||||
|
lock_path: &Path,
|
||||||
editable: bool,
|
editable: bool,
|
||||||
) -> Result<Dist, Error> {
|
) -> Result<Dist, Error> {
|
||||||
// Store the canonicalized path, which also serves to validate that it exists.
|
// Store the canonicalized path, which also serves to validate that it exists.
|
||||||
let path = match path.canonicalize() {
|
let canonicalized_path = match install_path.canonicalize() {
|
||||||
Ok(path) => path,
|
Ok(path) => path,
|
||||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||||
return Err(Error::NotFound(url.to_url()));
|
return Err(Error::NotFound(url.to_url()));
|
||||||
|
|
@ -332,14 +341,15 @@ impl Dist {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Determine whether the path represents an archive or a directory.
|
// Determine whether the path represents an archive or a directory.
|
||||||
if path.is_dir() {
|
if canonicalized_path.is_dir() {
|
||||||
Ok(Self::Source(SourceDist::Directory(DirectorySourceDist {
|
Ok(Self::Source(SourceDist::Directory(DirectorySourceDist {
|
||||||
name,
|
name,
|
||||||
path,
|
install_path: canonicalized_path.clone(),
|
||||||
|
lock_path: lock_path.to_path_buf(),
|
||||||
editable,
|
editable,
|
||||||
url,
|
url,
|
||||||
})))
|
})))
|
||||||
} else if path
|
} else if canonicalized_path
|
||||||
.extension()
|
.extension()
|
||||||
.is_some_and(|ext| ext.eq_ignore_ascii_case("whl"))
|
.is_some_and(|ext| ext.eq_ignore_ascii_case("whl"))
|
||||||
{
|
{
|
||||||
|
|
@ -359,7 +369,7 @@ impl Dist {
|
||||||
|
|
||||||
Ok(Self::Built(BuiltDist::Path(PathBuiltDist {
|
Ok(Self::Built(BuiltDist::Path(PathBuiltDist {
|
||||||
filename,
|
filename,
|
||||||
path,
|
path: canonicalized_path,
|
||||||
url,
|
url,
|
||||||
})))
|
})))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -369,7 +379,8 @@ impl Dist {
|
||||||
|
|
||||||
Ok(Self::Source(SourceDist::Path(PathSourceDist {
|
Ok(Self::Source(SourceDist::Path(PathSourceDist {
|
||||||
name,
|
name,
|
||||||
path,
|
install_path: canonicalized_path.clone(),
|
||||||
|
lock_path: canonicalized_path,
|
||||||
url,
|
url,
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
@ -396,9 +407,13 @@ impl Dist {
|
||||||
ParsedUrl::Archive(archive) => {
|
ParsedUrl::Archive(archive) => {
|
||||||
Self::from_http_url(name, url.verbatim, archive.url, archive.subdirectory)
|
Self::from_http_url(name, url.verbatim, archive.url, archive.subdirectory)
|
||||||
}
|
}
|
||||||
ParsedUrl::Path(file) => {
|
ParsedUrl::Path(file) => Self::from_file_url(
|
||||||
Self::from_file_url(name, url.verbatim, &file.path, file.editable)
|
name,
|
||||||
}
|
url.verbatim,
|
||||||
|
&file.install_path,
|
||||||
|
&file.lock_path,
|
||||||
|
file.editable,
|
||||||
|
),
|
||||||
ParsedUrl::Git(git) => {
|
ParsedUrl::Git(git) => {
|
||||||
Self::from_git_url(name, url.verbatim, git.url, git.subdirectory)
|
Self::from_git_url(name, url.verbatim, git.url, git.subdirectory)
|
||||||
}
|
}
|
||||||
|
|
@ -517,8 +532,8 @@ impl SourceDist {
|
||||||
/// Returns the path to the source distribution, if it's a local distribution.
|
/// Returns the path to the source distribution, if it's a local distribution.
|
||||||
pub fn as_path(&self) -> Option<&Path> {
|
pub fn as_path(&self) -> Option<&Path> {
|
||||||
match self {
|
match self {
|
||||||
Self::Path(dist) => Some(&dist.path),
|
Self::Path(dist) => Some(&dist.install_path),
|
||||||
Self::Directory(dist) => Some(&dist.path),
|
Self::Directory(dist) => Some(&dist.install_path),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,8 @@ impl From<&ResolvedDist> for Requirement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Dist::Built(BuiltDist::Path(wheel)) => RequirementSource::Path {
|
Dist::Built(BuiltDist::Path(wheel)) => RequirementSource::Path {
|
||||||
path: wheel.path.clone(),
|
install_path: wheel.path.clone(),
|
||||||
|
lock_path: wheel.path.clone(),
|
||||||
url: wheel.url.clone(),
|
url: wheel.url.clone(),
|
||||||
editable: false,
|
editable: false,
|
||||||
},
|
},
|
||||||
|
|
@ -168,12 +169,14 @@ impl From<&ResolvedDist> for Requirement {
|
||||||
subdirectory: sdist.subdirectory.clone(),
|
subdirectory: sdist.subdirectory.clone(),
|
||||||
},
|
},
|
||||||
Dist::Source(SourceDist::Path(sdist)) => RequirementSource::Path {
|
Dist::Source(SourceDist::Path(sdist)) => RequirementSource::Path {
|
||||||
path: sdist.path.clone(),
|
install_path: sdist.install_path.clone(),
|
||||||
|
lock_path: sdist.lock_path.clone(),
|
||||||
url: sdist.url.clone(),
|
url: sdist.url.clone(),
|
||||||
editable: false,
|
editable: false,
|
||||||
},
|
},
|
||||||
Dist::Source(SourceDist::Directory(sdist)) => RequirementSource::Path {
|
Dist::Source(SourceDist::Directory(sdist)) => RequirementSource::Path {
|
||||||
path: sdist.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,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,9 @@ impl UnnamedRequirementUrl for VerbatimParsedUrl {
|
||||||
) -> Result<Self, Self::Err> {
|
) -> Result<Self, Self::Err> {
|
||||||
let verbatim = VerbatimUrl::parse_path(&path, &working_dir)?;
|
let verbatim = VerbatimUrl::parse_path(&path, &working_dir)?;
|
||||||
let parsed_path_url = ParsedPathUrl {
|
let parsed_path_url = ParsedPathUrl {
|
||||||
path: verbatim.as_path()?,
|
|
||||||
url: verbatim.to_url(),
|
url: verbatim.to_url(),
|
||||||
|
install_path: verbatim.as_path()?,
|
||||||
|
lock_path: path.as_ref().to_path_buf(),
|
||||||
editable: false,
|
editable: false,
|
||||||
};
|
};
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
@ -72,8 +73,9 @@ impl UnnamedRequirementUrl for VerbatimParsedUrl {
|
||||||
fn parse_absolute_path(path: impl AsRef<Path>) -> Result<Self, Self::Err> {
|
fn parse_absolute_path(path: impl AsRef<Path>) -> Result<Self, Self::Err> {
|
||||||
let verbatim = VerbatimUrl::parse_absolute_path(&path)?;
|
let verbatim = VerbatimUrl::parse_absolute_path(&path)?;
|
||||||
let parsed_path_url = ParsedPathUrl {
|
let parsed_path_url = ParsedPathUrl {
|
||||||
path: verbatim.as_path()?,
|
|
||||||
url: verbatim.to_url(),
|
url: verbatim.to_url(),
|
||||||
|
install_path: verbatim.as_path()?,
|
||||||
|
lock_path: path.as_ref().to_path_buf(),
|
||||||
editable: false,
|
editable: false,
|
||||||
};
|
};
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
@ -171,16 +173,27 @@ impl ParsedUrl {
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Hash, Ord)]
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Hash, Ord)]
|
||||||
pub struct ParsedPathUrl {
|
pub struct ParsedPathUrl {
|
||||||
pub url: Url,
|
pub url: Url,
|
||||||
pub path: PathBuf,
|
/// The resolved, absolute path to the distribution which we use for installing.
|
||||||
|
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 ParsedPathUrl {
|
impl ParsedPathUrl {
|
||||||
/// Construct a [`ParsedPathUrl`] from a path requirement source.
|
/// Construct a [`ParsedPathUrl`] from a path requirement source.
|
||||||
pub fn from_source(path: PathBuf, editable: bool, url: Url) -> Self {
|
pub fn from_source(
|
||||||
|
install_path: PathBuf,
|
||||||
|
lock_path: PathBuf,
|
||||||
|
editable: bool,
|
||||||
|
url: Url,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
url,
|
url,
|
||||||
path,
|
install_path,
|
||||||
|
lock_path,
|
||||||
editable,
|
editable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -311,7 +324,8 @@ impl TryFrom<Url> for ParsedUrl {
|
||||||
.map_err(|()| ParsedUrlError::InvalidFileUrl(url.clone()))?;
|
.map_err(|()| ParsedUrlError::InvalidFileUrl(url.clone()))?;
|
||||||
Ok(Self::Path(ParsedPathUrl {
|
Ok(Self::Path(ParsedPathUrl {
|
||||||
url,
|
url,
|
||||||
path,
|
install_path: path.clone(),
|
||||||
|
lock_path: path,
|
||||||
editable: false,
|
editable: false,
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,12 @@ pub enum RequirementSource {
|
||||||
/// `.tag.gz` file) or a source tree (a directory with a pyproject.toml in, or a legacy
|
/// `.tag.gz` file) or a source tree (a directory with a pyproject.toml in, or a legacy
|
||||||
/// source distribution with only a setup.py but non pyproject.toml in it).
|
/// source distribution with only a setup.py but non pyproject.toml in it).
|
||||||
Path {
|
Path {
|
||||||
path: PathBuf,
|
/// The resolved, absolute path to the distribution which we use for installing.
|
||||||
|
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
|
||||||
|
|
@ -185,7 +190,8 @@ impl RequirementSource {
|
||||||
pub fn from_parsed_url(parsed_url: ParsedUrl, url: VerbatimUrl) -> Self {
|
pub fn from_parsed_url(parsed_url: ParsedUrl, url: VerbatimUrl) -> Self {
|
||||||
match parsed_url {
|
match parsed_url {
|
||||||
ParsedUrl::Path(local_file) => RequirementSource::Path {
|
ParsedUrl::Path(local_file) => RequirementSource::Path {
|
||||||
path: local_file.path,
|
install_path: local_file.install_path.clone(),
|
||||||
|
lock_path: local_file.lock_path.clone(),
|
||||||
url,
|
url,
|
||||||
editable: local_file.editable,
|
editable: local_file.editable,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1843,7 +1843,8 @@ mod test {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
path: "/foo/bar",
|
install_path: "/foo/bar",
|
||||||
|
lock_path: "/foo/bar",
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
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,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -70,7 +71,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
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,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -123,7 +125,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
path: "/scripts/packages/black_editable",
|
install_path: "/scripts/packages/black_editable",
|
||||||
|
lock_path: "/scripts/packages/black_editable",
|
||||||
editable: false,
|
editable: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
|
lock_path: "./editable",
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -79,7 +80,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
|
lock_path: "./editable",
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -135,7 +137,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
|
lock_path: "./editable",
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -212,7 +215,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
|
lock_path: "./editable",
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -289,7 +293,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
|
lock_path: "./editable",
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -359,7 +364,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
path: "<REQUIREMENTS_DIR>/editable[d",
|
install_path: "<REQUIREMENTS_DIR>/editable[d",
|
||||||
|
lock_path: "./editable[d",
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
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,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -70,7 +71,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
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,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -123,7 +125,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
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,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
|
lock_path: "./editable",
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -79,7 +80,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
|
lock_path: "./editable",
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -135,7 +137,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
|
lock_path: "./editable",
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -212,7 +215,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
|
lock_path: "./editable",
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -289,7 +293,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
path: "<REQUIREMENTS_DIR>/editable",
|
install_path: "<REQUIREMENTS_DIR>/editable",
|
||||||
|
lock_path: "./editable",
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -359,7 +364,8 @@ RequirementsTxt {
|
||||||
query: None,
|
query: None,
|
||||||
fragment: None,
|
fragment: None,
|
||||||
},
|
},
|
||||||
path: "<REQUIREMENTS_DIR>/editable[d",
|
install_path: "<REQUIREMENTS_DIR>/editable[d",
|
||||||
|
lock_path: "./editable[d",
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,8 @@ impl<'a> BuiltWheelIndex<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Determine the last-modified time of the source distribution.
|
// Determine the last-modified time of the source distribution.
|
||||||
let modified = ArchiveTimestamp::from_file(&source_dist.path).map_err(Error::CacheRead)?;
|
let modified =
|
||||||
|
ArchiveTimestamp::from_file(&source_dist.install_path).map_err(Error::CacheRead)?;
|
||||||
|
|
||||||
// If the distribution is stale, omit it from the index.
|
// If the distribution is stale, omit it from the index.
|
||||||
if !pointer.is_up_to_date(modified) {
|
if !pointer.is_up_to_date(modified) {
|
||||||
|
|
@ -106,10 +107,12 @@ impl<'a> BuiltWheelIndex<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Determine the last-modified time of the source distribution.
|
// Determine the last-modified time of the source distribution.
|
||||||
let Some(modified) =
|
let Some(modified) = ArchiveTimestamp::from_source_tree(&source_dist.install_path)
|
||||||
ArchiveTimestamp::from_source_tree(&source_dist.path).map_err(Error::CacheRead)?
|
.map_err(Error::CacheRead)?
|
||||||
else {
|
else {
|
||||||
return Err(Error::DirWithoutEntrypoint(source_dist.path.clone()));
|
return Err(Error::DirWithoutEntrypoint(
|
||||||
|
source_dist.install_path.clone(),
|
||||||
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the distribution is stale, omit it from the index.
|
// If the distribution is stale, omit it from the index.
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,12 @@ pub(crate) fn lower_requirement(
|
||||||
if matches!(requirement.version_or_url, Some(VersionOrUrl::Url(_))) {
|
if matches!(requirement.version_or_url, Some(VersionOrUrl::Url(_))) {
|
||||||
return Err(LoweringError::ConflictingUrls);
|
return Err(LoweringError::ConflictingUrls);
|
||||||
}
|
}
|
||||||
path_source(path, project_dir, editable.unwrap_or(false))?
|
path_source(
|
||||||
|
path,
|
||||||
|
project_dir,
|
||||||
|
workspace.root(),
|
||||||
|
editable.unwrap_or(false),
|
||||||
|
)?
|
||||||
}
|
}
|
||||||
Source::Registry { index } => match requirement.version_or_url {
|
Source::Registry { index } => match requirement.version_or_url {
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -199,7 +204,12 @@ pub(crate) fn lower_requirement(
|
||||||
.get(&requirement.name)
|
.get(&requirement.name)
|
||||||
.ok_or(LoweringError::UndeclaredWorkspacePackage)?
|
.ok_or(LoweringError::UndeclaredWorkspacePackage)?
|
||||||
.clone();
|
.clone();
|
||||||
path_source(path.root(), workspace.root(), editable.unwrap_or(true))?
|
path_source(
|
||||||
|
path.root(),
|
||||||
|
workspace.root(),
|
||||||
|
workspace.root(),
|
||||||
|
editable.unwrap_or(true),
|
||||||
|
)?
|
||||||
}
|
}
|
||||||
Source::CatchAll { .. } => {
|
Source::CatchAll { .. } => {
|
||||||
// Emit a dedicated error message, which is an improvement over Serde's default error.
|
// Emit a dedicated error message, which is an improvement over Serde's default error.
|
||||||
|
|
@ -219,6 +229,7 @@ pub(crate) fn lower_requirement(
|
||||||
fn path_source(
|
fn path_source(
|
||||||
path: impl AsRef<Path>,
|
path: impl AsRef<Path>,
|
||||||
project_dir: &Path,
|
project_dir: &Path,
|
||||||
|
workspace_root: &Path,
|
||||||
editable: bool,
|
editable: bool,
|
||||||
) -> Result<RequirementSource, LoweringError> {
|
) -> Result<RequirementSource, LoweringError> {
|
||||||
let url = VerbatimUrl::parse_path(path.as_ref(), project_dir)?
|
let url = VerbatimUrl::parse_path(path.as_ref(), project_dir)?
|
||||||
|
|
@ -228,8 +239,12 @@ fn path_source(
|
||||||
.absolutize_from(project_dir)
|
.absolutize_from(project_dir)
|
||||||
.map_err(|err| LoweringError::Absolutize(path.as_ref().to_path_buf(), err))?
|
.map_err(|err| LoweringError::Absolutize(path.as_ref().to_path_buf(), err))?
|
||||||
.to_path_buf();
|
.to_path_buf();
|
||||||
|
let ascend_to_workspace = project_dir
|
||||||
|
.strip_prefix(workspace_root)
|
||||||
|
.expect("Project must be below workspace root");
|
||||||
Ok(RequirementSource::Path {
|
Ok(RequirementSource::Path {
|
||||||
path: path_buf,
|
install_path: path_buf,
|
||||||
|
lock_path: ascend_to_workspace.join(project_dir),
|
||||||
url,
|
url,
|
||||||
editable,
|
editable,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,12 @@ impl Workspace {
|
||||||
extras,
|
extras,
|
||||||
marker: None,
|
marker: None,
|
||||||
source: RequirementSource::Path {
|
source: RequirementSource::Path {
|
||||||
path: member.root.clone(),
|
install_path: member.root.clone(),
|
||||||
|
lock_path: member
|
||||||
|
.root
|
||||||
|
.strip_prefix(&self.root)
|
||||||
|
.expect("Project must be below workspace root")
|
||||||
|
.to_path_buf(),
|
||||||
editable: true,
|
editable: true,
|
||||||
url,
|
url,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -272,13 +272,14 @@ impl<'a> Planner<'a> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RequirementSource::Path { url, editable, .. } => {
|
RequirementSource::Path {
|
||||||
|
url,
|
||||||
|
editable,
|
||||||
|
install_path,
|
||||||
|
lock_path,
|
||||||
|
} => {
|
||||||
// Store the canonicalized path, which also serves to validate that it exists.
|
// Store the canonicalized path, which also serves to validate that it exists.
|
||||||
let path = match url
|
let path = match install_path.canonicalize() {
|
||||||
.to_file_path()
|
|
||||||
.map_err(|()| Error::MissingFilePath(url.to_url()))?
|
|
||||||
.canonicalize()
|
|
||||||
{
|
|
||||||
Ok(path) => path,
|
Ok(path) => path,
|
||||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||||
return Err(Error::NotFound(url.to_url()).into());
|
return Err(Error::NotFound(url.to_url()).into());
|
||||||
|
|
@ -291,7 +292,8 @@ impl<'a> Planner<'a> {
|
||||||
let sdist = DirectorySourceDist {
|
let sdist = DirectorySourceDist {
|
||||||
name: requirement.name.clone(),
|
name: requirement.name.clone(),
|
||||||
url: url.clone(),
|
url: url.clone(),
|
||||||
path,
|
install_path: path,
|
||||||
|
lock_path: lock_path.clone(),
|
||||||
editable: *editable,
|
editable: *editable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -369,7 +371,8 @@ impl<'a> Planner<'a> {
|
||||||
let sdist = PathSourceDist {
|
let sdist = PathSourceDist {
|
||||||
name: requirement.name.clone(),
|
name: requirement.name.clone(),
|
||||||
url: url.clone(),
|
url: url.clone(),
|
||||||
path,
|
install_path: path,
|
||||||
|
lock_path: lock_path.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Find the most-compatible wheel from the cache, since we don't know
|
// Find the most-compatible wheel from the cache, since we don't know
|
||||||
|
|
|
||||||
|
|
@ -149,9 +149,10 @@ impl RequirementSatisfaction {
|
||||||
Ok(Self::Satisfied)
|
Ok(Self::Satisfied)
|
||||||
}
|
}
|
||||||
RequirementSource::Path {
|
RequirementSource::Path {
|
||||||
url: _,
|
install_path: requested_path,
|
||||||
path: requested_path,
|
lock_path: _,
|
||||||
editable: requested_editable,
|
editable: requested_editable,
|
||||||
|
url: _,
|
||||||
} => {
|
} => {
|
||||||
let InstalledDist::Url(InstalledDirectUrlDist { direct_url, .. }) = &distribution
|
let InstalledDist::Url(InstalledDirectUrlDist { direct_url, .. }) = &distribution
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -254,9 +254,16 @@ fn required_dist(requirement: &Requirement) -> Result<Option<Dist>, distribution
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
RequirementSource::Path {
|
RequirementSource::Path {
|
||||||
path,
|
install_path,
|
||||||
|
lock_path,
|
||||||
url,
|
url,
|
||||||
editable,
|
editable,
|
||||||
} => Dist::from_file_url(requirement.name.clone(), url.clone(), path, *editable)?,
|
} => Dist::from_file_url(
|
||||||
|
requirement.name.clone(),
|
||||||
|
url.clone(),
|
||||||
|
install_path,
|
||||||
|
lock_path,
|
||||||
|
*editable,
|
||||||
|
)?,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,15 +139,15 @@ impl<'a, Context: BuildContext> NamedRequirementsResolver<'a, Context> {
|
||||||
|
|
||||||
let source = match &requirement.url.parsed_url {
|
let source = match &requirement.url.parsed_url {
|
||||||
// If the path points to a directory, attempt to read the name from static metadata.
|
// If the path points to a directory, attempt to read the name from static metadata.
|
||||||
ParsedUrl::Path(parsed_path_url) if parsed_path_url.path.is_dir() => {
|
ParsedUrl::Path(parsed_path_url) if parsed_path_url.install_path.is_dir() => {
|
||||||
// Attempt to read a `PKG-INFO` from the directory.
|
// Attempt to read a `PKG-INFO` from the directory.
|
||||||
if let Some(metadata) = fs_err::read(parsed_path_url.path.join("PKG-INFO"))
|
if let Some(metadata) = fs_err::read(parsed_path_url.install_path.join("PKG-INFO"))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|contents| Metadata10::parse_pkg_info(&contents).ok())
|
.and_then(|contents| Metadata10::parse_pkg_info(&contents).ok())
|
||||||
{
|
{
|
||||||
debug!(
|
debug!(
|
||||||
"Found PKG-INFO metadata for {path} ({name})",
|
"Found PKG-INFO metadata for {path} ({name})",
|
||||||
path = parsed_path_url.path.display(),
|
path = parsed_path_url.install_path.display(),
|
||||||
name = metadata.name
|
name = metadata.name
|
||||||
);
|
);
|
||||||
return Ok(pep508_rs::Requirement {
|
return Ok(pep508_rs::Requirement {
|
||||||
|
|
@ -160,7 +160,7 @@ impl<'a, Context: BuildContext> NamedRequirementsResolver<'a, Context> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to read a `pyproject.toml` file.
|
// Attempt to read a `pyproject.toml` file.
|
||||||
let project_path = parsed_path_url.path.join("pyproject.toml");
|
let project_path = parsed_path_url.install_path.join("pyproject.toml");
|
||||||
if let Some(pyproject) = fs_err::read_to_string(project_path)
|
if let Some(pyproject) = fs_err::read_to_string(project_path)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|contents| toml::from_str::<PyProjectToml>(&contents).ok())
|
.and_then(|contents| toml::from_str::<PyProjectToml>(&contents).ok())
|
||||||
|
|
@ -169,7 +169,7 @@ impl<'a, Context: BuildContext> NamedRequirementsResolver<'a, Context> {
|
||||||
if let Some(project) = pyproject.project {
|
if let Some(project) = pyproject.project {
|
||||||
debug!(
|
debug!(
|
||||||
"Found PEP 621 metadata for {path} in `pyproject.toml` ({name})",
|
"Found PEP 621 metadata for {path} in `pyproject.toml` ({name})",
|
||||||
path = parsed_path_url.path.display(),
|
path = parsed_path_url.install_path.display(),
|
||||||
name = project.name
|
name = project.name
|
||||||
);
|
);
|
||||||
return Ok(pep508_rs::Requirement {
|
return Ok(pep508_rs::Requirement {
|
||||||
|
|
@ -187,7 +187,7 @@ impl<'a, Context: BuildContext> NamedRequirementsResolver<'a, Context> {
|
||||||
if let Some(name) = poetry.name {
|
if let Some(name) = poetry.name {
|
||||||
debug!(
|
debug!(
|
||||||
"Found Poetry metadata for {path} in `pyproject.toml` ({name})",
|
"Found Poetry metadata for {path} in `pyproject.toml` ({name})",
|
||||||
path = parsed_path_url.path.display(),
|
path = parsed_path_url.install_path.display(),
|
||||||
name = name
|
name = name
|
||||||
);
|
);
|
||||||
return Ok(pep508_rs::Requirement {
|
return Ok(pep508_rs::Requirement {
|
||||||
|
|
@ -204,7 +204,7 @@ impl<'a, Context: BuildContext> NamedRequirementsResolver<'a, Context> {
|
||||||
|
|
||||||
// Attempt to read a `setup.cfg` from the directory.
|
// Attempt to read a `setup.cfg` from the directory.
|
||||||
if let Some(setup_cfg) =
|
if let Some(setup_cfg) =
|
||||||
fs_err::read_to_string(parsed_path_url.path.join("setup.cfg"))
|
fs_err::read_to_string(parsed_path_url.install_path.join("setup.cfg"))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|contents| {
|
.and_then(|contents| {
|
||||||
let mut ini = Ini::new_cs();
|
let mut ini = Ini::new_cs();
|
||||||
|
|
@ -217,7 +217,7 @@ impl<'a, Context: BuildContext> NamedRequirementsResolver<'a, Context> {
|
||||||
if let Ok(name) = PackageName::from_str(name) {
|
if let Ok(name) = PackageName::from_str(name) {
|
||||||
debug!(
|
debug!(
|
||||||
"Found setuptools metadata for {path} in `setup.cfg` ({name})",
|
"Found setuptools metadata for {path} in `setup.cfg` ({name})",
|
||||||
path = parsed_path_url.path.display(),
|
path = parsed_path_url.install_path.display(),
|
||||||
name = name
|
name = name
|
||||||
);
|
);
|
||||||
return Ok(pep508_rs::Requirement {
|
return Ok(pep508_rs::Requirement {
|
||||||
|
|
@ -234,14 +234,14 @@ impl<'a, Context: BuildContext> NamedRequirementsResolver<'a, Context> {
|
||||||
|
|
||||||
SourceUrl::Directory(DirectorySourceUrl {
|
SourceUrl::Directory(DirectorySourceUrl {
|
||||||
url: &requirement.url.verbatim,
|
url: &requirement.url.verbatim,
|
||||||
path: Cow::Borrowed(&parsed_path_url.path),
|
path: Cow::Borrowed(&parsed_path_url.install_path),
|
||||||
editable: parsed_path_url.editable,
|
editable: parsed_path_url.editable,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// If it's not a directory, assume it's a file.
|
// If it's not a directory, assume it's a file.
|
||||||
ParsedUrl::Path(parsed_path_url) => SourceUrl::Path(PathSourceUrl {
|
ParsedUrl::Path(parsed_path_url) => SourceUrl::Path(PathSourceUrl {
|
||||||
url: &requirement.url.verbatim,
|
url: &requirement.url.verbatim,
|
||||||
path: Cow::Borrowed(&parsed_path_url.path),
|
path: Cow::Borrowed(&parsed_path_url.install_path),
|
||||||
}),
|
}),
|
||||||
ParsedUrl::Archive(parsed_archive_url) => SourceUrl::Direct(DirectSourceUrl {
|
ParsedUrl::Archive(parsed_archive_url) => SourceUrl::Direct(DirectSourceUrl {
|
||||||
url: &parsed_archive_url.url,
|
url: &parsed_archive_url.url,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
// as we build out universal locking.
|
// as we build out universal locking.
|
||||||
#![allow(dead_code, unreachable_code, unused_variables)]
|
#![allow(dead_code, unreachable_code, unused_variables)]
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::collections::{BTreeMap, VecDeque};
|
use std::collections::{BTreeMap, VecDeque};
|
||||||
|
use std::fmt::{Debug, Display};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
|
@ -11,6 +13,7 @@ use either::Either;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use petgraph::visit::EdgeRef;
|
use petgraph::visit::EdgeRef;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
use serde::{Deserialize, Deserializer};
|
||||||
use toml_edit::{value, Array, ArrayOfTables, InlineTable, Item, Table, Value};
|
use toml_edit::{value, Array, ArrayOfTables, InlineTable, Item, Table, Value};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
|
@ -22,7 +25,7 @@ use distribution_types::{
|
||||||
RegistrySourceDist, RemoteSource, Resolution, ResolvedDist, ToUrlError,
|
RegistrySourceDist, RemoteSource, Resolution, ResolvedDist, ToUrlError,
|
||||||
};
|
};
|
||||||
use pep440_rs::Version;
|
use pep440_rs::Version;
|
||||||
use pep508_rs::{MarkerEnvironment, MarkerTree, VerbatimUrl};
|
use pep508_rs::{MarkerEnvironment, MarkerTree, VerbatimUrl, VerbatimUrlError};
|
||||||
use platform_tags::{TagCompatibility, TagPriority, Tags};
|
use platform_tags::{TagCompatibility, TagPriority, Tags};
|
||||||
use pypi_types::{HashDigest, ParsedArchiveUrl, ParsedGitUrl};
|
use pypi_types::{HashDigest, ParsedArchiveUrl, ParsedGitUrl};
|
||||||
use uv_configuration::ExtrasSpecification;
|
use uv_configuration::ExtrasSpecification;
|
||||||
|
|
@ -145,6 +148,7 @@ impl Lock {
|
||||||
/// Convert the [`Lock`] to a [`Resolution`] using the given marker environment, tags, and root.
|
/// Convert the [`Lock`] to a [`Resolution`] using the given marker environment, tags, and root.
|
||||||
pub fn to_resolution(
|
pub fn to_resolution(
|
||||||
&self,
|
&self,
|
||||||
|
workspace_root: &Path,
|
||||||
marker_env: &MarkerEnvironment,
|
marker_env: &MarkerEnvironment,
|
||||||
tags: &Tags,
|
tags: &Tags,
|
||||||
root_name: &PackageName,
|
root_name: &PackageName,
|
||||||
|
|
@ -202,7 +206,7 @@ impl Lock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let name = dist.id.name.clone();
|
let name = dist.id.name.clone();
|
||||||
let resolved_dist = ResolvedDist::Installable(dist.to_dist(tags)?);
|
let resolved_dist = ResolvedDist::Installable(dist.to_dist(workspace_root, tags)?);
|
||||||
map.insert(name, resolved_dist);
|
map.insert(name, resolved_dist);
|
||||||
}
|
}
|
||||||
let diagnostics = vec![];
|
let diagnostics = vec![];
|
||||||
|
|
@ -485,9 +489,9 @@ impl TryFrom<LockWire> for Lock {
|
||||||
|
|
||||||
// Also check that our sources are consistent with whether we have
|
// Also check that our sources are consistent with whether we have
|
||||||
// hashes or not.
|
// hashes or not.
|
||||||
let requires_hash = dist.id.source.kind.requires_hash();
|
let requires_hash = dist.id.source.requires_hash();
|
||||||
if let Some(ref sdist) = dist.sdist {
|
if let Some(ref sdist) = dist.sdist {
|
||||||
if requires_hash != sdist.hash.is_some() {
|
if requires_hash != sdist.hash().is_some() {
|
||||||
return Err(LockErrorKind::Hash {
|
return Err(LockErrorKind::Hash {
|
||||||
id: dist.id.clone(),
|
id: dist.id.clone(),
|
||||||
artifact_type: "source distribution",
|
artifact_type: "source distribution",
|
||||||
|
|
@ -581,14 +585,14 @@ impl Distribution {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the [`Distribution`] to a [`Dist`] that can be used in installation.
|
/// Convert the [`Distribution`] to a [`Dist`] that can be used in installation.
|
||||||
fn to_dist(&self, tags: &Tags) -> Result<Dist, LockError> {
|
fn to_dist(&self, workspace_root: &Path, tags: &Tags) -> Result<Dist, LockError> {
|
||||||
if let Some(best_wheel_index) = self.find_best_wheel(tags) {
|
if let Some(best_wheel_index) = self.find_best_wheel(tags) {
|
||||||
return match &self.id.source.kind {
|
return match &self.id.source {
|
||||||
SourceKind::Registry => {
|
Source::Registry(url) => {
|
||||||
let wheels = self
|
let wheels = self
|
||||||
.wheels
|
.wheels
|
||||||
.iter()
|
.iter()
|
||||||
.map(|wheel| wheel.to_registry_dist(&self.id.source))
|
.map(|wheel| wheel.to_registry_dist(url, &self.id.source))
|
||||||
.collect();
|
.collect();
|
||||||
let reg_built_dist = RegistryBuiltDist {
|
let reg_built_dist = RegistryBuiltDist {
|
||||||
wheels,
|
wheels,
|
||||||
|
|
@ -597,41 +601,46 @@ impl Distribution {
|
||||||
};
|
};
|
||||||
Ok(Dist::Built(BuiltDist::Registry(reg_built_dist)))
|
Ok(Dist::Built(BuiltDist::Registry(reg_built_dist)))
|
||||||
}
|
}
|
||||||
SourceKind::Path => {
|
Source::Path(path) => {
|
||||||
let filename: WheelFilename = self.wheels[best_wheel_index].filename.clone();
|
let filename: WheelFilename = self.wheels[best_wheel_index].filename.clone();
|
||||||
let path_dist = PathBuiltDist {
|
let path_dist = PathBuiltDist {
|
||||||
filename,
|
filename,
|
||||||
url: VerbatimUrl::from_url(self.id.source.url.clone()),
|
url: VerbatimUrl::from_path(workspace_root.join(path)).map_err(|err| {
|
||||||
path: self.id.source.url.to_file_path().unwrap(),
|
LockErrorKind::VerbatimUrl {
|
||||||
|
id: self.id.clone(),
|
||||||
|
err,
|
||||||
|
}
|
||||||
|
})?,
|
||||||
|
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))
|
||||||
}
|
}
|
||||||
SourceKind::Direct(direct) => {
|
Source::Direct(url, direct) => {
|
||||||
let filename: WheelFilename = self.wheels[best_wheel_index].filename.clone();
|
let filename: WheelFilename = self.wheels[best_wheel_index].filename.clone();
|
||||||
let url = Url::from(ParsedArchiveUrl {
|
let url = Url::from(ParsedArchiveUrl {
|
||||||
url: self.id.source.url.clone(),
|
url: url.clone(),
|
||||||
subdirectory: direct.subdirectory.as_ref().map(PathBuf::from),
|
subdirectory: direct.subdirectory.as_ref().map(PathBuf::from),
|
||||||
});
|
});
|
||||||
let direct_dist = DirectUrlBuiltDist {
|
let direct_dist = DirectUrlBuiltDist {
|
||||||
filename,
|
filename,
|
||||||
location: self.id.source.url.clone(),
|
location: url.clone(),
|
||||||
url: VerbatimUrl::from_url(url),
|
url: VerbatimUrl::from_url(url),
|
||||||
};
|
};
|
||||||
let built_dist = BuiltDist::DirectUrl(direct_dist);
|
let built_dist = BuiltDist::DirectUrl(direct_dist);
|
||||||
Ok(Dist::Built(built_dist))
|
Ok(Dist::Built(built_dist))
|
||||||
}
|
}
|
||||||
SourceKind::Git(_) => Err(LockErrorKind::InvalidWheelSource {
|
Source::Git(_, _) => Err(LockErrorKind::InvalidWheelSource {
|
||||||
id: self.id.clone(),
|
id: self.id.clone(),
|
||||||
source_type: "Git",
|
source_type: "Git",
|
||||||
}
|
}
|
||||||
.into()),
|
.into()),
|
||||||
SourceKind::Directory => Err(LockErrorKind::InvalidWheelSource {
|
Source::Directory(_) => Err(LockErrorKind::InvalidWheelSource {
|
||||||
id: self.id.clone(),
|
id: self.id.clone(),
|
||||||
source_type: "directory",
|
source_type: "directory",
|
||||||
}
|
}
|
||||||
.into()),
|
.into()),
|
||||||
SourceKind::Editable => Err(LockErrorKind::InvalidWheelSource {
|
Source::Editable(_) => Err(LockErrorKind::InvalidWheelSource {
|
||||||
id: self.id.clone(),
|
id: self.id.clone(),
|
||||||
source_type: "editable",
|
source_type: "editable",
|
||||||
}
|
}
|
||||||
|
|
@ -640,43 +649,59 @@ impl Distribution {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(sdist) = &self.sdist {
|
if let Some(sdist) = &self.sdist {
|
||||||
return match &self.id.source.kind {
|
return match &self.id.source {
|
||||||
SourceKind::Path => {
|
Source::Path(path) => {
|
||||||
let path_dist = PathSourceDist {
|
let path_dist = PathSourceDist {
|
||||||
name: self.id.name.clone(),
|
name: self.id.name.clone(),
|
||||||
url: VerbatimUrl::from_url(self.id.source.url.clone()),
|
url: VerbatimUrl::from_path(workspace_root.join(path)).map_err(|err| {
|
||||||
path: self.id.source.url.to_file_path().unwrap(),
|
LockErrorKind::VerbatimUrl {
|
||||||
|
id: self.id.clone(),
|
||||||
|
err,
|
||||||
|
}
|
||||||
|
})?,
|
||||||
|
install_path: workspace_root.join(path),
|
||||||
|
lock_path: path.clone(),
|
||||||
};
|
};
|
||||||
let source_dist = distribution_types::SourceDist::Path(path_dist);
|
let source_dist = distribution_types::SourceDist::Path(path_dist);
|
||||||
Ok(Dist::Source(source_dist))
|
Ok(Dist::Source(source_dist))
|
||||||
}
|
}
|
||||||
SourceKind::Directory => {
|
Source::Directory(path) => {
|
||||||
let dir_dist = DirectorySourceDist {
|
let dir_dist = DirectorySourceDist {
|
||||||
name: self.id.name.clone(),
|
name: self.id.name.clone(),
|
||||||
url: VerbatimUrl::from_url(self.id.source.url.clone()),
|
url: VerbatimUrl::from_path(workspace_root.join(path)).map_err(|err| {
|
||||||
path: self.id.source.url.to_file_path().unwrap(),
|
LockErrorKind::VerbatimUrl {
|
||||||
|
id: self.id.clone(),
|
||||||
|
err,
|
||||||
|
}
|
||||||
|
})?,
|
||||||
|
install_path: workspace_root.join(path),
|
||||||
|
lock_path: path.clone(),
|
||||||
editable: false,
|
editable: false,
|
||||||
};
|
};
|
||||||
let source_dist = distribution_types::SourceDist::Directory(dir_dist);
|
let source_dist = distribution_types::SourceDist::Directory(dir_dist);
|
||||||
Ok(Dist::Source(source_dist))
|
Ok(Dist::Source(source_dist))
|
||||||
}
|
}
|
||||||
SourceKind::Editable => {
|
Source::Editable(path) => {
|
||||||
let dir_dist = DirectorySourceDist {
|
let dir_dist = DirectorySourceDist {
|
||||||
name: self.id.name.clone(),
|
name: self.id.name.clone(),
|
||||||
url: VerbatimUrl::from_url(self.id.source.url.clone()),
|
url: VerbatimUrl::from_path(workspace_root.join(path)).map_err(|err| {
|
||||||
path: self.id.source.url.to_file_path().unwrap(),
|
LockErrorKind::VerbatimUrl {
|
||||||
|
id: self.id.clone(),
|
||||||
|
err,
|
||||||
|
}
|
||||||
|
})?,
|
||||||
|
install_path: workspace_root.join(path),
|
||||||
|
lock_path: path.clone(),
|
||||||
editable: true,
|
editable: true,
|
||||||
};
|
};
|
||||||
let source_dist = distribution_types::SourceDist::Directory(dir_dist);
|
let source_dist = distribution_types::SourceDist::Directory(dir_dist);
|
||||||
Ok(Dist::Source(source_dist))
|
Ok(Dist::Source(source_dist))
|
||||||
}
|
}
|
||||||
SourceKind::Git(git) => {
|
Source::Git(url, git) => {
|
||||||
// Reconstruct the `GitUrl` from the `GitSource`.
|
// Reconstruct the `GitUrl` from the `GitSource`.
|
||||||
let git_url = uv_git::GitUrl::new(
|
let git_url =
|
||||||
self.id.source.url.clone(),
|
uv_git::GitUrl::new(url.clone(), GitReference::from(git.kind.clone()))
|
||||||
GitReference::from(git.kind.clone()),
|
.with_precise(git.precise);
|
||||||
)
|
|
||||||
.with_precise(git.precise);
|
|
||||||
|
|
||||||
// Reconstruct the PEP 508-compatible URL from the `GitSource`.
|
// Reconstruct the PEP 508-compatible URL from the `GitSource`.
|
||||||
let url = Url::from(ParsedGitUrl {
|
let url = Url::from(ParsedGitUrl {
|
||||||
|
|
@ -693,32 +718,32 @@ impl Distribution {
|
||||||
let source_dist = distribution_types::SourceDist::Git(git_dist);
|
let source_dist = distribution_types::SourceDist::Git(git_dist);
|
||||||
Ok(Dist::Source(source_dist))
|
Ok(Dist::Source(source_dist))
|
||||||
}
|
}
|
||||||
SourceKind::Direct(direct) => {
|
Source::Direct(url, direct) => {
|
||||||
let url = Url::from(ParsedArchiveUrl {
|
let url = Url::from(ParsedArchiveUrl {
|
||||||
url: self.id.source.url.clone(),
|
url: url.clone(),
|
||||||
subdirectory: direct.subdirectory.as_ref().map(PathBuf::from),
|
subdirectory: direct.subdirectory.as_ref().map(PathBuf::from),
|
||||||
});
|
});
|
||||||
let direct_dist = DirectUrlSourceDist {
|
let direct_dist = DirectUrlSourceDist {
|
||||||
name: self.id.name.clone(),
|
name: self.id.name.clone(),
|
||||||
location: self.id.source.url.clone(),
|
location: url.clone(),
|
||||||
subdirectory: direct.subdirectory.as_ref().map(PathBuf::from),
|
subdirectory: direct.subdirectory.as_ref().map(PathBuf::from),
|
||||||
url: VerbatimUrl::from_url(url),
|
url: VerbatimUrl::from_url(url),
|
||||||
};
|
};
|
||||||
let source_dist = distribution_types::SourceDist::DirectUrl(direct_dist);
|
let source_dist = distribution_types::SourceDist::DirectUrl(direct_dist);
|
||||||
Ok(Dist::Source(source_dist))
|
Ok(Dist::Source(source_dist))
|
||||||
}
|
}
|
||||||
SourceKind::Registry => {
|
Source::Registry(url) => {
|
||||||
let file = Box::new(distribution_types::File {
|
let file = Box::new(distribution_types::File {
|
||||||
dist_info_metadata: false,
|
dist_info_metadata: false,
|
||||||
filename: sdist.url.filename().unwrap().to_string(),
|
filename: sdist.filename().unwrap().to_string(),
|
||||||
hashes: vec![],
|
hashes: vec![],
|
||||||
requires_python: None,
|
requires_python: None,
|
||||||
size: sdist.size,
|
size: sdist.size(),
|
||||||
upload_time_utc_ms: None,
|
upload_time_utc_ms: None,
|
||||||
url: FileLocation::AbsoluteUrl(sdist.url.to_string()),
|
url: FileLocation::AbsoluteUrl(url.to_string()),
|
||||||
yanked: None,
|
yanked: None,
|
||||||
});
|
});
|
||||||
let index = IndexUrl::Url(VerbatimUrl::from_url(self.id.source.url.clone()));
|
let index = IndexUrl::Url(VerbatimUrl::from_url(url.clone()));
|
||||||
let reg_dist = RegistrySourceDist {
|
let reg_dist = RegistrySourceDist {
|
||||||
name: self.id.name.clone(),
|
name: self.id.name.clone(),
|
||||||
version: self.id.version.clone(),
|
version: self.id.version.clone(),
|
||||||
|
|
@ -765,10 +790,10 @@ impl Distribution {
|
||||||
|
|
||||||
/// Returns the [`ResolvedRepositoryReference`] for the distribution, if it is a Git source.
|
/// Returns the [`ResolvedRepositoryReference`] for the distribution, if it is a Git source.
|
||||||
pub fn as_git_ref(&self) -> Option<ResolvedRepositoryReference> {
|
pub fn as_git_ref(&self) -> Option<ResolvedRepositoryReference> {
|
||||||
match &self.id.source.kind {
|
match &self.id.source {
|
||||||
SourceKind::Git(git) => Some(ResolvedRepositoryReference {
|
Source::Git(url, git) => Some(ResolvedRepositoryReference {
|
||||||
reference: RepositoryReference {
|
reference: RepositoryReference {
|
||||||
url: RepositoryUrl::new(&self.id.source.url),
|
url: RepositoryUrl::new(url),
|
||||||
reference: GitReference::from(git.kind.clone()),
|
reference: GitReference::from(git.kind.clone()),
|
||||||
},
|
},
|
||||||
sha: git.precise,
|
sha: git.precise,
|
||||||
|
|
@ -804,10 +829,28 @@ impl std::fmt::Display for DistributionId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// NOTE: Care should be taken when adding variants to this enum. Namely, new
|
||||||
|
/// variants should be added without changing the relative ordering of other
|
||||||
|
/// variants. Otherwise, this could cause the lock file to have a different
|
||||||
|
/// canonical ordering of distributions.
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||||
struct Source {
|
enum Source {
|
||||||
kind: SourceKind,
|
Registry(Url),
|
||||||
url: Url,
|
Git(Url, GitSource),
|
||||||
|
Direct(Url, DirectSource),
|
||||||
|
Path(PathBuf),
|
||||||
|
Directory(PathBuf),
|
||||||
|
Editable(PathBuf),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [`PathBuf`], but we show `.` instead of an empty path.
|
||||||
|
fn serialize_path_with_dot(path: &Path) -> Cow<str> {
|
||||||
|
let path = path.to_string_lossy();
|
||||||
|
if path.is_empty() {
|
||||||
|
Cow::Borrowed(".")
|
||||||
|
} else {
|
||||||
|
path
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Source {
|
impl Source {
|
||||||
|
|
@ -862,70 +905,58 @@ impl Source {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_direct_built_dist(direct_dist: &DirectUrlBuiltDist) -> Source {
|
fn from_direct_built_dist(direct_dist: &DirectUrlBuiltDist) -> Source {
|
||||||
Source {
|
Source::Direct(
|
||||||
kind: SourceKind::Direct(DirectSource { subdirectory: None }),
|
direct_dist.url.to_url(),
|
||||||
url: direct_dist.url.to_url(),
|
DirectSource { subdirectory: None },
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_direct_source_dist(direct_dist: &DirectUrlSourceDist) -> Source {
|
fn from_direct_source_dist(direct_dist: &DirectUrlSourceDist) -> Source {
|
||||||
Source {
|
Source::Direct(
|
||||||
kind: SourceKind::Direct(DirectSource {
|
direct_dist.url.to_url(),
|
||||||
|
DirectSource {
|
||||||
subdirectory: direct_dist
|
subdirectory: direct_dist
|
||||||
.subdirectory
|
.subdirectory
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.and_then(Path::to_str)
|
.and_then(Path::to_str)
|
||||||
.map(ToString::to_string),
|
.map(ToString::to_string),
|
||||||
}),
|
},
|
||||||
url: direct_dist.url.to_url(),
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_path_built_dist(path_dist: &PathBuiltDist) -> Source {
|
fn from_path_built_dist(path_dist: &PathBuiltDist) -> Source {
|
||||||
Source {
|
Source::Path(path_dist.path.clone())
|
||||||
kind: SourceKind::Path,
|
|
||||||
url: path_dist.url.to_url(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_path_source_dist(path_dist: &PathSourceDist) -> Source {
|
fn from_path_source_dist(path_dist: &PathSourceDist) -> Source {
|
||||||
Source {
|
Source::Path(path_dist.install_path.clone())
|
||||||
kind: SourceKind::Path,
|
|
||||||
url: path_dist.url.to_url(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_directory_source_dist(directory_dist: &DirectorySourceDist) -> Source {
|
fn from_directory_source_dist(directory_dist: &DirectorySourceDist) -> Source {
|
||||||
Source {
|
if directory_dist.editable {
|
||||||
kind: if directory_dist.editable {
|
Source::Editable(directory_dist.lock_path.clone())
|
||||||
SourceKind::Editable
|
} else {
|
||||||
} else {
|
Source::Directory(directory_dist.lock_path.clone())
|
||||||
SourceKind::Directory
|
|
||||||
},
|
|
||||||
url: directory_dist.url.to_url(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_index_url(index_url: &IndexUrl) -> Source {
|
fn from_index_url(index_url: &IndexUrl) -> Source {
|
||||||
match *index_url {
|
match *index_url {
|
||||||
IndexUrl::Pypi(ref verbatim_url) => Source {
|
IndexUrl::Pypi(ref verbatim_url) => Source::Registry(verbatim_url.to_url()),
|
||||||
kind: SourceKind::Registry,
|
IndexUrl::Url(ref verbatim_url) => Source::Registry(verbatim_url.to_url()),
|
||||||
url: verbatim_url.to_url(),
|
// TODO(konsti): Retain path on index url without converting to URL.
|
||||||
},
|
IndexUrl::Path(ref verbatim_url) => Source::Path(
|
||||||
IndexUrl::Url(ref verbatim_url) => Source {
|
verbatim_url
|
||||||
kind: SourceKind::Registry,
|
.to_file_path()
|
||||||
url: verbatim_url.to_url(),
|
.expect("Could not convert index url to path"),
|
||||||
},
|
),
|
||||||
IndexUrl::Path(ref verbatim_url) => Source {
|
|
||||||
kind: SourceKind::Path,
|
|
||||||
url: verbatim_url.to_url(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_git_dist(git_dist: &GitSourceDist) -> Source {
|
fn from_git_dist(git_dist: &GitSourceDist) -> Source {
|
||||||
Source {
|
Source::Git(
|
||||||
kind: SourceKind::Git(GitSource {
|
locked_git_url(git_dist),
|
||||||
|
GitSource {
|
||||||
kind: GitSourceKind::from(git_dist.git.reference().clone()),
|
kind: GitSourceKind::from(git_dist.git.reference().clone()),
|
||||||
precise: git_dist.git.precise().expect("precise commit"),
|
precise: git_dist.git.precise().expect("precise commit"),
|
||||||
subdirectory: git_dist
|
subdirectory: git_dist
|
||||||
|
|
@ -933,9 +964,8 @@ impl Source {
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.and_then(Path::to_str)
|
.and_then(Path::to_str)
|
||||||
.map(ToString::to_string),
|
.map(ToString::to_string),
|
||||||
}),
|
},
|
||||||
url: locked_git_url(git_dist),
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -943,45 +973,45 @@ impl std::str::FromStr for Source {
|
||||||
type Err = SourceParseError;
|
type Err = SourceParseError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Source, SourceParseError> {
|
fn from_str(s: &str) -> Result<Source, SourceParseError> {
|
||||||
let (kind, url) = s.split_once('+').ok_or_else(|| SourceParseError::NoPlus {
|
let (kind, url_or_path) = s.split_once('+').ok_or_else(|| SourceParseError::NoPlus {
|
||||||
given: s.to_string(),
|
given: s.to_string(),
|
||||||
})?;
|
})?;
|
||||||
let mut url = Url::parse(url).map_err(|err| SourceParseError::InvalidUrl {
|
|
||||||
given: s.to_string(),
|
|
||||||
err,
|
|
||||||
})?;
|
|
||||||
match kind {
|
match kind {
|
||||||
"registry" => Ok(Source {
|
"registry" => {
|
||||||
kind: SourceKind::Registry,
|
let url = Url::parse(url_or_path).map_err(|err| SourceParseError::InvalidUrl {
|
||||||
url,
|
given: s.to_string(),
|
||||||
}),
|
err,
|
||||||
"git" => Ok(Source {
|
})?;
|
||||||
kind: SourceKind::Git(GitSource::from_url(&mut url).map_err(|err| match err {
|
Ok(Source::Registry(url))
|
||||||
|
}
|
||||||
|
"git" => {
|
||||||
|
let mut url =
|
||||||
|
Url::parse(url_or_path).map_err(|err| SourceParseError::InvalidUrl {
|
||||||
|
given: s.to_string(),
|
||||||
|
err,
|
||||||
|
})?;
|
||||||
|
let git_source = GitSource::from_url(&mut url).map_err(|err| match err {
|
||||||
GitSourceError::InvalidSha => SourceParseError::InvalidSha {
|
GitSourceError::InvalidSha => SourceParseError::InvalidSha {
|
||||||
given: s.to_string(),
|
given: s.to_string(),
|
||||||
},
|
},
|
||||||
GitSourceError::MissingSha => SourceParseError::MissingSha {
|
GitSourceError::MissingSha => SourceParseError::MissingSha {
|
||||||
given: s.to_string(),
|
given: s.to_string(),
|
||||||
},
|
},
|
||||||
})?),
|
})?;
|
||||||
url,
|
Ok(Source::Git(url, git_source))
|
||||||
}),
|
}
|
||||||
"direct" => Ok(Source {
|
"direct" => {
|
||||||
kind: SourceKind::Direct(DirectSource::from_url(&mut url)),
|
let mut url =
|
||||||
url,
|
Url::parse(url_or_path).map_err(|err| SourceParseError::InvalidUrl {
|
||||||
}),
|
given: s.to_string(),
|
||||||
"path" => Ok(Source {
|
err,
|
||||||
kind: SourceKind::Path,
|
})?;
|
||||||
url,
|
let direct_source = DirectSource::from_url(&mut url);
|
||||||
}),
|
Ok(Source::Direct(url, direct_source))
|
||||||
"directory" => Ok(Source {
|
}
|
||||||
kind: SourceKind::Directory,
|
"path" => Ok(Source::Path(PathBuf::from(url_or_path))),
|
||||||
url,
|
"directory" => Ok(Source::Directory(PathBuf::from(url_or_path))),
|
||||||
}),
|
"editable" => Ok(Source::Editable(PathBuf::from(url_or_path))),
|
||||||
"editable" => Ok(Source {
|
|
||||||
kind: SourceKind::Editable,
|
|
||||||
url,
|
|
||||||
}),
|
|
||||||
name => Err(SourceParseError::UnrecognizedSourceName {
|
name => Err(SourceParseError::UnrecognizedSourceName {
|
||||||
given: s.to_string(),
|
given: s.to_string(),
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
|
|
@ -992,7 +1022,14 @@ impl std::str::FromStr for Source {
|
||||||
|
|
||||||
impl std::fmt::Display for Source {
|
impl std::fmt::Display for Source {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
write!(f, "{}+{}", self.kind.name(), self.url)
|
match self {
|
||||||
|
Source::Registry(url) | Source::Git(url, _) | Source::Direct(url, _) => {
|
||||||
|
write!(f, "{}+{}", self.name(), url)
|
||||||
|
}
|
||||||
|
Source::Path(path) | Source::Directory(path) | Source::Editable(path) => {
|
||||||
|
write!(f, "{}+{}", self.name(), serialize_path_with_dot(path))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1006,29 +1043,15 @@ impl<'de> serde::Deserialize<'de> for Source {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// NOTE: Care should be taken when adding variants to this enum. Namely, new
|
impl Source {
|
||||||
/// variants should be added without changing the relative ordering of other
|
|
||||||
/// variants. Otherwise, this could cause the lock file to have a different
|
|
||||||
/// canonical ordering of distributions.
|
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
|
||||||
enum SourceKind {
|
|
||||||
Registry,
|
|
||||||
Git(GitSource),
|
|
||||||
Direct(DirectSource),
|
|
||||||
Path,
|
|
||||||
Directory,
|
|
||||||
Editable,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SourceKind {
|
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
match *self {
|
match *self {
|
||||||
SourceKind::Registry => "registry",
|
Self::Registry(..) => "registry",
|
||||||
SourceKind::Git(_) => "git",
|
Self::Git(..) => "git",
|
||||||
SourceKind::Direct(_) => "direct",
|
Self::Direct(..) => "direct",
|
||||||
SourceKind::Path => "path",
|
Self::Path(..) => "path",
|
||||||
SourceKind::Directory => "directory",
|
Self::Directory(..) => "directory",
|
||||||
SourceKind::Editable => "editable",
|
Self::Editable(..) => "editable",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1038,8 +1061,8 @@ impl SourceKind {
|
||||||
/// _not_ be present.
|
/// _not_ be present.
|
||||||
fn requires_hash(&self) -> bool {
|
fn requires_hash(&self) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
SourceKind::Registry | SourceKind::Direct(_) | SourceKind::Path => true,
|
Self::Registry(..) | Self::Direct(..) | Self::Path(..) => true,
|
||||||
SourceKind::Git(_) | SourceKind::Directory | SourceKind::Editable => false,
|
Self::Git(..) | Self::Directory(..) | Self::Editable(..) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1127,12 +1150,7 @@ enum GitSourceKind {
|
||||||
|
|
||||||
/// Inspired by: <https://discuss.python.org/t/lock-files-again-but-this-time-w-sdists/46593>
|
/// Inspired by: <https://discuss.python.org/t/lock-files-again-but-this-time-w-sdists/46593>
|
||||||
#[derive(Clone, Debug, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Deserialize)]
|
||||||
struct SourceDist {
|
struct SourceDistMetadata {
|
||||||
/// A URL or file path (via `file://`) where the source dist that was
|
|
||||||
/// locked against was found. The location does not need to exist in the
|
|
||||||
/// future, so this should be treated as only a hint to where to look
|
|
||||||
/// and/or recording where the source dist file originally came from.
|
|
||||||
url: Url,
|
|
||||||
/// A hash of the source distribution.
|
/// A hash of the source distribution.
|
||||||
///
|
///
|
||||||
/// This is only present for source distributions that come from registries
|
/// This is only present for source distributions that come from registries
|
||||||
|
|
@ -1145,15 +1163,79 @@ struct SourceDist {
|
||||||
size: Option<u64>,
|
size: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A URL or file path where the source dist that was
|
||||||
|
/// locked against was found. The location does not need to exist in the
|
||||||
|
/// future, so this should be treated as only a hint to where to look
|
||||||
|
/// and/or recording where the source dist file originally came from.
|
||||||
|
#[derive(Clone, Debug, serde::Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum SourceDist {
|
||||||
|
Url {
|
||||||
|
url: Url,
|
||||||
|
#[serde(flatten)]
|
||||||
|
metadata: SourceDistMetadata,
|
||||||
|
},
|
||||||
|
Path {
|
||||||
|
#[serde(deserialize_with = "deserialize_path_with_dot")]
|
||||||
|
path: PathBuf,
|
||||||
|
#[serde(flatten)]
|
||||||
|
metadata: SourceDistMetadata,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [`PathBuf`], but we show `.` instead of an empty path.
|
||||||
|
fn deserialize_path_with_dot<'de, D>(deserializer: D) -> Result<PathBuf, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let path = String::deserialize(deserializer)?;
|
||||||
|
if path == "." {
|
||||||
|
Ok(PathBuf::new())
|
||||||
|
} else {
|
||||||
|
Ok(PathBuf::from(path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceDist {
|
||||||
|
pub(crate) fn filename(&self) -> Option<Cow<str>> {
|
||||||
|
match self {
|
||||||
|
SourceDist::Url { url, .. } => url.filename().ok(),
|
||||||
|
SourceDist::Path { path, .. } => {
|
||||||
|
path.file_name().map(|filename| filename.to_string_lossy())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash(&self) -> Option<&Hash> {
|
||||||
|
match &self {
|
||||||
|
SourceDist::Url { metadata, .. } => metadata.hash.as_ref(),
|
||||||
|
SourceDist::Path { metadata, .. } => metadata.hash.as_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn size(&self) -> Option<u64> {
|
||||||
|
match &self {
|
||||||
|
SourceDist::Url { metadata, .. } => metadata.size,
|
||||||
|
SourceDist::Path { metadata, .. } => metadata.size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SourceDist {
|
impl SourceDist {
|
||||||
/// Returns the TOML representation of this source distribution.
|
/// Returns the TOML representation of this source distribution.
|
||||||
fn to_toml(&self) -> Result<InlineTable> {
|
fn to_toml(&self) -> Result<InlineTable> {
|
||||||
let mut table = InlineTable::new();
|
let mut table = InlineTable::new();
|
||||||
table.insert("url", Value::from(self.url.to_string()));
|
match &self {
|
||||||
if let Some(ref hash) = self.hash {
|
SourceDist::Url { url, .. } => {
|
||||||
|
table.insert("url", Value::from(url.as_str()));
|
||||||
|
}
|
||||||
|
SourceDist::Path { path, .. } => {
|
||||||
|
table.insert("path", Value::from(serialize_path_with_dot(path).as_ref()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(hash) = self.hash() {
|
||||||
table.insert("hash", Value::from(hash.to_string()));
|
table.insert("hash", Value::from(hash.to_string()));
|
||||||
}
|
}
|
||||||
if let Some(size) = self.size {
|
if let Some(size) = self.size() {
|
||||||
table.insert("size", Value::from(i64::try_from(size)?));
|
table.insert("size", Value::from(i64::try_from(size)?));
|
||||||
}
|
}
|
||||||
Ok(table)
|
Ok(table)
|
||||||
|
|
@ -1219,30 +1301,39 @@ impl SourceDist {
|
||||||
.map_err(LockError::from)?;
|
.map_err(LockError::from)?;
|
||||||
let hash = reg_dist.file.hashes.first().cloned().map(Hash::from);
|
let hash = reg_dist.file.hashes.first().cloned().map(Hash::from);
|
||||||
let size = reg_dist.file.size;
|
let size = reg_dist.file.size;
|
||||||
Ok(SourceDist { url, hash, size })
|
Ok(SourceDist::Url {
|
||||||
|
url,
|
||||||
|
metadata: SourceDistMetadata { hash, size },
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_direct_dist(direct_dist: &DirectUrlSourceDist, hashes: &[HashDigest]) -> SourceDist {
|
fn from_direct_dist(direct_dist: &DirectUrlSourceDist, hashes: &[HashDigest]) -> SourceDist {
|
||||||
SourceDist {
|
SourceDist::Url {
|
||||||
url: direct_dist.url.to_url(),
|
url: direct_dist.url.to_url(),
|
||||||
hash: hashes.first().cloned().map(Hash::from),
|
metadata: SourceDistMetadata {
|
||||||
size: None,
|
hash: hashes.first().cloned().map(Hash::from),
|
||||||
|
size: None,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_git_dist(git_dist: &GitSourceDist, hashes: &[HashDigest]) -> SourceDist {
|
fn from_git_dist(git_dist: &GitSourceDist, hashes: &[HashDigest]) -> SourceDist {
|
||||||
SourceDist {
|
SourceDist::Url {
|
||||||
url: locked_git_url(git_dist),
|
url: locked_git_url(git_dist),
|
||||||
hash: hashes.first().cloned().map(Hash::from),
|
metadata: SourceDistMetadata {
|
||||||
size: None,
|
hash: hashes.first().cloned().map(Hash::from),
|
||||||
|
size: None,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_path_dist(path_dist: &PathSourceDist, hashes: &[HashDigest]) -> SourceDist {
|
fn from_path_dist(path_dist: &PathSourceDist, hashes: &[HashDigest]) -> SourceDist {
|
||||||
SourceDist {
|
SourceDist::Path {
|
||||||
url: path_dist.url.to_url(),
|
path: path_dist.lock_path.clone(),
|
||||||
hash: hashes.first().cloned().map(Hash::from),
|
metadata: SourceDistMetadata {
|
||||||
size: None,
|
hash: hashes.first().cloned().map(Hash::from),
|
||||||
|
size: None,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1250,10 +1341,12 @@ impl SourceDist {
|
||||||
directory_dist: &DirectorySourceDist,
|
directory_dist: &DirectorySourceDist,
|
||||||
hashes: &[HashDigest],
|
hashes: &[HashDigest],
|
||||||
) -> SourceDist {
|
) -> SourceDist {
|
||||||
SourceDist {
|
SourceDist::Path {
|
||||||
url: directory_dist.url.to_url(),
|
path: directory_dist.lock_path.clone(),
|
||||||
hash: hashes.first().cloned().map(Hash::from),
|
metadata: SourceDistMetadata {
|
||||||
size: None,
|
hash: hashes.first().cloned().map(Hash::from),
|
||||||
|
size: None,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1439,7 +1532,7 @@ impl Wheel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_registry_dist(&self, source: &Source) -> RegistryBuiltWheel {
|
fn to_registry_dist(&self, url: &Url, source: &Source) -> RegistryBuiltWheel {
|
||||||
let filename: WheelFilename = self.filename.clone();
|
let filename: WheelFilename = self.filename.clone();
|
||||||
let file = Box::new(distribution_types::File {
|
let file = Box::new(distribution_types::File {
|
||||||
dist_info_metadata: false,
|
dist_info_metadata: false,
|
||||||
|
|
@ -1451,7 +1544,7 @@ impl Wheel {
|
||||||
url: FileLocation::AbsoluteUrl(self.url.to_string()),
|
url: FileLocation::AbsoluteUrl(self.url.to_string()),
|
||||||
yanked: None,
|
yanked: None,
|
||||||
});
|
});
|
||||||
let index = IndexUrl::Url(VerbatimUrl::from_url(source.url.clone()));
|
let index = IndexUrl::Url(VerbatimUrl::from_url(url.clone()));
|
||||||
RegistryBuiltWheel {
|
RegistryBuiltWheel {
|
||||||
filename,
|
filename,
|
||||||
file,
|
file,
|
||||||
|
|
@ -1632,7 +1725,7 @@ impl<'de> serde::Deserialize<'de> for Hash {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
pub struct LockError(Box<LockErrorKind>);
|
pub struct LockError(Box<LockErrorKind>);
|
||||||
|
|
||||||
|
|
@ -1651,7 +1744,7 @@ where
|
||||||
/// For example, if there are two or more duplicative distributions given
|
/// For example, if there are two or more duplicative distributions given
|
||||||
/// to `Lock::new`, then an error is returned. It's likely that the fault
|
/// to `Lock::new`, then an error is returned. It's likely that the fault
|
||||||
/// is with the caller somewhere in such cases.
|
/// is with the caller somewhere in such cases.
|
||||||
#[derive(Clone, Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
enum LockErrorKind {
|
enum LockErrorKind {
|
||||||
/// An error that occurs when multiple distributions with the same
|
/// An error that occurs when multiple distributions with the same
|
||||||
/// ID were found.
|
/// ID were found.
|
||||||
|
|
@ -1732,7 +1825,7 @@ enum LockErrorKind {
|
||||||
},
|
},
|
||||||
/// An error that occurs when a hash is expected (or not) for a particular
|
/// An error that occurs when a hash is expected (or not) for a particular
|
||||||
/// artifact, but one was not found (or was).
|
/// artifact, but one was not found (or was).
|
||||||
#[error("since the distribution `{id}` comes from a {source} dependency, a hash was {expected} but one was not found for {artifact_type}", source = id.source.kind.name(), expected = if *expected { "expected" } else { "not expected" })]
|
#[error("since the distribution `{id}` comes from a {source} dependency, a hash was {expected} but one was not found for {artifact_type}", source = id.source.name(), expected = if *expected { "expected" } else { "not expected" })]
|
||||||
Hash {
|
Hash {
|
||||||
/// The ID of the distribution that has a missing hash.
|
/// The ID of the distribution that has a missing hash.
|
||||||
id: DistributionId,
|
id: DistributionId,
|
||||||
|
|
@ -1777,6 +1870,15 @@ enum LockErrorKind {
|
||||||
/// The ID of the distribution that has a missing base.
|
/// The ID of the distribution that has a missing base.
|
||||||
id: DistributionId,
|
id: DistributionId,
|
||||||
},
|
},
|
||||||
|
/// An error that occurs when converting between URLs and paths.
|
||||||
|
#[error("found dependency `{id}` with no locked distribution")]
|
||||||
|
VerbatimUrl {
|
||||||
|
/// The ID of the distribution that has a missing base.
|
||||||
|
id: DistributionId,
|
||||||
|
/// The inner error we forward.
|
||||||
|
#[source]
|
||||||
|
err: VerbatimUrlError,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error that occurs when a source string could not be parsed.
|
/// An error that occurs when a source string could not be parsed.
|
||||||
|
|
@ -1827,7 +1929,7 @@ impl std::error::Error for HashParseError {}
|
||||||
|
|
||||||
impl std::fmt::Display for HashParseError {
|
impl std::fmt::Display for HashParseError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
self.0.fmt(f)
|
Display::fmt(self.0, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -334,7 +334,8 @@ impl PubGrubRequirement {
|
||||||
RequirementSource::Path {
|
RequirementSource::Path {
|
||||||
editable,
|
editable,
|
||||||
url,
|
url,
|
||||||
path,
|
install_path,
|
||||||
|
lock_path,
|
||||||
} => {
|
} => {
|
||||||
let Some(expected) = urls.get(&requirement.name) else {
|
let Some(expected) = urls.get(&requirement.name) else {
|
||||||
return Err(ResolveError::DisallowedUrl(
|
return Err(ResolveError::DisallowedUrl(
|
||||||
|
|
@ -344,7 +345,8 @@ impl PubGrubRequirement {
|
||||||
};
|
};
|
||||||
|
|
||||||
let parsed_url = ParsedUrl::Path(ParsedPathUrl::from_source(
|
let parsed_url = ParsedUrl::Path(ParsedPathUrl::from_source(
|
||||||
path.clone(),
|
install_path.clone(),
|
||||||
|
lock_path.clone(),
|
||||||
*editable,
|
*editable,
|
||||||
url.to_url(),
|
url.to_url(),
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,9 @@ fn iter_locals(source: &RequirementSource) -> Box<dyn Iterator<Item = Version> +
|
||||||
.filter(pep440_rs::Version::is_local),
|
.filter(pep440_rs::Version::is_local),
|
||||||
),
|
),
|
||||||
RequirementSource::Git { .. } => Box::new(iter::empty()),
|
RequirementSource::Git { .. } => Box::new(iter::empty()),
|
||||||
RequirementSource::Path { path, .. } => Box::new(
|
RequirementSource::Path {
|
||||||
|
install_path: path, ..
|
||||||
|
} => Box::new(
|
||||||
path.file_name()
|
path.file_name()
|
||||||
.and_then(|filename| {
|
.and_then(|filename| {
|
||||||
let filename = filename.to_string_lossy();
|
let filename = filename.to_string_lossy();
|
||||||
|
|
|
||||||
|
|
@ -53,13 +53,15 @@ impl Urls {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RequirementSource::Path {
|
RequirementSource::Path {
|
||||||
path,
|
install_path,
|
||||||
|
lock_path,
|
||||||
editable,
|
editable,
|
||||||
url,
|
url,
|
||||||
} => {
|
} => {
|
||||||
let url = VerbatimParsedUrl {
|
let url = VerbatimParsedUrl {
|
||||||
parsed_url: ParsedUrl::Path(ParsedPathUrl::from_source(
|
parsed_url: ParsedUrl::Path(ParsedPathUrl::from_source(
|
||||||
path.clone(),
|
install_path.clone(),
|
||||||
|
lock_path.clone(),
|
||||||
*editable,
|
*editable,
|
||||||
url.to_url(),
|
url.to_url(),
|
||||||
)),
|
)),
|
||||||
|
|
@ -140,7 +142,8 @@ impl Urls {
|
||||||
a.subdirectory == b.subdirectory && git.same_ref(&a.url, &b.url)
|
a.subdirectory == b.subdirectory && git.same_ref(&a.url, &b.url)
|
||||||
}
|
}
|
||||||
(ParsedUrl::Path(a), ParsedUrl::Path(b)) => {
|
(ParsedUrl::Path(a), ParsedUrl::Path(b)) => {
|
||||||
a.path == b.path || is_same_file(&a.path, &b.path).unwrap_or(false)
|
a.install_path == b.install_path
|
||||||
|
|| is_same_file(&a.install_path, &b.install_path).unwrap_or(false)
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,20 +12,9 @@ Ok(
|
||||||
"anyio",
|
"anyio",
|
||||||
),
|
),
|
||||||
version: "4.3.0",
|
version: "4.3.0",
|
||||||
source: Source {
|
source: Path(
|
||||||
kind: Path,
|
"file:///foo/bar",
|
||||||
url: Url {
|
),
|
||||||
scheme: "file",
|
|
||||||
cannot_be_a_base: false,
|
|
||||||
username: "",
|
|
||||||
password: None,
|
|
||||||
host: None,
|
|
||||||
port: None,
|
|
||||||
path: "/foo/bar",
|
|
||||||
query: None,
|
|
||||||
fragment: None,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
sdist: None,
|
sdist: None,
|
||||||
wheels: [
|
wheels: [
|
||||||
|
|
@ -80,20 +69,9 @@ Ok(
|
||||||
"anyio",
|
"anyio",
|
||||||
),
|
),
|
||||||
version: "4.3.0",
|
version: "4.3.0",
|
||||||
source: Source {
|
source: Path(
|
||||||
kind: Path,
|
"file:///foo/bar",
|
||||||
url: Url {
|
),
|
||||||
scheme: "file",
|
|
||||||
cannot_be_a_base: false,
|
|
||||||
username: "",
|
|
||||||
password: None,
|
|
||||||
host: None,
|
|
||||||
port: None,
|
|
||||||
path: "/foo/bar",
|
|
||||||
query: None,
|
|
||||||
fragment: None,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}: 0,
|
}: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ pub(crate) async fn run(
|
||||||
.await?;
|
.await?;
|
||||||
project::sync::do_sync(
|
project::sync::do_sync(
|
||||||
project.project_name(),
|
project.project_name(),
|
||||||
|
project.workspace().root(),
|
||||||
&venv,
|
&venv,
|
||||||
&lock,
|
&lock,
|
||||||
&index_locations,
|
&index_locations,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
use distribution_types::IndexLocations;
|
use distribution_types::IndexLocations;
|
||||||
use install_wheel_rs::linker::LinkMode;
|
use install_wheel_rs::linker::LinkMode;
|
||||||
|
|
@ -60,6 +61,7 @@ pub(crate) async fn sync(
|
||||||
// Perform the sync operation.
|
// Perform the sync operation.
|
||||||
do_sync(
|
do_sync(
|
||||||
project.project_name(),
|
project.project_name(),
|
||||||
|
project.workspace().root(),
|
||||||
&venv,
|
&venv,
|
||||||
&lock,
|
&lock,
|
||||||
&index_locations,
|
&index_locations,
|
||||||
|
|
@ -77,7 +79,8 @@ pub(crate) async fn sync(
|
||||||
/// Sync a lockfile with an environment.
|
/// Sync a lockfile with an environment.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(super) async fn do_sync(
|
pub(super) async fn do_sync(
|
||||||
project: &PackageName,
|
project_name: &PackageName,
|
||||||
|
workspace_root: &Path,
|
||||||
venv: &PythonEnvironment,
|
venv: &PythonEnvironment,
|
||||||
lock: &Lock,
|
lock: &Lock,
|
||||||
index_locations: &IndexLocations,
|
index_locations: &IndexLocations,
|
||||||
|
|
@ -108,7 +111,8 @@ pub(super) async fn do_sync(
|
||||||
let tags = venv.interpreter().tags()?;
|
let tags = venv.interpreter().tags()?;
|
||||||
|
|
||||||
// Read the lockfile.
|
// Read the lockfile.
|
||||||
let resolution = lock.to_resolution(markers, tags, project, &extras, &dev)?;
|
let resolution =
|
||||||
|
lock.to_resolution(workspace_root, markers, tags, project_name, &extras, &dev)?;
|
||||||
|
|
||||||
// Initialize the registry client.
|
// Initialize the registry client.
|
||||||
// TODO(zanieb): Support client options e.g. offline, tls, etc.
|
// TODO(zanieb): Support client options e.g. offline, tls, etc.
|
||||||
|
|
|
||||||
|
|
@ -90,8 +90,8 @@ fn lock_wheel_registry() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "anyio"
|
name = "anyio"
|
||||||
|
|
@ -173,8 +173,8 @@ fn lock_sdist_registry() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "source-distribution"
|
name = "source-distribution"
|
||||||
|
|
@ -232,8 +232,8 @@ fn lock_sdist_git() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "uv-public-pypackage"
|
name = "uv-public-pypackage"
|
||||||
|
|
@ -347,8 +347,8 @@ fn lock_wheel_url() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "anyio"
|
name = "anyio"
|
||||||
|
|
@ -472,8 +472,8 @@ fn lock_sdist_url() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "anyio"
|
name = "anyio"
|
||||||
|
|
@ -608,8 +608,8 @@ fn lock_project_extra() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "anyio"
|
name = "anyio"
|
||||||
|
|
@ -893,8 +893,8 @@ fn lock_dependency_extra() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "flask"
|
name = "flask"
|
||||||
|
|
@ -1116,8 +1116,8 @@ fn lock_conditional_dependency_extra() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "requests"
|
name = "requests"
|
||||||
|
|
@ -1210,7 +1210,7 @@ fn lock_conditional_dependency_extra() -> Result<()> {
|
||||||
fs_err::copy(lockfile, context_38.temp_dir.join("uv.lock"))?;
|
fs_err::copy(lockfile, context_38.temp_dir.join("uv.lock"))?;
|
||||||
|
|
||||||
// Install from the lockfile.
|
// Install from the lockfile.
|
||||||
uv_snapshot!(context.filters(), context_38.sync(), @r###"
|
uv_snapshot!(context_38.filters(), context_38.sync(), @r###"
|
||||||
success: true
|
success: true
|
||||||
exit_code: 0
|
exit_code: 0
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
@ -1277,8 +1277,8 @@ fn lock_preference() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "iniconfig"
|
name = "iniconfig"
|
||||||
|
|
@ -1330,8 +1330,8 @@ fn lock_preference() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "iniconfig"
|
name = "iniconfig"
|
||||||
|
|
@ -1372,8 +1372,8 @@ fn lock_preference() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "iniconfig"
|
name = "iniconfig"
|
||||||
|
|
@ -1426,8 +1426,8 @@ fn lock_git_sha() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "uv-public-pypackage"
|
name = "uv-public-pypackage"
|
||||||
|
|
@ -1484,8 +1484,8 @@ fn lock_git_sha() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "uv-public-pypackage"
|
name = "uv-public-pypackage"
|
||||||
|
|
@ -1527,8 +1527,8 @@ fn lock_git_sha() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "uv-public-pypackage"
|
name = "uv-public-pypackage"
|
||||||
|
|
@ -1704,8 +1704,8 @@ fn lock_requires_python() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "pygls"
|
name = "pygls"
|
||||||
|
|
@ -1869,8 +1869,8 @@ fn lock_requires_python() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "pygls"
|
name = "pygls"
|
||||||
|
|
@ -2022,8 +2022,8 @@ fn lock_requires_python() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "pygls"
|
name = "pygls"
|
||||||
|
|
@ -2224,8 +2224,8 @@ fn lock_requires_python_star() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "linehaul"
|
name = "linehaul"
|
||||||
|
|
@ -2393,8 +2393,8 @@ fn lock_requires_python_pre() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "linehaul"
|
name = "linehaul"
|
||||||
|
|
@ -2477,8 +2477,8 @@ fn lock_dev() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "iniconfig"
|
name = "iniconfig"
|
||||||
|
|
@ -2578,8 +2578,8 @@ fn lock_conditional_unconditional() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "iniconfig"
|
name = "iniconfig"
|
||||||
|
|
@ -2638,8 +2638,8 @@ fn lock_multiple_markers() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "iniconfig"
|
name = "iniconfig"
|
||||||
|
|
|
||||||
|
|
@ -92,8 +92,8 @@ fn fork_basic() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "package-a"
|
name = "package-a"
|
||||||
|
|
@ -220,8 +220,8 @@ fn fork_marker_accrue() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "package-a"
|
name = "package-a"
|
||||||
|
|
@ -414,8 +414,8 @@ fn fork_marker_selection() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "package-a"
|
name = "package-a"
|
||||||
|
|
@ -578,8 +578,8 @@ fn fork_marker_track() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "package-a"
|
name = "package-a"
|
||||||
|
|
@ -715,8 +715,8 @@ fn fork_non_fork_marker_transitive() -> Result<()> {
|
||||||
[[distribution]]
|
[[distribution]]
|
||||||
name = "project"
|
name = "project"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "editable+file://[TEMP_DIR]/"
|
source = "editable+."
|
||||||
sdist = { url = "file://[TEMP_DIR]/" }
|
sdist = { path = "." }
|
||||||
|
|
||||||
[[distribution.dependencies]]
|
[[distribution.dependencies]]
|
||||||
name = "package-a"
|
name = "package-a"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ use std::process::Command;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use assert_cmd::assert::OutputAssertExt;
|
use assert_cmd::assert::OutputAssertExt;
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
use crate::common::{copy_dir_ignore, get_bin, uv_snapshot, TestContext, EXCLUDE_NEWER};
|
use crate::common::{copy_dir_ignore, get_bin, uv_snapshot, TestContext, EXCLUDE_NEWER};
|
||||||
|
|
||||||
|
|
@ -529,27 +528,12 @@ fn workspace_lock_idempotence(workspace: &str, subdirectories: &[&str]) -> Resul
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
|
|
||||||
let raw_lock = fs_err::read_to_string(work_dir.join("uv.lock"))?;
|
let lock = fs_err::read_to_string(work_dir.join("uv.lock"))?;
|
||||||
// Remove temp paths from lock.
|
|
||||||
// TODO(konsti): There shouldn't be absolute paths in the lock to begin with.
|
|
||||||
let redacted_lock = raw_lock
|
|
||||||
.replace(
|
|
||||||
Url::from_directory_path(&context.temp_dir)
|
|
||||||
.unwrap()
|
|
||||||
.as_str(),
|
|
||||||
"file:///tmp",
|
|
||||||
)
|
|
||||||
.replace(
|
|
||||||
Url::from_directory_path(fs_err::canonicalize(&context.temp_dir)?)
|
|
||||||
.unwrap()
|
|
||||||
.as_str(),
|
|
||||||
"file:///tmp",
|
|
||||||
);
|
|
||||||
// Check the lockfile is the same for all resolutions.
|
// Check the lockfile is the same for all resolutions.
|
||||||
if let Some(shared_lock) = &shared_lock {
|
if let Some(shared_lock) = &shared_lock {
|
||||||
assert_eq!(shared_lock, &redacted_lock);
|
assert_eq!(shared_lock, &lock);
|
||||||
} else {
|
} else {
|
||||||
shared_lock = Some(redacted_lock);
|
shared_lock = Some(lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue