Move warnings for conflicting modules into preview (#15253)

This commit is contained in:
Zanie Blue 2025-08-13 14:39:09 -05:00 committed by GitHub
parent 2c54d3929c
commit b8049eaa20
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 74 additions and 8 deletions

1
Cargo.lock generated
View File

@ -5443,6 +5443,7 @@ dependencies = [
"thiserror 2.0.12", "thiserror 2.0.12",
"tracing", "tracing",
"uv-cache-info", "uv-cache-info",
"uv-configuration",
"uv-distribution-filename", "uv-distribution-filename",
"uv-fs", "uv-fs",
"uv-normalize", "uv-normalize",

View File

@ -16,6 +16,7 @@ bitflags::bitflags! {
const ADD_BOUNDS = 1 << 4; const ADD_BOUNDS = 1 << 4;
const PACKAGE_CONFLICTS = 1 << 5; const PACKAGE_CONFLICTS = 1 << 5;
const EXTRA_BUILD_DEPENDENCIES = 1 << 6; const EXTRA_BUILD_DEPENDENCIES = 1 << 6;
const DETECT_MODULE_CONFLICTS = 1 << 7;
} }
} }
@ -32,6 +33,7 @@ impl PreviewFeatures {
Self::ADD_BOUNDS => "add-bounds", Self::ADD_BOUNDS => "add-bounds",
Self::PACKAGE_CONFLICTS => "package-conflicts", Self::PACKAGE_CONFLICTS => "package-conflicts",
Self::EXTRA_BUILD_DEPENDENCIES => "extra-build-dependencies", Self::EXTRA_BUILD_DEPENDENCIES => "extra-build-dependencies",
Self::DETECT_MODULE_CONFLICTS => "detect-module-conflicts",
_ => panic!("`flag_as_str` can only be used for exactly one feature flag"), _ => panic!("`flag_as_str` can only be used for exactly one feature flag"),
} }
} }
@ -76,6 +78,7 @@ impl FromStr for PreviewFeatures {
"add-bounds" => Self::ADD_BOUNDS, "add-bounds" => Self::ADD_BOUNDS,
"package-conflicts" => Self::PACKAGE_CONFLICTS, "package-conflicts" => Self::PACKAGE_CONFLICTS,
"extra-build-dependencies" => Self::EXTRA_BUILD_DEPENDENCIES, "extra-build-dependencies" => Self::EXTRA_BUILD_DEPENDENCIES,
"detect-module-conflicts" => Self::DETECT_MODULE_CONFLICTS,
_ => { _ => {
warn_user_once!("Unknown preview feature: `{part}`"); warn_user_once!("Unknown preview feature: `{part}`");
continue; continue;
@ -246,6 +249,10 @@ mod tests {
PreviewFeatures::EXTRA_BUILD_DEPENDENCIES.flag_as_str(), PreviewFeatures::EXTRA_BUILD_DEPENDENCIES.flag_as_str(),
"extra-build-dependencies" "extra-build-dependencies"
); );
assert_eq!(
PreviewFeatures::DETECT_MODULE_CONFLICTS.flag_as_str(),
"detect-module-conflicts"
);
} }
#[test] #[test]

View File

@ -390,7 +390,7 @@ impl BuildContext for BuildDispatch<'_> {
if wheels.len() == 1 { "" } else { "s" }, if wheels.len() == 1 { "" } else { "s" },
wheels.iter().map(ToString::to_string).join(", ") wheels.iter().map(ToString::to_string).join(", ")
); );
wheels = Installer::new(venv) wheels = Installer::new(venv, self.preview)
.with_link_mode(self.link_mode) .with_link_mode(self.link_mode)
.with_cache(self.cache) .with_cache(self.cache)
.install(wheels) .install(wheels)

View File

@ -22,6 +22,7 @@ name = "uv_install_wheel"
[dependencies] [dependencies]
uv-cache-info = { workspace = true } uv-cache-info = { workspace = true }
uv-configuration = { workspace = true }
uv-distribution-filename = { workspace = true } uv-distribution-filename = { workspace = true }
uv-fs = { workspace = true } uv-fs = { workspace = true }
uv-normalize = { workspace = true } uv-normalize = { workspace = true }

View File

