Avoid using editable tag in lockfile for non-package dependencies (#6728)

## Summary

Use a dedicated source type for non-package requirements. Also enables
us to support non-package `path` dependencies _and_ removes the need to
have the member `pyproject.toml` files available when we sync _and_
makes it explicit which dependencies are virtual vs. not (as evidenced
by the snapshot changes). All good things!
This commit is contained in:
Charlie Marsh 2024-08-27 21:19:05 -04:00 committed by GitHub
parent 8fdb3a882e
commit 56cc0c9b3c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 407 additions and 74 deletions

View File

@ -34,6 +34,7 @@ pub struct CachedDirectUrlDist {
pub url: VerbatimUrl,
pub path: PathBuf,
pub editable: bool,
pub r#virtual: bool,
pub hashes: Vec<HashDigest>,
}
@ -57,6 +58,7 @@ impl CachedDist {
hashes,
path,
editable: false,
r#virtual: false,
}),
Dist::Built(BuiltDist::Path(dist)) => Self::Url(CachedDirectUrlDist {
filename,
@ -64,6 +66,7 @@ impl CachedDist {
hashes,
path,
editable: false,
r#virtual: false,
}),
Dist::Source(SourceDist::Registry(_dist)) => Self::Registry(CachedRegistryDist {
filename,
@ -76,6 +79,7 @@ impl CachedDist {
hashes,
path,
editable: false,
r#virtual: false,
}),
Dist::Source(SourceDist::Git(dist)) => Self::Url(CachedDirectUrlDist {
filename,
@ -83,6 +87,7 @@ impl CachedDist {
hashes,
path,
editable: false,
r#virtual: false,
}),
Dist::Source(SourceDist::Path(dist)) => Self::Url(CachedDirectUrlDist {
filename,
@ -90,6 +95,7 @@ impl CachedDist {
hashes,
path,
editable: false,
r#virtual: false,
}),
Dist::Source(SourceDist::Directory(dist)) => Self::Url(CachedDirectUrlDist {
filename,
@ -97,6 +103,7 @@ impl CachedDist {
hashes,
path,
editable: dist.editable,
r#virtual: dist.r#virtual,
}),
}
}
@ -124,6 +131,7 @@ impl CachedDist {
url: dist.url.raw().clone(),
install_path: path,
editable: dist.editable,
r#virtual: dist.r#virtual,
})))
} else {
Ok(Some(ParsedUrl::try_from(dist.url.to_url())?))
@ -161,6 +169,7 @@ impl CachedDirectUrlDist {
hashes,
path,
editable: false,
r#virtual: false,
}
}
}

View File

