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

View file

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

View file

@ -5399,7 +5399,8 @@ fn sync_override_package() -> Result<()> {
.child("__init__.py") .child("__init__.py")
.touch()?; .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"); let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str( pyproject_toml.write_str(
r#" r#"
@ -5414,7 +5415,7 @@ fn sync_override_package() -> Result<()> {
build-backend = "hatchling.build" build-backend = "hatchling.build"
[tool.uv.sources] [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]/) + 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"); let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str( pyproject_toml.write_str(
r#" r#"
@ -5453,7 +5454,7 @@ fn sync_override_package() -> Result<()> {
build-backend = "hatchling.build" build-backend = "hatchling.build"
[tool.uv.sources] [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]/) ~ 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(()) Ok(())
} }