@ -10,20 +10,33 @@ use std::sync::{Arc, Mutex};
use std::time::SystemTime; use std::time::SystemTime;
use tempfile::tempdir_in; use tempfile::tempdir_in;
use tracing::{debug, instrument, trace}; use tracing::{debug, instrument, trace};
use uv_configuration::{Preview, PreviewFeatures};
use uv_distribution_filename::WheelFilename; use uv_distribution_filename::WheelFilename;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_warnings::{warn_user, warn_user_once}; use uv_warnings::{warn_user, warn_user_once};
use walkdir::WalkDir; use walkdir::WalkDir;
#[allow(clippy::struct_field_names)]
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Locks { pub struct Locks {
/// The parent directory of a file in a synchronized copy /// The parent directory of a file in a synchronized copy
copy_dir_locks: Mutex<FxHashMap<PathBuf, Arc<Mutex<()>>>>, copy_dir_locks: Mutex<FxHashMap<PathBuf, Arc<Mutex<()>>>>,
/// Top level modules (excluding namespaces) we write to. /// Top level modules (excluding namespaces) we write to.
modules: Mutex<FxHashMap<OsString, WheelFilename>>, modules: Mutex<FxHashMap<OsString, WheelFilename>>,
/// Preview settings for feature flags.
preview: Preview,
} }
impl Locks { impl Locks {
/// Create a new Locks instance with the given preview settings.
pub fn new(preview: Preview) -> Self {
Self {
copy_dir_locks: Mutex::new(FxHashMap::default()),
modules: Mutex::new(FxHashMap::default()),
preview,
}
}
/// Warn when a module exists in multiple packages. /// Warn when a module exists in multiple packages.
fn warn_module_conflict(&self, module: &OsStr, wheel_a: &WheelFilename) { fn warn_module_conflict(&self, module: &OsStr, wheel_a: &WheelFilename) {
if let Some(wheel_b) = self if let Some(wheel_b) = self
@ -32,6 +45,14 @@ impl Locks {
.unwrap() .unwrap()
.insert(module.to_os_string(), wheel_a.clone()) .insert(module.to_os_string(), wheel_a.clone())
{ {
// Only warn if the preview feature is enabled
if !self
.preview
.is_enabled(PreviewFeatures::DETECT_MODULE_CONFLICTS)
{
return;
}
// Sort for consistent output, at least with two packages // Sort for consistent output, at least with two packages
let (wheel_a, wheel_b) = if wheel_b.name > wheel_a.name { let (wheel_a, wheel_b) = if wheel_b.name > wheel_a.name {
(&wheel_b, wheel_a) (&wheel_b, wheel_a)

View File

@ -7,7 +7,7 @@ use tokio::sync::oneshot;
use tracing::instrument; use tracing::instrument;
use uv_cache::Cache; use uv_cache::Cache;
use uv_configuration::RAYON_INITIALIZE; use uv_configuration::{Preview, RAYON_INITIALIZE};
use uv_distribution_types::CachedDist; use uv_distribution_types::CachedDist;
use uv_install_wheel::{Layout, LinkMode}; use uv_install_wheel::{Layout, LinkMode};
use uv_python::PythonEnvironment; use uv_python::PythonEnvironment;
@ -21,11 +21,13 @@ pub struct Installer<'a> {
name: Option<String>, name: Option<String>,
/// The metadata associated with the [`Installer`]. /// The metadata associated with the [`Installer`].
metadata: bool, metadata: bool,
/// Preview settings for the installer.
preview: Preview,
} }
impl<'a> Installer<'a> { impl<'a> Installer<'a> {
/// Initialize a new installer. /// Initialize a new installer.
pub fn new(venv: &'a PythonEnvironment) -> Self { pub fn new(venv: &'a PythonEnvironment, preview: Preview) -> Self {
Self { Self {
venv, venv,
link_mode: LinkMode::default(), link_mode: LinkMode::default(),
@ -33,6 +35,7 @@ impl<'a> Installer<'a> {
reporter: None, reporter: None,
name: Some("uv".to_string()), name: Some("uv".to_string()),
metadata: true, metadata: true,
preview,
} }
} }
@ -88,6 +91,7 @@ impl<'a> Installer<'a> {
reporter, reporter,
name: installer_name, name: installer_name,
metadata: installer_metadata, metadata: installer_metadata,
preview,
} = self; } = self;
if cache.is_some_and(Cache::is_temporary) { if cache.is_some_and(Cache::is_temporary) {
@ -113,6 +117,7 @@ impl<'a> Installer<'a> {
reporter.as_ref(), reporter.as_ref(),
relocatable, relocatable,
installer_metadata, installer_metadata,
preview,
); );
// This may fail if the main task was cancelled. // This may fail if the main task was cancelled.
@ -143,6 +148,7 @@ impl<'a> Installer<'a> {
self.reporter.as_ref(), self.reporter.as_ref(),
self.venv.relocatable(), self.venv.relocatable(),
self.metadata, self.metadata,
self.preview,
) )
} }
} }
@ -157,10 +163,11 @@ fn install(
reporter: Option<&Arc<dyn Reporter>>, reporter: Option<&Arc<dyn Reporter>>,
relocatable: bool, relocatable: bool,
installer_metadata: bool, installer_metadata: bool,
preview: Preview,
) -> Result<Vec<CachedDist>> { ) -> Result<Vec<CachedDist>> {
// Initialize the threadpool with the user settings. // Initialize the threadpool with the user settings.
LazyLock::force(&RAYON_INITIALIZE); LazyLock::force(&RAYON_INITIALIZE);
let locks = uv_install_wheel::Locks::default(); let locks = uv_install_wheel::Locks::new(preview);
wheels.par_iter().try_for_each(|wheel| { wheels.par_iter().try_for_each(|wheel| {
uv_install_wheel::install_wheel( uv_install_wheel::install_wheel(
layout, layout,

View File

@ -611,6 +611,7 @@ pub(crate) async fn pip_install(
installer_metadata, installer_metadata,
dry_run, dry_run,
printer, printer,
preview,
) )
.await .await
{ {

View File

@ -450,6 +450,7 @@ pub(crate) async fn install(
installer_metadata: bool, installer_metadata: bool,
dry_run: DryRun, dry_run: DryRun,
printer: Printer, printer: Printer,
preview: uv_configuration::Preview,
) -> Result<Changelog, Error> { ) -> Result<Changelog, Error> {
let start = std::time::Instant::now(); let start = std::time::Instant::now();
@ -571,7 +572,7 @@ pub(crate) async fn install(
let mut installs = wheels.into_iter().chain(cached).collect::<Vec<_>>(); let mut installs = wheels.into_iter().chain(cached).collect::<Vec<_>>();
if !installs.is_empty() { if !installs.is_empty() {
let start = std::time::Instant::now(); let start = std::time::Instant::now();
installs = uv_installer::Installer::new(venv) installs = uv_installer::Installer::new(venv, preview)
.with_link_mode(link_mode) .with_link_mode(link_mode)
.with_cache(cache) .with_cache(cache)
.with_installer_metadata(installer_metadata) .with_installer_metadata(installer_metadata)

View File

@ -550,6 +550,7 @@ pub(crate) async fn pip_sync(
installer_metadata, installer_metadata,
dry_run, dry_run,
printer, printer,
preview,
) )
.await .await
{ {

View File

@ -2173,6 +2173,7 @@ pub(crate) async fn sync_environment(
installer_metadata, installer_metadata,
dry_run, dry_run,
printer, printer,
preview,
) )
.await?; .await?;
@ -2434,6 +2435,7 @@ pub(crate) async fn update_environment(
installer_metadata, installer_metadata,
dry_run, dry_run,
printer, printer,
preview,
) )
.await?; .await?;

View File

@ -809,6 +809,7 @@ pub(super) async fn do_sync(
installer_metadata, installer_metadata,
dry_run, dry_run,
printer, printer,
preview,
) )
.await?; .await?;

View File

@ -12486,7 +12486,7 @@ fn overlapping_packages_warning() -> Result<()> {
.child("__init__.py") .child("__init__.py")
.touch()?; .touch()?;
// Check that overlapping packages show a warning // Check that overlapping packages don't show a warning by default
uv_snapshot!(context.filters(), context.pip_install() uv_snapshot!(context.filters(), context.pip_install()
.arg("--no-deps") .arg("--no-deps")
.arg(&built_by_uv) .arg(&built_by_uv)
@ -12495,6 +12495,29 @@ fn overlapping_packages_warning() -> Result<()> {
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
----- stderr -----
Resolved 2 packages in [TIME]
Prepared 2 packages in [TIME]
Installed 2 packages in [TIME]
+ also-built-by-uv==0.1.0 (from file://[TEMP_DIR]/also-built-by-uv)
+ built-by-uv==0.1.0 (from file://[WORKSPACE]/scripts/packages/built-by-uv)
"
);
// Clean up for the next test
context.venv().arg("--clear").assert().success();
// Check that overlapping packages show a warning when preview feature is enabled
uv_snapshot!(context.filters(), context.pip_install()
.arg("--no-deps")
.arg("--preview-features")
.arg("detect-module-conflicts")
.arg(&built_by_uv)
.arg(also_build_by_uv.path()), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr ----- ----- stderr -----
Resolved 2 packages in [TIME] Resolved 2 packages in [TIME]
Prepared 2 packages in [TIME] Prepared 2 packages in [TIME]

View File

@ -7720,7 +7720,7 @@ fn preview_features() {
show_settings: true, show_settings: true,
preview: Preview { preview: Preview {
flags: PreviewFeatures( flags: PreviewFeatures(
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES, PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS,
), ),
}, },
python_preference: Managed, python_preference: Managed,
@ -7946,7 +7946,7 @@ fn preview_features() {
show_settings: true, show_settings: true,
preview: Preview { preview: Preview {
flags: PreviewFeatures( flags: PreviewFeatures(
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES, PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS,
), ),
}, },
python_preference: Managed, python_preference: Managed,