@ -309,6 +309,8 @@ pub struct DirectorySourceDist {
pub install_path: PathBuf,
/// Whether the package should be installed in editable mode.
pub editable: bool,
/// Whether the package should be built and installed.
pub r#virtual: bool,
/// The URL as it was provided by the user.
pub url: VerbatimUrl,
}
@ -404,6 +406,7 @@ impl Dist {
url: VerbatimUrl,
install_path: &Path,
editable: bool,
r#virtual: bool,
) -> Result<Dist, Error> {
// Convert to an absolute path.
let install_path = path::absolute(install_path)?;
@ -421,6 +424,7 @@ impl Dist {
name,
install_path,
editable,
r#virtual,
url,
})))
}
@ -458,6 +462,7 @@ impl Dist {
url.verbatim,
&directory.install_path,
directory.editable,
directory.r#virtual,
),
ParsedUrl::Git(git) => {
Self::from_git_url(name, url.verbatim, git.url, git.subdirectory)

View File

@ -220,6 +220,7 @@ impl From<&ResolvedDist> for Requirement {
install_path: sdist.install_path.clone(),
url: sdist.url.clone(),
editable: sdist.editable,
r#virtual: sdist.r#virtual,
},
},
ResolvedDist::Installed(dist) => RequirementSource::Registry {

View File

@ -72,6 +72,7 @@ impl UnnamedRequirementUrl for VerbatimParsedUrl {
url: verbatim.to_url(),
install_path: verbatim.as_path()?,
editable: false,
r#virtual: false,
})
} else {
ParsedUrl::Path(ParsedPathUrl {
@ -101,6 +102,7 @@ impl UnnamedRequirementUrl for VerbatimParsedUrl {
url: verbatim.to_url(),
install_path: verbatim.as_path()?,
editable: false,
r#virtual: false,
})
} else {
ParsedUrl::Path(ParsedPathUrl {
@ -208,15 +210,17 @@ pub struct ParsedDirectoryUrl {
/// The absolute path to the distribution which we use for installing.
pub install_path: PathBuf,
pub editable: bool,
pub r#virtual: bool,
}
impl ParsedDirectoryUrl {
/// Construct a [`ParsedDirectoryUrl`] from a path requirement source.
pub fn from_source(install_path: PathBuf, editable: bool, url: Url) -> Self {
pub fn from_source(install_path: PathBuf, editable: bool, r#virtual: bool, url: Url) -> Self {
Self {
url,
install_path,
editable,
r#virtual,
}
}
}
@ -370,6 +374,7 @@ impl TryFrom<Url> for ParsedUrl {
url,
install_path: path.clone(),
editable: false,
r#virtual: false,
}))
} else {
Ok(Self::Path(ParsedPathUrl {

View File

@ -197,12 +197,14 @@ impl From<Requirement> for pep508_rs::Requirement<VerbatimParsedUrl> {
RequirementSource::Directory {
install_path,
editable,
r#virtual,
url,
} => Some(VersionOrUrl::Url(VerbatimParsedUrl {
parsed_url: ParsedUrl::Directory(ParsedDirectoryUrl {
url: url.to_url(),
install_path,
editable,
r#virtual,
}),
verbatim: url,
})),
@ -360,6 +362,8 @@ pub enum RequirementSource {
install_path: PathBuf,
/// For a source tree (a directory), whether to install as an editable.
editable: bool,
/// For a source tree (a directory), whether the project should be built and installed.
r#virtual: bool,
/// The PEP 508 style URL in the format
/// `file:///<path>#subdirectory=<subdirectory>`.
url: VerbatimUrl,
@ -379,6 +383,7 @@ impl RequirementSource {
ParsedUrl::Directory(directory) => RequirementSource::Directory {
install_path: directory.install_path.clone(),
editable: directory.editable,
r#virtual: directory.r#virtual,
url,
},
ParsedUrl::Git(git) => RequirementSource::Git {
@ -435,11 +440,13 @@ impl RequirementSource {
Self::Directory {
install_path,
editable,
r#virtual,
url,
} => Some(VerbatimParsedUrl {
parsed_url: ParsedUrl::Directory(ParsedDirectoryUrl::from_source(
install_path.clone(),
*editable,
*r#virtual,
url.to_url(),
)),
verbatim: url.clone(),
@ -515,11 +522,14 @@ impl RequirementSource {
RequirementSource::Directory {
install_path,
editable,
r#virtual,
url,
..
} => Ok(Self::Directory {
install_path: relative_to(&install_path, path)
.or_else(|_| std::path::absolute(install_path))?,
editable,
r#virtual,
url,
}),
}
@ -582,6 +592,8 @@ enum RequirementSourceWire {
Directory { directory: PortablePathBuf },
/// Ex) `source = { editable = "/home/ferris/iniconfig" }`
Editable { editable: PortablePathBuf },
/// Ex) `source = { editable = "/home/ferris/iniconfig" }`
Virtual { r#virtual: PortablePathBuf },
/// Ex) `source = { specifier = "foo >1,<2" }`
Registry {
#[serde(skip_serializing_if = "VersionSpecifiers::is_empty", default)]
@ -668,12 +680,17 @@ impl From<RequirementSource> for RequirementSourceWire {
RequirementSource::Directory {
install_path,
editable,
r#virtual,
url: _,
} => {
if editable {
Self::Editable {
editable: PortablePathBuf::from(install_path),
}
} else if r#virtual {
Self::Virtual {
r#virtual: PortablePathBuf::from(install_path),
}
} else {
Self::Directory {
directory: PortablePathBuf::from(install_path),
@ -760,6 +777,7 @@ impl TryFrom<RequirementSourceWire> for RequirementSource {
Ok(Self::Directory {
install_path: directory,
editable: false,
r#virtual: false,
url,
})
}
@ -769,6 +787,17 @@ impl TryFrom<RequirementSourceWire> for RequirementSource {
Ok(Self::Directory {
install_path: editable,
editable: true,
r#virtual: false,
url,
})
}
RequirementSourceWire::Virtual { r#virtual } => {
let r#virtual = PathBuf::from(r#virtual);
let url = VerbatimUrl::from_path(&r#virtual, &*CWD)?;
Ok(Self::Directory {
install_path: r#virtual,
editable: false,
r#virtual: true,
url,
})
}
@ -825,6 +854,7 @@ mod tests {
source: RequirementSource::Directory {
install_path: PathBuf::from(path),
editable: false,
r#virtual: false,
url: VerbatimUrl::from_absolute_path(path).unwrap(),
},
origin: None,

View File

@ -1861,6 +1861,7 @@ mod test {
},
install_path: "/foo/bar",
editable: true,
virtual: false,
},
),
verbatim: VerbatimUrl {

View File

@ -23,6 +23,7 @@ RequirementsTxt {
},
install_path: "<REQUIREMENTS_DIR>/scripts/packages/black_editable",
editable: false,
virtual: false,
},
),
verbatim: VerbatimUrl {
@ -72,6 +73,7 @@ RequirementsTxt {
},
install_path: "<REQUIREMENTS_DIR>/scripts/packages/black_editable",
editable: false,
virtual: false,
},
),
verbatim: VerbatimUrl {
@ -125,6 +127,7 @@ RequirementsTxt {
},
install_path: "/scripts/packages/black_editable",
editable: false,
virtual: false,
},
),
verbatim: VerbatimUrl {

View File

@ -25,6 +25,7 @@ RequirementsTxt {
},
install_path: "<REQUIREMENTS_DIR>/editable",
editable: true,
virtual: false,
},
),
verbatim: VerbatimUrl {
@ -81,6 +82,7 @@ RequirementsTxt {
},
install_path: "<REQUIREMENTS_DIR>/editable",
editable: true,
virtual: false,
},
),
verbatim: VerbatimUrl {
@ -137,6 +139,7 @@ RequirementsTxt {
},
install_path: "<REQUIREMENTS_DIR>/editable",
editable: true,
virtual: false,
},
),
verbatim: VerbatimUrl {
@ -193,6 +196,7 @@ RequirementsTxt {
},
install_path: "<REQUIREMENTS_DIR>/editable",
editable: true,
virtual: false,
},
),
verbatim: VerbatimUrl {
@ -249,6 +253,7 @@ RequirementsTxt {
},
install_path: "<REQUIREMENTS_DIR>/editable",
editable: true,
virtual: false,
},
),
verbatim: VerbatimUrl {
@ -298,6 +303,7 @@ RequirementsTxt {
},
install_path: "<REQUIREMENTS_DIR>/editable[d",
editable: true,
virtual: false,
},
),
verbatim: VerbatimUrl {

View File

@ -23,6 +23,7 @@ RequirementsTxt {
},
install_path: "<REQUIREMENTS_DIR>/scripts/packages/black_editable",
editable: false,
virtual: false,
},
),
verbatim: VerbatimUrl {
@ -72,6 +73,7 @@ RequirementsTxt {
},
install_path: "<REQUIREMENTS_DIR>/scripts/packages/black_editable",
editable: false,
virtual: false,
},
),
verbatim: VerbatimUrl {
@ -125,6 +127,7 @@ RequirementsTxt {
},
install_path: "<REQUIREMENTS_DIR>/scripts/packages/black_editable",
editable: false,
virtual: false,
},
),
verbatim: VerbatimUrl {

View File

@ -25,6 +25,7 @@ RequirementsTxt {
},
install_path: "<REQUIREMENTS_DIR>/editable",
editable: true,
virtual: false,
},
),
verbatim: VerbatimUrl {
@ -81,6 +82,7 @@ RequirementsTxt {
},
install_path: "<REQUIREMENTS_DIR>/editable",
editable: true,
virtual: false,
},
),
verbatim: VerbatimUrl {
@ -137,6 +139,7 @@ RequirementsTxt {
},
install_path: "<REQUIREMENTS_DIR>/editable",
editable: true,
virtual: false,
},
),
verbatim: VerbatimUrl {
@ -193,6 +196,7 @@ RequirementsTxt {
},
install_path: "<REQUIREMENTS_DIR>/editable",
editable: true,
virtual: false,
},
),
verbatim: VerbatimUrl {
@ -249,6 +253,7 @@ RequirementsTxt {
},
install_path: "<REQUIREMENTS_DIR>/editable",
editable: true,
virtual: false,
},
),
verbatim: VerbatimUrl {
@ -298,6 +303,7 @@ RequirementsTxt {
},
install_path: "<REQUIREMENTS_DIR>/editable[d",
editable: true,
virtual: false,
},
),
verbatim: VerbatimUrl {

View File

@ -55,6 +55,7 @@ impl CachedWheel {
url,
path: self.entry.into_path_buf(),
editable: false,
r#virtual: false,
hashes: self.hashes,
}
}
@ -66,6 +67,19 @@ impl CachedWheel {
url,
path: self.entry.into_path_buf(),
editable: true,
r#virtual: false,
hashes: self.hashes,
}
}
/// Convert a [`CachedWheel`] into an editable [`CachedDirectUrlDist`].
pub fn into_virtual(self, url: VerbatimUrl) -> CachedDirectUrlDist {
CachedDirectUrlDist {
filename: self.filename,
url,
path: self.entry.into_path_buf(),
editable: false,
r#virtual: true,
hashes: self.hashes,
}
}

