Treat path sources as package = true by default

This commit is contained in:
John Mumm 2025-06-24 11:32:48 +02:00
parent aa2448ef83
commit 6dd339c6c3
No known key found for this signature in database
GPG key ID: 73D2271AFDC26EA8
3 changed files with 25 additions and 93 deletions

View file

@ -16,7 +16,7 @@ use uv_pep508::{MarkerTree, VerbatimUrl, VersionOrUrl, looks_like_git_repository
use uv_pypi_types::{ConflictItem, ParsedUrlError, VerbatimParsedUrl};
use uv_redacted::DisplaySafeUrl;
use uv_workspace::Workspace;
use uv_workspace::pyproject::{PyProjectToml, Source, Sources};
use uv_workspace::pyproject::{Source, Sources};
use crate::metadata::GitWorkspaceMember;
@ -723,14 +723,8 @@ fn path_source(
})
} else {
// Determine whether the project is a package or virtual.
let is_package = package.unwrap_or_else(|| {
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)
});
// Default to `package = true` for path sources.
let is_package = package.unwrap_or(true);
Ok(RequirementSource::Directory {
install_path: install_path.into_boxed_path(),

View file

@ -7139,12 +7139,12 @@ fn lock_exclusion() -> Result<()> {
]
[package.metadata]
requires-dist = [{ name = "project", virtual = "../" }]
requires-dist = [{ name = "project", directory = "../" }]
[[package]]
name = "project"
version = "0.1.0"
source = { virtual = "../" }
source = { directory = "../" }
"#
);
});
@ -7727,7 +7727,7 @@ fn lock_dev_transitive() -> Result<()> {
[package.metadata]
requires-dist = [
{ name = "baz", editable = "baz" },
{ name = "foo", virtual = "../foo" },
{ name = "foo", directory = "../foo" },
{ name = "iniconfig", specifier = ">1" },
]
@ -7749,7 +7749,7 @@ fn lock_dev_transitive() -> Result<()> {
[[package]]
name = "foo"
version = "0.1.0"
source = { virtual = "../foo" }
source = { directory = "../foo" }
[package.metadata]
@ -13566,7 +13566,7 @@ fn lock_narrowed_python_version_upper() -> Result<()> {
[[package]]
name = "dependency"
version = "0.1.0"
source = { virtual = "dependency" }
source = { directory = "dependency" }
dependencies = [
{ name = "iniconfig", marker = "python_full_version >= '3.10'" },
]
@ -13592,7 +13592,7 @@ fn lock_narrowed_python_version_upper() -> Result<()> {
]
[package.metadata]
requires-dist = [{ name = "dependency", marker = "python_full_version >= '3.10'", virtual = "dependency" }]
requires-dist = [{ name = "dependency", marker = "python_full_version >= '3.10'", directory = "dependency" }]
"#
);
});
@ -16870,9 +16870,9 @@ fn lock_explicit_virtual_project() -> Result<()> {
Ok(())
}
/// Lock a project that is implicitly virtual (by way of omitting `build-system`).
/// Lock a project with a dev dependency
#[test]
fn lock_implicit_virtual_project() -> Result<()> {
fn lock_project_dev_dependency() -> Result<()> {
let context = TestContext::new("3.12");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
@ -17088,10 +17088,9 @@ 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`).
/// Lock a project that has a path dependency
#[test]
fn lock_implicit_virtual_path() -> Result<()> {
fn lock_path_dependency() -> Result<()> {
let context = TestContext::new("3.12");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
@ -17158,7 +17157,7 @@ fn lock_implicit_virtual_path() -> Result<()> {
[[package]]
name = "child"
version = "0.1.0"
source = { virtual = "child" }
source = { directory = "child" }
dependencies = [
{ name = "iniconfig" },
]
@ -17196,7 +17195,7 @@ fn lock_implicit_virtual_path() -> Result<()> {
[package.metadata]
requires-dist = [
{ name = "anyio", specifier = ">3" },
{ name = "child", virtual = "child" },
{ name = "child", directory = "child" },
]
[[package]]
@ -17233,19 +17232,20 @@ fn lock_implicit_virtual_path() -> Result<()> {
"###);
// Install from the lockfile. The virtual project should _not_ be installed.
uv_snapshot!(context.filters(), context.sync().arg("--frozen"), @r###"
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]
Prepared 5 packages in [TIME]
Installed 5 packages in [TIME]
+ anyio==4.3.0
+ child==0.1.0 (from file://[TEMP_DIR]/child)
+ idna==3.6
+ iniconfig==2.0.0
+ sniffio==1.3.1
"###);
");
Ok(())
}

View file

@ -5399,7 +5399,8 @@ fn sync_override_package() -> Result<()> {
.child("__init__.py")
.touch()?;
// Create a package that depends on it.
// Create a package that depends on it. Mark the source with
// `package = false`.
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
@ -5414,7 +5415,7 @@ fn sync_override_package() -> Result<()> {
build-backend = "hatchling.build"
[tool.uv.sources]
core = { path = "./core" }
core = { path = "./core", package = false }
"#,
)?;
@ -5438,7 +5439,7 @@ fn sync_override_package() -> Result<()> {
+ project==0.0.0 (from file://[TEMP_DIR]/)
"###);
// Mark the source as `package = true`.
// Remove `package = false` from the source.
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
@ -5453,7 +5454,7 @@ fn sync_override_package() -> Result<()> {
build-backend = "hatchling.build"
[tool.uv.sources]
core = { path = "./core", package = true }
core = { path = "./core" }
"#,
)?;
@ -5472,69 +5473,6 @@ fn sync_override_package() -> Result<()> {
~ project==0.0.0 (from file://[TEMP_DIR]/)
"###);
// Remove `package = false`.
let pyproject_toml = context.temp_dir.child("core").child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "core"
version = "0.1.0"
requires-python = ">=3.12"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
"#,
)?;
// Syncing the project _should_ install `core`.
uv_snapshot!(context.filters(), context.sync(), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 2 packages in [TIME]
Prepared 1 package in [TIME]
Uninstalled 1 package in [TIME]
Installed 1 package in [TIME]
~ core==0.1.0 (from file://[TEMP_DIR]/core)
"###);
// Mark the source as `package = false`.
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "project"
version = "0.0.0"
requires-python = ">=3.12"
dependencies = ["core"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.uv.sources]
core = { path = "./core", package = false }
"#,
)?;
// Syncing the project should _not_ install `core`.
uv_snapshot!(context.filters(), context.sync(), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 2 packages in [TIME]
Prepared 1 package in [TIME]
Uninstalled 2 packages in [TIME]
Installed 1 package in [TIME]
- core==0.1.0 (from file://[TEMP_DIR]/core)
~ project==0.0.0 (from file://[TEMP_DIR]/)
"###);
Ok(())
}