diff --git a/crates/uv-settings/src/lib.rs b/crates/uv-settings/src/lib.rs index ec5d8587675..d5dc9047f0d 100644 --- a/crates/uv-settings/src/lib.rs +++ b/crates/uv-settings/src/lib.rs @@ -76,6 +76,10 @@ impl FilesystemOptions { } pub fn system() -> Result, Error> { + if parse_boolish_environment_variable(EnvVars::UV_NO_SYSTEM_CONFIG)? == Some(true) { + return Ok(None); + } + let Some(file) = system_config_file() else { return Ok(None); }; diff --git a/crates/uv-static/src/env_vars.rs b/crates/uv-static/src/env_vars.rs index 97414185c42..2472e4f361d 100644 --- a/crates/uv-static/src/env_vars.rs +++ b/crates/uv-static/src/env_vars.rs @@ -187,6 +187,10 @@ impl EnvVars { #[attr_added_in("0.2.30")] pub const UV_NO_CONFIG: &'static str = "UV_NO_CONFIG"; + /// If set, uv will not read system-level configuration files. + #[attr_added_in("next release")] + pub const UV_NO_SYSTEM_CONFIG: &'static str = "UV_NO_SYSTEM_CONFIG"; + /// Equivalent to the `--isolated` command-line argument. If set, uv will avoid discovering /// a `pyproject.toml` or `uv.toml` file. #[attr_added_in("0.8.14")] diff --git a/crates/uv/tests/it/show_settings.rs b/crates/uv/tests/it/show_settings.rs index 80983526ff1..1695e46cc23 100644 --- a/crates/uv/tests/it/show_settings.rs +++ b/crates/uv/tests/it/show_settings.rs @@ -3630,6 +3630,382 @@ fn resolve_user_configuration() -> anyhow::Result<()> { Ok(()) } +/// Verify that system configuration can be disabled with `UV_NO_SYSTEM_CONFIG`. +#[test] +#[cfg_attr( + windows, + ignore = "Configuration tests are not yet supported on Windows" +)] +fn resolve_system_configuration_can_be_disabled() -> anyhow::Result<()> { + let xdg = assert_fs::TempDir::new().expect("Failed to create temp dir"); + let uv = xdg.child("uv"); + let config = uv.child("uv.toml"); + config.write_str(indoc::indoc! {r#" + [pip] + resolution = "lowest-direct" + "#})?; + + let context = uv_test::test_context!("3.12"); + + let requirements_in = context.temp_dir.child("requirements.in"); + requirements_in.write_str("anyio>3.0.0")?; + + uv_snapshot!(context.filters(), add_shared_args(context.pip_compile()) + .arg("--show-settings") + .arg("requirements.in") + .env(EnvVars::XDG_CONFIG_DIRS, xdg.path()), @r#" + success: true + exit_code: 0 + ----- stdout ----- + GlobalSettings { + required_version: None, + quiet: 0, + verbose: 0, + color: Auto, + network_settings: NetworkSettings { + connectivity: Online, + offline: Disabled, + system_certs: false, + http_proxy: None, + https_proxy: None, + no_proxy: None, + allow_insecure_host: [], + read_timeout: [TIME], + connect_timeout: [TIME], + retries: 3, + }, + concurrency: Concurrency { + downloads: 50, + builds: 16, + installs: 8, + }, + show_settings: true, + preview: Preview { + flags: [], + }, + python_preference: Managed, + python_downloads: Automatic, + no_progress: false, + installer_metadata: true, + } + CacheSettings { + no_cache: false, + cache_dir: Some( + "[CACHE_DIR]/", + ), + } + PipCompileSettings { + format: None, + src_file: [ + "requirements.in", + ], + constraints: [], + overrides: [], + excludes: [], + build_constraints: [], + constraints_from_workspace: [], + overrides_from_workspace: [], + excludes_from_workspace: [], + build_constraints_from_workspace: [], + environments: SupportedEnvironments( + [], + ), + required_environments: SupportedEnvironments( + [], + ), + refresh: None( + Timestamp( + SystemTime { + tv_sec: [TIME], + tv_nsec: [TIME], + }, + ), + ), + settings: PipSettings { + index_locations: IndexLocations { + indexes: [], + flat_index: [], + no_index: false, + }, + python: None, + install_mirrors: PythonInstallMirrors { + python_install_mirror: None, + pypy_install_mirror: None, + python_downloads_json_url: None, + }, + system: false, + extras: ExtrasSpecification( + ExtrasSpecificationInner { + include: Some( + [], + ), + exclude: [], + only_extras: false, + history: ExtrasSpecificationHistory { + extra: [], + only_extra: [], + no_extra: [], + all_extras: false, + no_default_extras: false, + defaults: List( + [], + ), + }, + }, + ), + groups: [], + break_system_packages: false, + target: None, + prefix: None, + index_strategy: FirstIndex, + keyring_provider: Disabled, + torch_backend: None, + build_isolation: Isolate, + extra_build_dependencies: ExtraBuildDependencies( + {}, + ), + extra_build_variables: ExtraBuildVariables( + {}, + ), + build_options: BuildOptions { + no_binary: None, + no_build: None, + }, + allow_empty_requirements: false, + strict: false, + dependency_mode: Transitive, + resolution: LowestDirect, + prerelease: IfNecessaryOrExplicit, + fork_strategy: RequiresPython, + dependency_metadata: DependencyMetadata( + {}, + ), + output_file: None, + no_strip_extras: false, + no_strip_markers: false, + no_annotate: false, + no_header: false, + custom_compile_command: None, + generate_hashes: false, + config_setting: ConfigSettings( + {}, + ), + config_settings_package: PackageConfigSettings( + {}, + ), + python_version: None, + python_platform: None, + universal: false, + exclude_newer: ExcludeNewer { + global: None, + package: ExcludeNewerPackage( + {}, + ), + }, + no_emit_package: [], + emit_index_url: false, + emit_find_links: false, + emit_build_options: false, + emit_marker_expression: false, + emit_index_annotation: false, + annotation_style: Split, + link_mode: Clone, + compile_bytecode: false, + sources: None, + hash_checking: Some( + Verify, + ), + upgrade: Upgrade { + strategy: None, + constraints: {}, + }, + reinstall: None, + }, + } + + ----- stderr ----- + "#); + + uv_snapshot!(context.filters(), add_shared_args(context.pip_compile()) + .arg("--show-settings") + .arg("requirements.in") + .env(EnvVars::XDG_CONFIG_DIRS, xdg.path()) + .env(EnvVars::UV_NO_SYSTEM_CONFIG, "1"), @r#" + success: true + exit_code: 0 + ----- stdout ----- + GlobalSettings { + required_version: None, + quiet: 0, + verbose: 0, + color: Auto, + network_settings: NetworkSettings { + connectivity: Online, + offline: Disabled, + system_certs: false, + http_proxy: None, + https_proxy: None, + no_proxy: None, + allow_insecure_host: [], + read_timeout: [TIME], + connect_timeout: [TIME], + retries: 3, + }, + concurrency: Concurrency { + downloads: 50, + builds: 16, + installs: 8, + }, + show_settings: true, + preview: Preview { + flags: [], + }, + python_preference: Managed, + python_downloads: Automatic, + no_progress: false, + installer_metadata: true, + } + CacheSettings { + no_cache: false, + cache_dir: Some( + "[CACHE_DIR]/", + ), + } + PipCompileSettings { + format: None, + src_file: [ + "requirements.in", + ], + constraints: [], + overrides: [], + excludes: [], + build_constraints: [], + constraints_from_workspace: [], + overrides_from_workspace: [], + excludes_from_workspace: [], + build_constraints_from_workspace: [], + environments: SupportedEnvironments( + [], + ), + required_environments: SupportedEnvironments( + [], + ), + refresh: None( + Timestamp( + SystemTime { + tv_sec: [TIME], + tv_nsec: [TIME], + }, + ), + ), + settings: PipSettings { + index_locations: IndexLocations { + indexes: [], + flat_index: [], + no_index: false, + }, + python: None, + install_mirrors: PythonInstallMirrors { + python_install_mirror: None, + pypy_install_mirror: None, + python_downloads_json_url: None, + }, + system: false, + extras: ExtrasSpecification( + ExtrasSpecificationInner { + include: Some( + [], + ), + exclude: [], + only_extras: false, + history: ExtrasSpecificationHistory { + extra: [], + only_extra: [], + no_extra: [], + all_extras: false, + no_default_extras: false, + defaults: List( + [], + ), + }, + }, + ), + groups: [], + break_system_packages: false, + target: None, + prefix: None, + index_strategy: FirstIndex, + keyring_provider: Disabled, + torch_backend: None, + build_isolation: Isolate, + extra_build_dependencies: ExtraBuildDependencies( + {}, + ), + extra_build_variables: ExtraBuildVariables( + {}, + ), + build_options: BuildOptions { + no_binary: None, + no_build: None, + }, + allow_empty_requirements: false, + strict: false, + dependency_mode: Transitive, + resolution: Highest, + prerelease: IfNecessaryOrExplicit, + fork_strategy: RequiresPython, + dependency_metadata: DependencyMetadata( + {}, + ), + output_file: None, + no_strip_extras: false, + no_strip_markers: false, + no_annotate: false, + no_header: false, + custom_compile_command: None, + generate_hashes: false, + config_setting: ConfigSettings( + {}, + ), + config_settings_package: PackageConfigSettings( + {}, + ), + python_version: None, + python_platform: None, + universal: false, + exclude_newer: ExcludeNewer { + global: None, + package: ExcludeNewerPackage( + {}, + ), + }, + no_emit_package: [], + emit_index_url: false, + emit_find_links: false, + emit_build_options: false, + emit_marker_expression: false, + emit_index_annotation: false, + annotation_style: Split, + link_mode: Clone, + compile_bytecode: false, + sources: None, + hash_checking: Some( + Verify, + ), + upgrade: Upgrade { + strategy: None, + constraints: {}, + }, + reinstall: None, + }, + } + + ----- stderr ----- + "#); + + Ok(()) +} + /// When running a user-level command (like `uv tool install`), we should read user configuration, /// but ignore project-local configuration. #[test]