View File

@ -12,7 +12,7 @@ use pypi_types::{ParsedUrlError, Requirement, RequirementSource, VerbatimParsedU
use uv_git::GitReference;
use uv_normalize::PackageName;
use uv_warnings::warn_user_once;
use uv_workspace::pyproject::Source;
use uv_workspace::pyproject::{PyProjectToml, Source};
use uv_workspace::Workspace;
#[derive(Debug, Clone)]
@ -148,10 +148,21 @@ impl LoweredRequirement {
"Invalid path in file URL",
))
})?;
RequirementSource::Directory {
install_path,
url,
editable: true,
if member.pyproject_toml().is_package() {
RequirementSource::Directory {
install_path,
url,
editable: true,
r#virtual: false,
}
} else {
RequirementSource::Directory {
install_path,
url,
editable: false,
r#virtual: true,
}
}
}
Source::CatchAll { .. } => {
@ -372,17 +383,41 @@ fn path_source(
"Invalid path in file URL",
))
})?;
let is_dir = if let Ok(metadata) = install_path.metadata() {
metadata.is_dir()
} else {
install_path.extension().is_none()
};
if is_dir {
Ok(RequirementSource::Directory {
install_path,
url,
editable,
})
if editable {
Ok(RequirementSource::Directory {
install_path,
url,
editable,
r#virtual: false,
})
} else {
// Determine whether the project is a package or virtual.
let is_package = {
let pyproject_path = install_path.join("pyproject.toml");
fs_err::read_to_string(&pyproject_path)
.ok()
.and_then(|contents| PyProjectToml::from_string(contents).ok())
.map(|pyproject_toml| pyproject_toml.is_package())
.unwrap_or(true)
};
// If a project is not a package, treat it as a virtual dependency.
let r#virtual = !is_package;
Ok(RequirementSource::Directory {
install_path,
url,
editable,
r#virtual,
})
}
} else {
if editable {
return Err(LoweringError::EditableFile(url.to_string()));

View File

@ -264,6 +264,7 @@ impl<'a> Planner<'a> {
}
RequirementSource::Directory {
r#virtual,
url,
editable,
install_path,
@ -284,6 +285,7 @@ impl<'a> Planner<'a> {
url: url.clone(),
install_path,
editable: *editable,
r#virtual: *r#virtual,
};
// Find the most-compatible wheel from the cache, since we don't know

View File

@ -197,6 +197,7 @@ impl RequirementSatisfaction {
RequirementSource::Directory {
install_path: requested_path,
editable: requested_editable,
r#virtual: _,
url: _,
} => {
let InstalledDist::Url(InstalledDirectUrlDist { direct_url, .. }) = &distribution

View File

@ -288,6 +288,7 @@ fn required_dist(requirement: &Requirement) -> Result<Option<Dist>, distribution
} => Dist::from_file_url(requirement.name.clone(), url.clone(), install_path, *ext)?,
RequirementSource::Directory {
install_path,
r#virtual,
url,
editable,
} => Dist::from_directory_url(
@ -295,6 +296,7 @@ fn required_dist(requirement: &Requirement) -> Result<Option<Dist>, distribution
url.clone(),
install_path,
*editable,
*r#virtual,
)?,
}))
}

