This commit is contained in:
konstin 2025-05-27 23:08:26 +02:00
parent 8bcf72a158
commit 058d97b4c1
2 changed files with 100 additions and 8 deletions

View file

@ -25,22 +25,28 @@ pub struct Locks {
impl Locks {
/// Warn when a module exists in multiple packages.
fn warn_module_conflict(&self, module: &OsStr, filename: &WheelFilename) {
if let Some(existing) = self
fn warn_module_conflict(&self, module: &OsStr, wheel_a: &WheelFilename) {
if let Some(wheel_b) = self
.modules
.lock()
.unwrap()
.insert(module.to_os_string(), filename.clone())
.insert(module.to_os_string(), wheel_a.clone())
{
// Sort for consistent output, at least with two packages
let (wheel_a, wheel_b) = if wheel_b.name > wheel_a.name {
(&wheel_b, wheel_a)
} else {
(wheel_a, &wheel_b)
};
warn_user!(
"The module {} exists in two packages! \
This leads to a race condition and likely to a broken installation. \
Consider removing either {} ({}) or {} ({}).",
module.simplified_display(),
existing.name,
existing,
filename.name,
filename,
wheel_a.name,
wheel_a,
wheel_b.name,
wheel_b,
);
}
}
@ -548,7 +554,7 @@ fn synchronized_copy(from: &Path, to: &Path, locks: &Locks) -> std::io::Result<(
/// Warn when a module exists in multiple packages.
fn warn_module_conflict(locks: &Locks, filename: &WheelFilename, relative: &Path) {
// Check for `__init__.py` to account for namespace packages.
// TODO(konsti): We need to handle namespace packages, too.
// TODO(konsti): We need to warn for overlapping namespace packages, too.
if relative.components().count() == 2
&& relative.components().next_back().unwrap().as_os_str() == "__init__.py"
{

View file

@ -11350,3 +11350,89 @@ fn pep_751_dependency() -> Result<()> {
Ok(())
}
/// Check that we warn for overlapping packages but not for correctly overlapping namespace
/// packages.
#[test]
fn overlapping_packages_warning() -> Result<()> {
let context = TestContext::new("3.12");
context
.build()
.arg("--preview")
.arg(context.workspace_root.join("scripts/packages/built-by-uv"))
.arg("--out-dir")
.arg(context.temp_dir.path())
.assert()
.success();
// Overlaps with `built-by-uv`
let also_build_by_uv = context.temp_dir.child("also-built-by-uv");
also_build_by_uv.child("pyproject.toml").write_str(
r#"
[project]
name = "also-built-by-uv"
version = "0.1.0"
requires-python = ">=3.12"
[tool.uv.build-backend]
module-name = "built_by_uv"
[build-system]
requires = ["uv_build>=0.7,<10000"]
build-backend = "uv_build"
"#,
)?;
also_build_by_uv
.child("src")
.child("built_by_uv")
.child("__init__.py")
.touch()?;
context
.build()
.arg("--preview")
.arg(also_build_by_uv.path())
.arg("--out-dir")
.arg(context.temp_dir.path())
.assert()
.success();
// Check that overlapping packages show a warning
uv_snapshot!(context.filters(), context.pip_install()
.arg("--no-deps")
.arg(context.temp_dir.join("built_by_uv-0.1.0-py3-none-any.whl"))
.arg(context.temp_dir.join("also_built_by_uv-0.1.0-py3-none-any.whl")), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 2 packages in [TIME]
Prepared 2 packages in [TIME]
warning: The module built_by_uv exists in two packages! This leads to a race condition and likely to a broken installation. Consider removing either built-by-uv (built_by_uv-0.1.0-py3-none-any.whl) or also-built-by-uv (also_built_by_uv-0.1.0-py3-none-any.whl).
Installed 2 packages in [TIME]
+ also-built-by-uv==0.1.0 (from file://[TEMP_DIR]/also_built_by_uv-0.1.0-py3-none-any.whl)
+ built-by-uv==0.1.0 (from file://[TEMP_DIR]/built_by_uv-0.1.0-py3-none-any.whl)
"
);
// Check that correctly overlapping namespace packages don't show a warning
uv_snapshot!(context.pip_install()
.arg("--no-deps")
.arg("poetry")
.arg("poetry-core"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 2 packages in [TIME]
Prepared 2 packages in [TIME]
Installed 2 packages in [TIME]
+ poetry==1.8.2
+ poetry-core==1.9.0
"
);
Ok(())
}