View File

@ -1394,6 +1394,11 @@ impl Package {
source_type: "editable",
}
.into()),
Source::Virtual(_) => Err(LockErrorKind::InvalidWheelSource {
id: self.id.clone(),
source_type: "virtual",
}
.into()),
};
};
@ -1431,6 +1436,7 @@ impl Package {
url: verbatim_url(workspace_root.join(path), &self.id)?,
install_path: workspace_root.join(path),
editable: false,
r#virtual: false,
};
distribution_types::SourceDist::Directory(dir_dist)
}
@ -1440,6 +1446,17 @@ impl Package {
url: verbatim_url(workspace_root.join(path), &self.id)?,
install_path: workspace_root.join(path),
editable: true,
r#virtual: false,
};
distribution_types::SourceDist::Directory(dir_dist)
}
Source::Virtual(path) => {
let dir_dist = DirectorySourceDist {
name: self.id.name.clone(),
url: verbatim_url(workspace_root.join(path), &self.id)?,
install_path: workspace_root.join(path),
editable: false,
r#virtual: true,
};
distribution_types::SourceDist::Directory(dir_dist)
}
@ -1985,6 +2002,8 @@ enum Source {
Directory(PathBuf),
/// A path to a local directory that should be installed as editable.
Editable(PathBuf),
/// A path to a local directory that should not be built or installed.
Virtual(PathBuf),
}
impl Source {
@ -2093,6 +2112,8 @@ impl Source {
.map_err(LockErrorKind::DistributionRelativePath)?;
if directory_dist.editable {
Ok(Source::Editable(path))
} else if directory_dist.r#virtual {
Ok(Source::Virtual(path))
} else {
Ok(Source::Directory(path))
}
@ -2182,6 +2203,9 @@ impl Source {
Value::from(PortablePath::from(path).to_string()),
);
}
Source::Virtual(ref path) => {
source_table.insert("virtual", Value::from(PortablePath::from(path).to_string()));
}
}
table.insert("source", value(source_table));
}
@ -2198,7 +2222,8 @@ impl std::fmt::Display for Source {
Source::Registry(RegistrySource::Path(path))
| Source::Path(path)
| Source::Directory(path)
| Source::Editable(path) => {
| Source::Editable(path)
| Source::Virtual(path) => {
write!(f, "{}+{}", self.name(), PortablePath::from(path))
}
}
@ -2214,6 +2239,7 @@ impl Source {
Self::Path(..) => "path",
Self::Directory(..) => "directory",
Self::Editable(..) => "editable",
Self::Virtual(..) => "virtual",
}
}
@ -2228,7 +2254,9 @@ impl Source {
match *self {
Self::Registry(..) => None,
Self::Direct(..) | Self::Path(..) => Some(true),
Self::Git(..) | Self::Directory(..) | Self::Editable(..) => Some(false),
Self::Git(..) | Self::Directory(..) | Self::Editable(..) | Self::Virtual(..) => {
Some(false)
}
}
}
}
@ -2256,6 +2284,9 @@ enum SourceWire {
Editable {
editable: PortablePathBuf,
},
Virtual {
r#virtual: PortablePathBuf,
},
}
impl TryFrom<SourceWire> for Source {
@ -2292,6 +2323,7 @@ impl TryFrom<SourceWire> for Source {
Path { path } => Ok(Source::Path(path.into())),
Directory { directory } => Ok(Source::Directory(directory.into())),
Editable { editable } => Ok(Source::Editable(editable.into())),
Virtual { r#virtual } => Ok(Source::Virtual(r#virtual.into())),
}
}
}
@ -3280,6 +3312,7 @@ fn normalize_requirement(
RequirementSource::Directory {
install_path,
editable,
r#virtual,
url: _,
} => {
let install_path = uv_fs::normalize_path(&workspace.install_path().join(&install_path));
@ -3293,6 +3326,7 @@ fn normalize_requirement(
source: RequirementSource::Directory {
install_path,
editable,
r#virtual,
url,
},
origin: None,

View File

@ -145,12 +145,14 @@ impl PubGrubRequirement {
}
RequirementSource::Directory {
editable,
r#virtual,
url,
install_path,
} => {
let parsed_url = ParsedUrl::Directory(ParsedDirectoryUrl::from_source(
install_path.clone(),
*editable,
*r#virtual,
url.to_url(),
));
(url, parsed_url)

View File

@ -281,10 +281,20 @@ impl Workspace {
name: project.name.clone(),
extras,
marker: MarkerTree::TRUE,
source: RequirementSource::Directory {
install_path: member.root.clone(),
editable: true,
url,
source: if member.pyproject_toml.is_package() {
RequirementSource::Directory {
install_path: member.root.clone(),
editable: true,
r#virtual: false,
url,
}
} else {
RequirementSource::Directory {
install_path: member.root.clone(),
editable: false,
r#virtual: true,
url,
}
},
origin: None,
})

View File

@ -1,8 +1,7 @@
use anyhow::{Context, Result};
use itertools::Itertools;
use rustc_hash::FxHashSet;
use distribution_types::Name;
use distribution_types::{Dist, ResolvedDist, SourceDist};
use pep508_rs::MarkerTree;
use uv_auth::store_credentials_from_url;
use uv_cache::Cache;
@ -199,7 +198,7 @@ pub(super) async fn do_sync(
let resolution = lock.to_resolution(project, &markers, tags, extras, &dev)?;
// Always skip virtual projects, which shouldn't be built or installed.
let resolution = apply_no_virtual_project(resolution, project);
let resolution = apply_no_virtual_project(resolution);
// Filter resolution based on install-specific options.
let resolution = install_options.filter_resolution(resolution, project);
@ -299,28 +298,20 @@ pub(super) async fn do_sync(
/// Filter out any virtual workspace members.
fn apply_no_virtual_project(
resolution: distribution_types::Resolution,
project: &VirtualProject,
) -> distribution_types::Resolution {
let VirtualProject::Project(project) = project else {
// If the project is _only_ a virtual workspace root, we don't need to filter it out.
return resolution;
};
resolution.filter(|dist| {
let ResolvedDist::Installable(dist) = dist else {
return true;
};
let virtual_members = project
.workspace()
.packages()
.iter()
.filter_map(|(name, package)| {
// A project is a package if it's explicitly marked as such, _or_ if a build system is
// present.
if package.pyproject_toml().is_package() {
None
} else {
Some(name)
}
})
.collect::<FxHashSet<_>>();
let Dist::Source(dist) = dist else {
return true;
};
// Remove any virtual members from the resolution.
resolution.filter(|dist| !virtual_members.contains(dist.name()))
let SourceDist::Directory(dist) = dist else {
return true;
};
!dist.r#virtual
})
}

View File

@ -3853,6 +3853,10 @@ fn lock_python_version_marker_complement() -> Result<()> {
"typing-extensions ; python_full_version > '3.10'",
"typing-extensions ; python_full_version <= '3.10'",
]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#,
)?;
@ -5206,6 +5210,10 @@ fn lock_same_version_multiple_urls() -> Result<()> {
"dependency @ {} ; sys_platform == 'darwin'",
"dependency @ {} ; sys_platform != 'darwin'",
]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#,
Url::from_file_path(context.temp_dir.join("v1")).unwrap(),
Url::from_file_path(context.temp_dir.join("v2")).unwrap(),
@ -7754,6 +7762,10 @@ fn lock_editable() -> Result<()> {
name = "library"
version = "0.1.0"
dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?;
library.child("src/__init__.py").touch()?;
@ -8817,7 +8829,7 @@ fn lock_remove_member() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
"###
);
});
@ -11349,7 +11361,7 @@ fn lock_explicit_virtual_project() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "black" },
]
@ -11421,7 +11433,7 @@ fn lock_explicit_virtual_project() -> Result<()> {
Ok(())
}
/// Lock a project that is implicitly virtual (by way of omitting `build-system`0>
/// Lock a project that is implicitly virtual (by way of omitting `build-system`).
#[test]
fn lock_implicit_virtual_project() -> Result<()> {
let context = TestContext::new("3.12");
@ -11566,7 +11578,7 @@ fn lock_implicit_virtual_project() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "black" },
]
@ -11637,3 +11649,164 @@ fn lock_implicit_virtual_project() -> Result<()> {
Ok(())
}
/// Lock a project that has a path dependency that is implicitly virtual (by way of omitting
/// `build-system`).
#[test]
fn lock_implicit_virtual_path() -> Result<()> {
let context = TestContext::new("3.12");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["anyio >3", "child"]
[tool.uv.sources]
child = { path = "./child" }
"#,
)?;
let child = context.temp_dir.child("child");
child.child("pyproject.toml").write_str(
r#"
[project]
name = "child"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["iniconfig >1"]
"#,
)?;
uv_snapshot!(context.filters(), context.lock(), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 6 packages in [TIME]
"###);
let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock")).unwrap();
insta::with_settings!({
filters => context.filters(),
}, {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12"
[options]
exclude-newer = "2024-03-25T00:00:00Z"
[[package]]
name = "anyio"
version = "4.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "idna" },
{ name = "sniffio" },
]
sdist = { url = "https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6", size = 159642 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/14/fd/2f20c40b45e4fb4324834aea24bd4afdf1143390242c0b33774da0e2e34f/anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8", size = 85584 },
]
[[package]]
name = "child"
version = "0.1.0"
source = { virtual = "child" }
dependencies = [
{ name = "iniconfig" },
]
[package.metadata]
requires-dist = [{ name = "iniconfig", specifier = ">1" }]
[[package]]
name = "idna"
version = "3.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 },
]
[[package]]
name = "iniconfig"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
]
[[package]]
name = "project"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "anyio" },
{ name = "child" },
]
[package.metadata]
requires-dist = [
{ name = "anyio", specifier = ">3" },
{ name = "child", virtual = "child" },
]
[[package]]
name = "sniffio"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
]
"###
);
});
// Re-run with `--locked`.
uv_snapshot!(context.filters(), context.lock().arg("--locked"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 6 packages in [TIME]
"###);
// Re-run with `--offline`. We shouldn't need a network connection to validate an
// already-correct lockfile with immutable metadata.
uv_snapshot!(context.filters(), context.lock().arg("--locked").arg("--offline").arg("--no-cache"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 6 packages in [TIME]
"###);
// Install from the lockfile. The virtual project should _not_ be installed.
uv_snapshot!(context.filters(), context.sync().arg("--frozen"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Prepared 4 packages in [TIME]
Installed 4 packages in [TIME]
+ anyio==4.3.0
+ idna==3.6
+ iniconfig==2.0.0
+ sniffio==1.3.1
"###);
Ok(())
}

View File

@ -103,7 +103,7 @@ fn fork_allows_non_conflicting_non_overlapping_dependencies() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
]
@ -215,7 +215,7 @@ fn fork_allows_non_conflicting_repeated_dependencies() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a" },
]
@ -334,7 +334,7 @@ fn fork_basic() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" },
{ name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" },
@ -667,7 +667,7 @@ fn fork_filter_sibling_dependencies() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a", version = "4.3.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" },
{ name = "package-a", version = "4.4.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" },
@ -789,7 +789,7 @@ fn fork_upgrade() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-foo" },
]
@ -938,7 +938,7 @@ fn fork_incomplete_markers() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "python_full_version < '3.10'" },
{ name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "python_full_version >= '3.11'" },
@ -1074,7 +1074,7 @@ fn fork_marker_accrue() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a", marker = "implementation_name == 'cpython'" },
{ name = "package-b", marker = "implementation_name == 'pypy'" },
@ -1317,7 +1317,7 @@ fn fork_marker_inherit_combined_allowed() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" },
{ name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" },
@ -1485,7 +1485,7 @@ fn fork_marker_inherit_combined_disallowed() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" },
{ name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" },
@ -1654,7 +1654,7 @@ fn fork_marker_inherit_combined() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" },
{ name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" },
@ -1796,7 +1796,7 @@ fn fork_marker_inherit_isolated() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" },
{ name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" },
@ -1956,7 +1956,7 @@ fn fork_marker_inherit_transitive() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" },
{ name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" },
@ -2088,7 +2088,7 @@ fn fork_marker_inherit() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" },
{ name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" },
@ -2247,7 +2247,7 @@ fn fork_marker_limited_inherit() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" },
{ name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" },
@ -2390,7 +2390,7 @@ fn fork_marker_selection() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a" },
{ name = "package-b", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" },
@ -2557,7 +2557,7 @@ fn fork_marker_track() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a" },
{ name = "package-b", version = "2.7", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" },
@ -2692,7 +2692,7 @@ fn fork_non_fork_marker_transitive() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a" },
{ name = "package-b" },
@ -2972,7 +2972,7 @@ fn fork_overlapping_markers_basic() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a" },
]
@ -3216,7 +3216,7 @@ fn preferences_dependent_forking_bistable() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-cleaver" },
]
@ -3635,7 +3635,7 @@ fn preferences_dependent_forking_tristable() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-bar", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform != 'linux'" },
{ name = "package-bar", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" },
@ -3836,7 +3836,7 @@ fn preferences_dependent_forking() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-bar", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform != 'linux'" },
{ name = "package-bar", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" },
@ -4021,7 +4021,7 @@ fn fork_remaining_universe_partitioning() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'illumos'" },
{ name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'windows'" },
@ -4112,7 +4112,7 @@ fn fork_requires_python_full_prerelease() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
[package.metadata]
requires-dist = [{ name = "package-a", marker = "python_full_version == '3.9'", specifier = "==1.0.0" }]
@ -4196,7 +4196,7 @@ fn fork_requires_python_full() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
[package.metadata]
requires-dist = [{ name = "package-a", marker = "python_full_version == '3.9'", specifier = "==1.0.0" }]
@ -4293,7 +4293,7 @@ fn fork_requires_python_patch_overlap() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "package-a", marker = "python_full_version < '3.11'" },
]
@ -4377,7 +4377,7 @@ fn fork_requires_python() -> Result<()> {
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
[package.metadata]
requires-dist = [{ name = "package-a", marker = "python_full_version == '3.9.*'", specifier = "==1.0.0" }]

View File

@ -162,7 +162,7 @@ wheels = [
[[package]]
name = "uv-lock-instability"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "jax" },
]

View File

@ -466,7 +466,7 @@ fn jax_instability() -> Result<()> {
+[[package]]
name = "uv-lock-instability"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "jax" },
+ { name = "tzdata" },
@ -519,7 +519,7 @@ fn jax_instability() -> Result<()> {
-[[package]]
name = "uv-lock-instability"
version = "0.1.0"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "jax" },
- { name = "tzdata" },