From a8a57581f9ccabcd33e0479c6400650f9e2c7d4a Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Mon, 21 Oct 2024 12:02:22 -0500 Subject: [PATCH 01/19] Use 3.13 in CI (#8014) --- .github/workflows/ci.yml | 1 + .python-versions | 1 + crates/uv/tests/it/venv.rs | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe4379d62eea..4454bc68308b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -279,6 +279,7 @@ jobs: 3.10 3.11 3.12 + 3.13 - uses: Swatinem/rust-cache@v2 with: diff --git a/.python-versions b/.python-versions index c78335fbdd4d..013b3f8a9fb9 100644 --- a/.python-versions +++ b/.python-versions @@ -1,3 +1,4 @@ +3.13.0 3.12.6 3.11.10 3.10.15 diff --git a/crates/uv/tests/it/venv.rs b/crates/uv/tests/it/venv.rs index 072bcff7dc9e..4e0d9dd2f97d 100644 --- a/crates/uv/tests/it/venv.rs +++ b/crates/uv/tests/it/venv.rs @@ -50,6 +50,28 @@ fn create_venv() { context.venv.assert(predicates::path::is_dir()); } +#[test] +fn create_venv_313() { + let context = TestContext::new_with_versions(&["3.13"]); + + uv_snapshot!(context.filters(), context.venv() + .arg(context.venv.as_os_str()) + .arg("--python") + .arg("3.13"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Using CPython 3.13.[X] interpreter at: [PYTHON-3.13] + Creating virtual environment at: .venv + Activate with: source .venv/[BIN]/activate + "### + ); + + context.venv.assert(predicates::path::is_dir()); +} + #[test] fn create_venv_project_environment() -> Result<()> { let context = TestContext::new_with_versions(&["3.12"]); From 8062ebd1f856bcf71d6473af836d67e5b9479f7b Mon Sep 17 00:00:00 2001 From: Aditya Pratap Singh Date: Mon, 21 Oct 2024 22:34:49 +0530 Subject: [PATCH 02/19] Improve interactions between color environment variables and CLI options (#8215) closes #8173 --- crates/uv-cli/src/lib.rs | 2 +- crates/uv/src/settings.rs | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 734395be21dc..d9074451f913 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -179,7 +179,7 @@ pub struct GlobalArgs { conflicts_with = "no_color", value_name = "COLOR_CHOICE" )] - pub color: ColorChoice, + pub color: Option, /// Whether to load TLS certificates from the platform's native certificate store. /// diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index 19604a406827..a45993981e1b 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -74,11 +74,14 @@ impl GlobalSettings { Self { quiet: args.quiet, verbose: args.verbose, - color: if args.no_color - || std::env::var_os(EnvVars::NO_COLOR) - .filter(|v| !v.is_empty()) - .is_some() + color: if let Some(color_choice) = args.color { + // If `--color` is passed explicitly, use its value. + color_choice + } else if std::env::var_os(EnvVars::NO_COLOR) + .filter(|v| !v.is_empty()) + .is_some() { + // If the `NO_COLOR` is set, disable color output. ColorChoice::Never } else if std::env::var_os(EnvVars::FORCE_COLOR) .filter(|v| !v.is_empty()) @@ -87,9 +90,10 @@ impl GlobalSettings { .filter(|v| !v.is_empty()) .is_some() { + // If `FORCE_COLOR` or `CLICOLOR_FORCE` is set, always enable color output. ColorChoice::Always } else { - args.color + ColorChoice::Auto }, native_tls: flag(args.native_tls, args.no_native_tls) .combine(workspace.and_then(|workspace| workspace.globals.native_tls)) From af7170e38ffab089d8d57fec279e5247d2db0adf Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Mon, 4 Nov 2024 06:48:02 -0600 Subject: [PATCH 03/19] do not imply pre-release when `!=` operator is used (#7974) closes #6640 Could you suggest how I should test it? (already tested locally) --------- Co-authored-by: konstin Co-authored-by: Charles Tapley Hoyt Co-authored-by: Charlie Marsh --- crates/uv-resolver/src/prerelease.rs | 8 +++++-- crates/uv/tests/it/pip_compile.rs | 36 ++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/crates/uv-resolver/src/prerelease.rs b/crates/uv-resolver/src/prerelease.rs index 4a4cd492a171..ea6843a898c9 100644 --- a/crates/uv-resolver/src/prerelease.rs +++ b/crates/uv-resolver/src/prerelease.rs @@ -1,10 +1,11 @@ use uv_pypi_types::RequirementSource; -use uv_normalize::PackageName; - use crate::resolver::ForkSet; use crate::{DependencyMode, Manifest, ResolverEnvironment}; +use uv_normalize::PackageName; +use uv_pep440::Operator; + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] #[cfg_attr(feature = "clap", derive(clap::ValueEnum))] @@ -84,6 +85,9 @@ impl PrereleaseStrategy { if specifier .iter() + .filter(|spec| { + !matches!(spec.operator(), Operator::NotEqual | Operator::NotEqualStar) + }) .any(uv_pep440::VersionSpecifier::any_prerelease) { packages.add(&requirement, ()); diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs index 9352bacf4663..36e14a04f01b 100644 --- a/crates/uv/tests/it/pip_compile.rs +++ b/crates/uv/tests/it/pip_compile.rs @@ -12687,6 +12687,42 @@ fn unsupported_requires_python_dynamic_metadata() -> Result<()> { Ok(()) } +#[test] +fn negation_not_imply_prerelease() -> Result<()> { + let context = TestContext::new("3.12"); + + let requirements_in = context.temp_dir.child("requirements.in"); + requirements_in.write_str("flask<2.0.1, !=2.0.0rc1")?; + uv_snapshot!(context + .pip_compile() + .arg("requirements.in"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv pip compile --cache-dir [CACHE_DIR] requirements.in + click==8.1.7 + # via flask + flask==2.0.0 + # via -r requirements.in + itsdangerous==2.1.2 + # via flask + jinja2==3.1.3 + # via flask + markupsafe==2.1.5 + # via + # jinja2 + # werkzeug + werkzeug==3.0.1 + # via flask + + ----- stderr ----- + Resolved 6 packages in [TIME] + "###); + + Ok(()) +} + /// Perform a universal resolution with a constraint, where the constraint itself has a marker. #[test] fn lowest_direct_fork() -> Result<()> { From d47e93f635cec4ece83bb5d2b6ad3fc09dc41418 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Tue, 5 Nov 2024 10:57:55 -0600 Subject: [PATCH 04/19] Eliminate dependencies on `directores` and `dirs-sys` (#8048) Migrate all directory related logic to `etcetera`, eliminated two dependecies. --- Cargo.lock | 15 ++----------- Cargo.toml | 2 -- crates/uv-dirs/Cargo.toml | 2 -- crates/uv-dirs/src/lib.rs | 40 +++++++++++++++++++++++++---------- crates/uv-settings/Cargo.toml | 2 +- crates/uv-settings/src/lib.rs | 23 ++++++-------------- crates/uv-tool/src/lib.rs | 1 - 7 files changed, 39 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5ef911246d5e..33d0b992f487 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -914,15 +914,6 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "directories" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" -dependencies = [ - "dirs-sys", -] - [[package]] name = "dirs" version = "5.0.1" @@ -4599,8 +4590,6 @@ dependencies = [ name = "uv-dirs" version = "0.0.1" dependencies = [ - "directories", - "dirs-sys", "etcetera", "uv-static", ] @@ -5236,7 +5225,7 @@ version = "0.0.1" dependencies = [ "assert_fs", "clap", - "dirs-sys", + "etcetera", "fs-err", "indoc", "schemars", @@ -5632,7 +5621,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 5bd8e9491fb1..ed042f3d49b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,8 +91,6 @@ csv = { version = "1.3.0" } ctrlc = { version = "3.4.5" } dashmap = { version = "6.1.0" } data-encoding = { version = "2.6.0" } -directories = { version = "5.0.1" } -dirs-sys = { version = "0.4.1" } dotenvy = { version = "0.15.7" } dunce = { version = "1.0.5" } either = { version = "1.13.0" } diff --git a/crates/uv-dirs/Cargo.toml b/crates/uv-dirs/Cargo.toml index d32a4a4e3ccf..408c101fd3f2 100644 --- a/crates/uv-dirs/Cargo.toml +++ b/crates/uv-dirs/Cargo.toml @@ -19,6 +19,4 @@ workspace = true [dependencies] uv-static = { workspace = true } -dirs-sys = { workspace = true } -directories = { workspace = true } etcetera = { workspace = true } diff --git a/crates/uv-dirs/src/lib.rs b/crates/uv-dirs/src/lib.rs index 759a4df75dd2..464f95eaed2c 100644 --- a/crates/uv-dirs/src/lib.rs +++ b/crates/uv-dirs/src/lib.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{ffi::OsString, path::PathBuf}; use etcetera::BaseStrategy; @@ -20,19 +20,15 @@ use uv_static::EnvVars; pub fn user_executable_directory(override_variable: Option<&'static str>) -> Option { override_variable .and_then(std::env::var_os) - .and_then(dirs_sys::is_absolute_path) - .or_else(|| std::env::var_os(EnvVars::XDG_BIN_HOME).and_then(dirs_sys::is_absolute_path)) + .and_then(parse_path) + .or_else(|| std::env::var_os(EnvVars::XDG_BIN_HOME).and_then(parse_path)) .or_else(|| { std::env::var_os(EnvVars::XDG_DATA_HOME) - .and_then(dirs_sys::is_absolute_path) + .and_then(parse_path) .map(|path| path.join("../bin")) }) .or_else(|| { - // See https://github.com/dirs-dev/dirs-rs/blob/50b50f31f3363b7656e5e63b3fa1060217cbc844/src/win.rs#L5C58-L5C78 - #[cfg(windows)] - let home_dir = dirs_sys::known_folder_profile(); - #[cfg(not(windows))] - let home_dir = dirs_sys::home_dir(); + let home_dir = etcetera::home_dir().ok(); home_dir.map(|path| path.join(".local").join("bin")) }) } @@ -51,7 +47,16 @@ pub fn user_cache_dir() -> Option { /// Uses `/Users/user/Library/Application Support/uv` on macOS, in contrast to the new preference /// for using the XDG directories on all Unix platforms. pub fn legacy_user_cache_dir() -> Option { - directories::ProjectDirs::from("", "", "uv").map(|dirs| dirs.cache_dir().to_path_buf()) + etcetera::base_strategy::choose_native_strategy() + .ok() + .map(|dirs| dirs.cache_dir().join("uv")) + .map(|dir| { + if cfg!(windows) { + dir.join("cache") + } else { + dir + } + }) } /// Returns an appropriate user-level directory for storing application state. @@ -68,5 +73,18 @@ pub fn user_state_dir() -> Option { /// Uses `/Users/user/Library/Application Support/uv` on macOS, in contrast to the new preference /// for using the XDG directories on all Unix platforms. pub fn legacy_user_state_dir() -> Option { - directories::ProjectDirs::from("", "", "uv").map(|dirs| dirs.data_dir().to_path_buf()) + etcetera::base_strategy::choose_native_strategy() + .ok() + .map(|dirs| dirs.data_dir().join("uv")) + .map(|dir| if cfg!(windows) { dir.join("data") } else { dir }) +} + +/// Return a [`PathBuf`] if the given [`OsString`] is an absolute path. +fn parse_path(path: OsString) -> Option { + let path = PathBuf::from(path); + if path.is_absolute() { + Some(path) + } else { + None + } } diff --git a/crates/uv-settings/Cargo.toml b/crates/uv-settings/Cargo.toml index 1aca3d09c1f5..afee1de22f05 100644 --- a/crates/uv-settings/Cargo.toml +++ b/crates/uv-settings/Cargo.toml @@ -32,7 +32,7 @@ uv-static = { workspace = true } uv-warnings = { workspace = true } clap = { workspace = true } -dirs-sys = { workspace = true } +etcetera = { workspace = true } fs-err = { workspace = true } schemars = { workspace = true, optional = true } serde = { workspace = true } diff --git a/crates/uv-settings/src/lib.rs b/crates/uv-settings/src/lib.rs index 5534dbaebe42..e937d7b8bb69 100644 --- a/crates/uv-settings/src/lib.rs +++ b/crates/uv-settings/src/lib.rs @@ -2,6 +2,8 @@ use std::env; use std::ops::Deref; use std::path::{Path, PathBuf}; +use etcetera::BaseStrategy; + use uv_fs::Simplified; use uv_static::EnvVars; use uv_warnings::warn_user; @@ -174,23 +176,12 @@ impl From for FilesystemOptions { /// Returns the path to the user configuration directory. /// -/// This is similar to the `config_dir()` returned by the `dirs` crate, but it uses the -/// `XDG_CONFIG_HOME` environment variable on both Linux _and_ macOS, rather than the -/// `Application Support` directory on macOS. +/// On Windows, use, e.g., C:\Users\Alice\AppData\Roaming +/// On Linux and macOS, use `XDG_CONFIG_HOME` or $HOME/.config, e.g., /home/alice/.config. fn user_config_dir() -> Option { - // On Windows, use, e.g., `C:\Users\Alice\AppData\Roaming`. - #[cfg(windows)] - { - dirs_sys::known_folder_roaming_app_data() - } - - // On Linux and macOS, use, e.g., `/home/alice/.config`. - #[cfg(not(windows))] - { - env::var_os(EnvVars::XDG_CONFIG_HOME) - .and_then(dirs_sys::is_absolute_path) - .or_else(|| dirs_sys::home_dir().map(|path| path.join(".config"))) - } + etcetera::choose_base_strategy() + .map(|dirs| dirs.config_dir()) + .ok() } #[cfg(not(windows))] diff --git a/crates/uv-tool/src/lib.rs b/crates/uv-tool/src/lib.rs index 085793e5610c..24be27dc2d91 100644 --- a/crates/uv-tool/src/lib.rs +++ b/crates/uv-tool/src/lib.rs @@ -1,5 +1,4 @@ use core::fmt; - use fs_err as fs; use uv_dirs::user_executable_directory; From 35d14abf891005a19cceb9e87a65d4598f7948a9 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Mon, 4 Nov 2024 06:48:58 -0600 Subject: [PATCH 05/19] Treat the base Conda environment as a system environment (#7691) Closes https://github.com/astral-sh/uv/issues/7124 Closes https://github.com/astral-sh/uv/issues/7137 --- crates/uv-python/src/discovery.rs | 51 ++++++++++++++-------- crates/uv-python/src/tests.rs | 68 ++++++++++++++++++++++++++++++ crates/uv-python/src/virtualenv.rs | 53 ++++++++++++++++++++--- crates/uv-static/src/env_vars.rs | 3 ++ docs/configuration/environment.md | 1 + 5 files changed, 153 insertions(+), 23 deletions(-) diff --git a/crates/uv-python/src/discovery.rs b/crates/uv-python/src/discovery.rs index be3b5bb77faf..1dfe18383762 100644 --- a/crates/uv-python/src/discovery.rs +++ b/crates/uv-python/src/discovery.rs @@ -26,8 +26,8 @@ use crate::microsoft_store::find_microsoft_store_pythons; #[cfg(windows)] use crate::py_launcher::{registry_pythons, WindowsPython}; use crate::virtualenv::{ - conda_prefix_from_env, virtualenv_from_env, virtualenv_from_working_dir, - virtualenv_python_executable, + conda_environment_from_env, virtualenv_from_env, virtualenv_from_working_dir, + virtualenv_python_executable, CondaEnvironmentKind, }; use crate::{Interpreter, PythonVersion}; @@ -185,6 +185,8 @@ pub enum PythonSource { ActiveEnvironment, /// A conda environment was active e.g. via `CONDA_PREFIX` CondaPrefix, + /// A base conda environment was active e.g. via `CONDA_PREFIX` + BaseCondaPrefix, /// An environment was discovered e.g. via `.venv` DiscoveredEnvironment, /// An executable was found in the search path i.e. `PATH` @@ -233,18 +235,17 @@ pub enum Error { SourceNotAllowed(PythonRequest, PythonSource, PythonPreference), } -/// Lazily iterate over Python executables in mutable environments. +/// Lazily iterate over Python executables in mutable virtual environments. /// /// The following sources are supported: /// /// - Active virtual environment (via `VIRTUAL_ENV`) -/// - Active conda environment (via `CONDA_PREFIX`) /// - Discovered virtual environment (e.g. `.venv` in a parent directory) /// /// Notably, "system" environments are excluded. See [`python_executables_from_installed`]. -fn python_executables_from_environments<'a>( +fn python_executables_from_virtual_environments<'a>( ) -> impl Iterator> + 'a { - let from_virtual_environment = std::iter::once_with(|| { + let from_active_environment = std::iter::once_with(|| { virtualenv_from_env() .into_iter() .map(virtualenv_python_executable) @@ -252,8 +253,9 @@ fn python_executables_from_environments<'a>( }) .flatten(); + // N.B. we prefer the conda environment over discovered virtual environments let from_conda_environment = std::iter::once_with(|| { - conda_prefix_from_env() + conda_environment_from_env(CondaEnvironmentKind::Child) .into_iter() .map(virtualenv_python_executable) .map(|path| Ok((PythonSource::CondaPrefix, path))) @@ -271,7 +273,7 @@ fn python_executables_from_environments<'a>( }) .flatten_ok(); - from_virtual_environment + from_active_environment .chain(from_conda_environment) .chain(from_discovered_environment) } @@ -406,23 +408,35 @@ fn python_executables<'a>( }) .flatten(); - let from_environments = python_executables_from_environments(); + // Check if the the base conda environment is active + let from_base_conda_environment = std::iter::once_with(|| { + conda_environment_from_env(CondaEnvironmentKind::Base) + .into_iter() + .map(virtualenv_python_executable) + .map(|path| Ok((PythonSource::BaseCondaPrefix, path))) + }) + .flatten(); + + let from_virtual_environments = python_executables_from_virtual_environments(); let from_installed = python_executables_from_installed(version, implementation, preference); // Limit the search to the relevant environment preference; we later validate that they match // the preference but queries are expensive and we query less interpreters this way. match environments { EnvironmentPreference::OnlyVirtual => { - Box::new(from_parent_interpreter.chain(from_environments)) + Box::new(from_parent_interpreter.chain(from_virtual_environments)) } EnvironmentPreference::ExplicitSystem | EnvironmentPreference::Any => Box::new( from_parent_interpreter - .chain(from_environments) + .chain(from_virtual_environments) + .chain(from_base_conda_environment) + .chain(from_installed), + ), + EnvironmentPreference::OnlySystem => Box::new( + from_parent_interpreter + .chain(from_base_conda_environment) .chain(from_installed), ), - EnvironmentPreference::OnlySystem => { - Box::new(from_parent_interpreter.chain(from_installed)) - } } } @@ -617,8 +631,8 @@ fn satisfies_environment_preference( ) -> bool { match ( preference, - // Conda environments are not conformant virtual environments but we treat them as such - interpreter.is_virtualenv() || matches!(source, PythonSource::CondaPrefix), + // Conda environments are not conformant virtual environments but we treat them as such. + interpreter.is_virtualenv() || (matches!(source, PythonSource::CondaPrefix)), ) { (EnvironmentPreference::Any, _) => true, (EnvironmentPreference::OnlyVirtual, true) => true, @@ -1515,6 +1529,7 @@ impl PythonSource { Self::Managed | Self::Registry | Self::MicrosoftStore => false, Self::SearchPath | Self::CondaPrefix + | Self::BaseCondaPrefix | Self::ProvidedPath | Self::ParentInterpreter | Self::ActiveEnvironment @@ -1527,6 +1542,7 @@ impl PythonSource { match self { Self::Managed | Self::Registry | Self::SearchPath | Self::MicrosoftStore => false, Self::CondaPrefix + | Self::BaseCondaPrefix | Self::ProvidedPath | Self::ParentInterpreter | Self::ActiveEnvironment @@ -1846,6 +1862,7 @@ impl VersionRequest { Self::Default => match source { PythonSource::ParentInterpreter | PythonSource::CondaPrefix + | PythonSource::BaseCondaPrefix | PythonSource::ProvidedPath | PythonSource::DiscoveredEnvironment | PythonSource::ActiveEnvironment => Self::Any, @@ -2256,7 +2273,7 @@ impl fmt::Display for PythonSource { match self { Self::ProvidedPath => f.write_str("provided path"), Self::ActiveEnvironment => f.write_str("active virtual environment"), - Self::CondaPrefix => f.write_str("conda prefix"), + Self::CondaPrefix | Self::BaseCondaPrefix => f.write_str("conda prefix"), Self::DiscoveredEnvironment => f.write_str("virtual environment"), Self::SearchPath => f.write_str("search path"), Self::Registry => f.write_str("registry"), diff --git a/crates/uv-python/src/tests.rs b/crates/uv-python/src/tests.rs index ad5f2d495b1c..2c8a6475f742 100644 --- a/crates/uv-python/src/tests.rs +++ b/crates/uv-python/src/tests.rs @@ -949,6 +949,74 @@ fn find_python_from_conda_prefix() -> Result<()> { "We should allow the active conda python" ); + let baseenv = context.tempdir.child("base"); + TestContext::mock_conda_prefix(&baseenv, "3.12.1")?; + + // But not if it's a base environment + let result = context.run_with_vars( + &[ + ("CONDA_PREFIX", Some(baseenv.as_os_str())), + ("CONDA_DEFAULT_ENV", Some(&OsString::from("base"))), + ], + || { + find_python_installation( + &PythonRequest::Default, + EnvironmentPreference::OnlyVirtual, + PythonPreference::OnlySystem, + &context.cache, + ) + }, + )?; + + assert!( + matches!(result, Err(PythonNotFound { .. })), + "We should not allow the non-virtual environment; got {result:?}" + ); + + // Unless, system interpreters are included... + let python = context.run_with_vars( + &[ + ("CONDA_PREFIX", Some(baseenv.as_os_str())), + ("CONDA_DEFAULT_ENV", Some(&OsString::from("base"))), + ], + || { + find_python_installation( + &PythonRequest::Default, + EnvironmentPreference::OnlySystem, + PythonPreference::OnlySystem, + &context.cache, + ) + }, + )??; + + assert_eq!( + python.interpreter().python_full_version().to_string(), + "3.12.1", + "We should find the base conda environment" + ); + + // If the environment name doesn't match the default, we should not treat it as system + let python = context.run_with_vars( + &[ + ("CONDA_PREFIX", Some(condaenv.as_os_str())), + ("CONDA_DEFAULT_ENV", Some(&OsString::from("base"))), + ], + || { + find_python_installation( + &PythonRequest::Default, + EnvironmentPreference::OnlyVirtual, + PythonPreference::OnlySystem, + &context.cache, + ) + }, + )??; + + assert_eq!( + python.interpreter().python_full_version().to_string(), + "3.12.0", + "We should find the conda environment" + ); + Ok(()) } diff --git a/crates/uv-python/src/virtualenv.rs b/crates/uv-python/src/virtualenv.rs index 865aabf2096a..5fdfc094f029 100644 --- a/crates/uv-python/src/virtualenv.rs +++ b/crates/uv-python/src/virtualenv.rs @@ -57,15 +57,56 @@ pub(crate) fn virtualenv_from_env() -> Option { None } +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub(crate) enum CondaEnvironmentKind { + /// The base Conda environment; treated like a system Python environment. + Base, + /// Any other Conda environment; treated like a virtual environment. + Child, +} + +impl CondaEnvironmentKind { + /// Whether the given `CONDA_PREFIX` path is the base Conda environment. + /// + /// When the base environment is used, `CONDA_DEFAULT_ENV` will be set to a name, i.e., `base` or + /// `root` which does not match the prefix, e.g. `/usr/local` instead of + /// `/usr/local/conda/envs/`. + fn from_prefix_path(path: &Path) -> Self { + // If we cannot read `CONDA_DEFAULT_ENV`, there's no way to know if the base environment + let Ok(default_env) = env::var(EnvVars::CONDA_DEFAULT_ENV) else { + return CondaEnvironmentKind::Child; + }; + + // These are the expected names for the base environment + if default_env != "base" && default_env != "root" { + return CondaEnvironmentKind::Child; + } + + let Some(name) = path.file_name() else { + return CondaEnvironmentKind::Child; + }; + + if name.to_str().is_some_and(|name| name == default_env) { + CondaEnvironmentKind::Base + } else { + CondaEnvironmentKind::Child + } + } +} + /// Locate an active conda environment by inspecting environment variables. /// -/// Supports `CONDA_PREFIX`. -pub(crate) fn conda_prefix_from_env() -> Option { - if let Some(dir) = env::var_os(EnvVars::CONDA_PREFIX).filter(|value| !value.is_empty()) { - return Some(PathBuf::from(dir)); - } +/// If `base` is true, the active environment must be the base environment or `None` is returned, +/// and vice-versa. +pub(crate) fn conda_environment_from_env(kind: CondaEnvironmentKind) -> Option { + let dir = env::var_os(EnvVars::CONDA_PREFIX).filter(|value| !value.is_empty())?; + let path = PathBuf::from(dir); - None + if kind != CondaEnvironmentKind::from_prefix_path(&path) { + return None; + }; + + Some(path) } /// Locate a virtual environment by searching the file system. diff --git a/crates/uv-static/src/env_vars.rs b/crates/uv-static/src/env_vars.rs index 4ba81b8767bf..3b137863fe37 100644 --- a/crates/uv-static/src/env_vars.rs +++ b/crates/uv-static/src/env_vars.rs @@ -337,6 +337,9 @@ impl EnvVars { /// Used to detect an activated Conda environment. pub const CONDA_PREFIX: &'static str = "CONDA_PREFIX"; + /// Used to determine if an active Conda environment is the base environment or not. + pub const CONDA_DEFAULT_ENV: &'static str = "CONDA_DEFAULT_ENV"; + /// If set to `1` before a virtual environment is activated, then the /// virtual environment name will not be prepended to the terminal prompt. pub const VIRTUAL_ENV_DISABLE_PROMPT: &'static str = "VIRTUAL_ENV_DISABLE_PROMPT"; diff --git a/docs/configuration/environment.md b/docs/configuration/environment.md index 1f6d61c71102..b86f54126bb9 100644 --- a/docs/configuration/environment.md +++ b/docs/configuration/environment.md @@ -139,6 +139,7 @@ uv respects the following environment variables: See [`PycInvalidationMode`](https://docs.python.org/3/library/py_compile.html#py_compile.PycInvalidationMode). - [`VIRTUAL_ENV`](#VIRTUAL_ENV): Used to detect an activated virtual environment. - [`CONDA_PREFIX`](#CONDA_PREFIX): Used to detect an activated Conda environment. +- [`CONDA_DEFAULT_ENV`](#CONDA_DEFAULT_ENV): Used to determine if an active Conda environment is the base environment or not. - [`VIRTUAL_ENV_DISABLE_PROMPT`](#VIRTUAL_ENV_DISABLE_PROMPT): If set to `1` before a virtual environment is activated, then the virtual environment name will not be prepended to the terminal prompt. - [`PROMPT`](#PROMPT): Used to detect the use of the Windows Command Prompt (as opposed to PowerShell). From c61ae57fb49c6b1035abd897b89b086e1293e435 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Fri, 25 Oct 2024 11:09:41 -0500 Subject: [PATCH 06/19] Use XDG (i.e. `~/.local/bin`) instead of the Cargo home directory in the installer (#8420) Reviving https://github.com/astral-sh/uv/pull/2236 Basically implements https://github.com/axodotdev/cargo-dist/issues/287 --- Cargo.toml | 2 +- docs/configuration/installer.md | 7 +++++-- docs/getting-started/installation.md | 6 +++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ed042f3d49b4..37e8b974f9b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -335,4 +335,4 @@ github-custom-job-permissions = { "build-docker" = { packages = "write", content # Whether to install an updater program install-updater = false # Path that installers should place binaries in -install-path = "CARGO_HOME" +install-path = ["$XDG_BIN_HOME/", "$XDG_DATA_HOME/../bin", "~/.local/bin"] diff --git a/docs/configuration/installer.md b/docs/configuration/installer.md index 352689b5cc5a..76347741e57d 100644 --- a/docs/configuration/installer.md +++ b/docs/configuration/installer.md @@ -2,8 +2,11 @@ ## Changing the install path -By default, uv is installed to `~/.cargo/bin`. To change the installation path, use -`UV_INSTALL_DIR`: +By default, uv is installed to `~/.local/bin`. If `XDG_BIN_HOME` is set, it will be used instead. +Similarly, if `XDG_DATA_HOME` is set, the target directory will be inferred as +`XDG_DATA_HOME/../bin`. + +To change the installation path, use `UV_INSTALL_DIR`: === "macOS and Linux" diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index e89a1534e75d..ff0481bc1683 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -185,14 +185,14 @@ If you need to remove uv from your system, just remove the `uv` and `uvx` binari === "macOS and Linux" ```console - $ rm ~/.cargo/bin/uv ~/.cargo/bin/uvx + $ rm ~/.local/bin/uv ~/.local/bin/uvx ``` === "Windows" ```powershell - $ rm $HOME\.cargo\bin\uv.exe - $ rm $HOME\.cargo\bin\uvx.exe + $ rm $HOME\.local\bin\uv.exe + $ rm $HOME\.local\bin\uvx.exe ``` !!! tip From 86ac93bb7273d68e3fb036595a81c04c6ff146ef Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Tue, 5 Nov 2024 10:58:32 -0600 Subject: [PATCH 07/19] Error when disallowed settings are defined in `uv.toml` (#8550) These settings can only be defined in `pyproject.toml`, since they're project-centric, and not _configuration_. Closes https://github.com/astral-sh/uv/issues/8539. --------- Co-authored-by: Zanie Blue Co-authored-by: Charlie Marsh Co-authored-by: konsti --- crates/uv-settings/src/combine.rs | 7 ++++ crates/uv-settings/src/lib.rs | 57 ++++++++++++++++++++----- crates/uv-settings/src/settings.rs | 62 +++++++++++++++++++--------- crates/uv-workspace/src/pyproject.rs | 16 +++---- crates/uv/tests/it/pip_install.rs | 22 ++++++++++ docs/reference/settings.md | 4 +- 6 files changed, 129 insertions(+), 39 deletions(-) diff --git a/crates/uv-settings/src/combine.rs b/crates/uv-settings/src/combine.rs index dfd39085e463..61cc7368feb7 100644 --- a/crates/uv-settings/src/combine.rs +++ b/crates/uv-settings/src/combine.rs @@ -1,5 +1,6 @@ use std::num::NonZeroUsize; use std::path::PathBuf; + use url::Url; use uv_configuration::{ @@ -124,3 +125,9 @@ impl Combine for serde::de::IgnoredAny { self } } + +impl Combine for Option { + fn combine(self, _other: Self) -> Self { + self + } +} diff --git a/crates/uv-settings/src/lib.rs b/crates/uv-settings/src/lib.rs index e937d7b8bb69..13f7e78e2daf 100644 --- a/crates/uv-settings/src/lib.rs +++ b/crates/uv-settings/src/lib.rs @@ -46,6 +46,7 @@ impl FilesystemOptions { match read_file(&file) { Ok(options) => { tracing::debug!("Found user configuration in: `{}`", file.display()); + validate_uv_toml(&file, &options)?; Ok(Some(Self(options))) } Err(Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => Ok(None), @@ -82,11 +83,11 @@ impl FilesystemOptions { Ok(None) => { // Continue traversing the directory tree. } - Err(Error::PyprojectToml(file, err)) => { + Err(Error::PyprojectToml(path, err)) => { // If we see an invalid `pyproject.toml`, warn but continue. warn_user!( "Failed to parse `{}` during settings discovery:\n{}", - file.cyan(), + path.user_display().cyan(), textwrap::indent(&err.to_string(), " ") ); } @@ -107,7 +108,7 @@ impl FilesystemOptions { match fs_err::read_to_string(&path) { Ok(content) => { let options: Options = toml::from_str(&content) - .map_err(|err| Error::UvToml(path.user_display().to_string(), err))?; + .map_err(|err| Error::UvToml(path.clone(), Box::new(err)))?; // If the directory also contains a `[tool.uv]` table in a `pyproject.toml` file, // warn. @@ -124,6 +125,7 @@ impl FilesystemOptions { } tracing::debug!("Found workspace configuration at `{}`", path.display()); + validate_uv_toml(&path, &options)?; return Ok(Some(Self(options))); } Err(err) if err.kind() == std::io::ErrorKind::NotFound => {} @@ -136,7 +138,7 @@ impl FilesystemOptions { Ok(content) => { // Parse, but skip any `pyproject.toml` that doesn't have a `[tool.uv]` section. let pyproject: PyProjectToml = toml::from_str(&content) - .map_err(|err| Error::PyprojectToml(path.user_display().to_string(), err))?; + .map_err(|err| Error::PyprojectToml(path.clone(), Box::new(err)))?; let Some(tool) = pyproject.tool else { tracing::debug!( "Skipping `pyproject.toml` in `{}` (no `[tool]` section)", @@ -244,21 +246,56 @@ fn system_config_file() -> Option { /// Load [`Options`] from a `uv.toml` file. fn read_file(path: &Path) -> Result { let content = fs_err::read_to_string(path)?; - let options: Options = toml::from_str(&content) - .map_err(|err| Error::UvToml(path.user_display().to_string(), err))?; + let options: Options = + toml::from_str(&content).map_err(|err| Error::UvToml(path.to_path_buf(), Box::new(err)))?; Ok(options) } +/// Validate that an [`Options`] schema is compatible with `uv.toml`. +fn validate_uv_toml(path: &Path, options: &Options) -> Result<(), Error> { + // The `uv.toml` format is not allowed to include any of the following, which are + // permitted by the schema since they _can_ be included in `pyproject.toml` files + // (and we want to use `deny_unknown_fields`). + if options.workspace.is_some() { + return Err(Error::PyprojectOnlyField(path.to_path_buf(), "workspace")); + } + if options.sources.is_some() { + return Err(Error::PyprojectOnlyField(path.to_path_buf(), "sources")); + } + if options.dev_dependencies.is_some() { + return Err(Error::PyprojectOnlyField( + path.to_path_buf(), + "dev-dependencies", + )); + } + if options.default_groups.is_some() { + return Err(Error::PyprojectOnlyField( + path.to_path_buf(), + "default-groups", + )); + } + if options.managed.is_some() { + return Err(Error::PyprojectOnlyField(path.to_path_buf(), "managed")); + } + if options.package.is_some() { + return Err(Error::PyprojectOnlyField(path.to_path_buf(), "package")); + } + Ok(()) +} + #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] Io(#[from] std::io::Error), - #[error("Failed to parse: `{0}`")] - PyprojectToml(String, #[source] toml::de::Error), + #[error("Failed to parse: `{}`", _0.user_display())] + PyprojectToml(PathBuf, #[source] Box), + + #[error("Failed to parse: `{}`", _0.user_display())] + UvToml(PathBuf, #[source] Box), - #[error("Failed to parse: `{0}`")] - UvToml(String, #[source] toml::de::Error), + #[error("Failed to parse: `{}`. The `{1}` field is not allowed in a `uv.toml` file. `{1}` is only applicable in the context of a project, and should be placed in a `pyproject.toml` file instead.", _0.user_display())] + PyprojectOnlyField(PathBuf, &'static str), } #[cfg(test)] diff --git a/crates/uv-settings/src/settings.rs b/crates/uv-settings/src/settings.rs index 6ccb7d5eb752..5e0ee6051f0d 100644 --- a/crates/uv-settings/src/settings.rs +++ b/crates/uv-settings/src/settings.rs @@ -86,7 +86,8 @@ pub struct Options { cache_keys: Option>, // NOTE(charlie): These fields are shared with `ToolUv` in - // `crates/uv-workspace/src/pyproject.rs`, and the documentation lives on that struct. + // `crates/uv-workspace/src/pyproject.rs`. The documentation lives on that struct. + // They're respected in both `pyproject.toml` and `uv.toml` files. #[cfg_attr(feature = "schemars", schemars(skip))] pub override_dependencies: Option>>, @@ -95,6 +96,27 @@ pub struct Options { #[cfg_attr(feature = "schemars", schemars(skip))] pub environments: Option, + + // NOTE(charlie): These fields should be kept in-sync with `ToolUv` in + // `crates/uv-workspace/src/pyproject.rs`. The documentation lives on that struct. + // They're only respected in `pyproject.toml` files, and should be rejected in `uv.toml` files. + #[cfg_attr(feature = "schemars", schemars(skip))] + pub workspace: Option, + + #[cfg_attr(feature = "schemars", schemars(skip))] + pub sources: Option, + + #[cfg_attr(feature = "schemars", schemars(skip))] + pub dev_dependencies: Option, + + #[cfg_attr(feature = "schemars", schemars(skip))] + pub default_groups: Option, + + #[cfg_attr(feature = "schemars", schemars(skip))] + pub managed: Option, + + #[cfg_attr(feature = "schemars", schemars(skip))] + pub r#package: Option, } impl Options { @@ -1551,24 +1573,20 @@ pub struct OptionsWire { cache_keys: Option>, // NOTE(charlie): These fields are shared with `ToolUv` in - // `crates/uv-workspace/src/pyproject.rs`, and the documentation lives on that struct. + // `crates/uv-workspace/src/pyproject.rs`. The documentation lives on that struct. + // They're respected in both `pyproject.toml` and `uv.toml` files. override_dependencies: Option>>, constraint_dependencies: Option>>, environments: Option, // NOTE(charlie): These fields should be kept in-sync with `ToolUv` in - // `crates/uv-workspace/src/pyproject.rs`. - #[allow(dead_code)] + // `crates/uv-workspace/src/pyproject.rs`. The documentation lives on that struct. + // They're only respected in `pyproject.toml` files, and should be rejected in `uv.toml` files. workspace: Option, - #[allow(dead_code)] sources: Option, - #[allow(dead_code)] managed: Option, - #[allow(dead_code)] r#package: Option, - #[allow(dead_code)] default_groups: Option, - #[allow(dead_code)] dev_dependencies: Option, } @@ -1618,12 +1636,12 @@ impl From for Options { environments, publish_url, trusted_publishing, - workspace: _, - sources: _, - managed: _, - package: _, - default_groups: _, - dev_dependencies: _, + workspace, + sources, + default_groups, + dev_dependencies, + managed, + package, } = value; Self { @@ -1667,15 +1685,21 @@ impl From for Options { no_binary, no_binary_package, }, - publish: PublishOptions { - publish_url, - trusted_publishing, - }, pip, cache_keys, override_dependencies, constraint_dependencies, environments, + publish: PublishOptions { + publish_url, + trusted_publishing, + }, + workspace, + sources, + dev_dependencies, + default_groups, + managed, + package, } } } diff --git a/crates/uv-workspace/src/pyproject.rs b/crates/uv-workspace/src/pyproject.rs index 10d077501c95..b7dfe409c89d 100644 --- a/crates/uv-workspace/src/pyproject.rs +++ b/crates/uv-workspace/src/pyproject.rs @@ -230,7 +230,7 @@ pub struct ToolUv { /// /// See [Dependencies](../concepts/dependencies.md) for more. #[option( - default = "\"[]\"", + default = "{}", value_type = "dict", example = r#" [tool.uv.sources] @@ -269,7 +269,7 @@ pub struct ToolUv { /// given the lowest priority when resolving packages. Additionally, marking an index as default will disable the /// PyPI default index. #[option( - default = "\"[]\"", + default = "[]", value_type = "dict", example = r#" [[tool.uv.index]] @@ -340,7 +340,7 @@ pub struct ToolUv { ) )] #[option( - default = r#"[]"#, + default = "[]", value_type = "list[str]", example = r#" dev-dependencies = ["ruff==0.5.0"] @@ -374,7 +374,7 @@ pub struct ToolUv { ) )] #[option( - default = r#"[]"#, + default = "[]", value_type = "list[str]", example = r#" # Always install Werkzeug 2.3.0, regardless of whether transitive dependencies request @@ -405,7 +405,7 @@ pub struct ToolUv { ) )] #[option( - default = r#"[]"#, + default = "[]", value_type = "list[str]", example = r#" # Ensure that the grpcio version is always less than 1.65, if it's requested by a @@ -431,7 +431,7 @@ pub struct ToolUv { ) )] #[option( - default = r#"[]"#, + default = "[]", value_type = "str | list[str]", example = r#" # Resolve for macOS, but not for Linux or Windows. @@ -511,7 +511,7 @@ pub struct ToolUvWorkspace { /// /// For more information on the glob syntax, refer to the [`glob` documentation](https://docs.rs/glob/latest/glob/struct.Pattern.html). #[option( - default = r#"[]"#, + default = "[]", value_type = "list[str]", example = r#" members = ["member1", "path/to/member2", "libs/*"] @@ -525,7 +525,7 @@ pub struct ToolUvWorkspace { /// /// For more information on the glob syntax, refer to the [`glob` documentation](https://docs.rs/glob/latest/glob/struct.Pattern.html). #[option( - default = r#"[]"#, + default = "[]", value_type = "list[str]", example = r#" exclude = ["member1", "path/to/member2", "libs/*"] diff --git a/crates/uv/tests/it/pip_install.rs b/crates/uv/tests/it/pip_install.rs index 1862e80e7759..6c18b305ee13 100644 --- a/crates/uv/tests/it/pip_install.rs +++ b/crates/uv/tests/it/pip_install.rs @@ -201,6 +201,28 @@ fn invalid_pyproject_toml_option_unknown_field() -> Result<()> { Ok(()) } +#[test] +fn invalid_uv_toml_option_disallowed() -> Result<()> { + let context = TestContext::new("3.12"); + let uv_toml = context.temp_dir.child("uv.toml"); + uv_toml.write_str(indoc! {r" + managed = true + "})?; + + uv_snapshot!(context.pip_install() + .arg("iniconfig"), @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: Failed to parse: `uv.toml`. The `managed` field is not allowed in a `uv.toml` file. `managed` is only applicable in the context of a project, and should be placed in a `pyproject.toml` file instead. + "### + ); + + Ok(()) +} + /// For indirect, non-user controlled pyproject.toml, we don't enforce correctness. /// /// If we fail to extract the PEP 621 metadata, we fall back to treating it as a source diff --git a/docs/reference/settings.md b/docs/reference/settings.md index 222eed8be524..43672ae1ebb5 100644 --- a/docs/reference/settings.md +++ b/docs/reference/settings.md @@ -127,7 +127,7 @@ If an index is marked as `default = true`, it will be moved to the end of the pr given the lowest priority when resolving packages. Additionally, marking an index as default will disable the PyPI default index. -**Default value**: `"[]"` +**Default value**: `[]` **Type**: `dict` @@ -232,7 +232,7 @@ alternative registry. See [Dependencies](../concepts/dependencies.md) for more. -**Default value**: `"[]"` +**Default value**: `{}` **Type**: `dict` From 4476099f02820637d354fa5bad97973a9c971b88 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Mon, 28 Oct 2024 12:06:25 -0400 Subject: [PATCH 08/19] Use base executable to set virtualenv Python path (#8481) See extensive discussion in https://github.com/astral-sh/uv/pull/8433#issuecomment-2430472849. This PR brings us into alignment with the standard library by using `sys._base_executable` rather than canonicalizing the executable path. The benefits are primarily for Homebrew, where we'll now resolve to paths like `/opt/homebrew/opt/python@3.12/bin` instead of the undesirable `/opt/homebrew/Cellar/python@3.9/3.9.19_1/Frameworks/Python.framework/Versions/3.9/bin`. Most other users should see no change, though in some cases, nested virtual environments now have slightly different behavior -- namely, they _sometimes_ resolve to the virtual environment Python (at least for Homebrew; not for rtx or uv Pythons though). See [here](https://docs.google.com/spreadsheets/d/1Vw5ClYEjgrBJJhQiwa3cCenIA1GbcRyudYN9NwQaEcM/edit?gid=0#gid=0) for a breakdown. Closes https://github.com/astral-sh/uv/issues/1640. Closes https://github.com/astral-sh/uv/issues/1795. --- .../uv-python/python/get_interpreter_info.py | 1 + crates/uv-python/src/interpreter.rs | 21 +++++++++++ crates/uv-virtualenv/src/virtualenv.rs | 35 +++++++------------ 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/crates/uv-python/python/get_interpreter_info.py b/crates/uv-python/python/get_interpreter_info.py index d056d3851eb7..f39506bfebd9 100644 --- a/crates/uv-python/python/get_interpreter_info.py +++ b/crates/uv-python/python/get_interpreter_info.py @@ -563,6 +563,7 @@ def main() -> None: "sys_executable": sys.executable, "sys_path": sys.path, "stdlib": sysconfig.get_path("stdlib"), + "sysconfig_prefix": sysconfig.get_config_var("prefix"), "scheme": get_scheme(), "virtualenv": get_virtualenv(), "platform": os_and_arch, diff --git a/crates/uv-python/src/interpreter.rs b/crates/uv-python/src/interpreter.rs index 36b713a6141e..c7bd1740a360 100644 --- a/crates/uv-python/src/interpreter.rs +++ b/crates/uv-python/src/interpreter.rs @@ -45,6 +45,7 @@ pub struct Interpreter { sys_executable: PathBuf, sys_path: Vec, stdlib: PathBuf, + sysconfig_prefix: Option, tags: OnceLock, target: Option, prefix: Option, @@ -78,6 +79,7 @@ impl Interpreter { sys_executable: info.sys_executable, sys_path: info.sys_path, stdlib: info.stdlib, + sysconfig_prefix: info.sysconfig_prefix, tags: OnceLock::new(), target: None, prefix: None, @@ -365,6 +367,11 @@ impl Interpreter { &self.stdlib } + /// Return the `prefix` path for this Python interpreter, as returned by `sysconfig.get_config_var("prefix")`. + pub fn sysconfig_prefix(&self) -> Option<&Path> { + self.sysconfig_prefix.as_deref() + } + /// Return the `purelib` path for this Python interpreter, as returned by `sysconfig.get_paths()`. pub fn purelib(&self) -> &Path { &self.scheme.purelib @@ -424,6 +431,19 @@ impl Interpreter { self.prefix.as_ref() } + /// Returns `true` if an [`Interpreter`] may be a `python-build-standalone` interpreter. + /// + /// This method may return false positives, but it should not return false negatives. In other + /// words, if this method returns `true`, the interpreter _may_ be from + /// `python-build-standalone`; if it returns `false`, the interpreter is definitely _not_ from + /// `python-build-standalone`. + /// + /// See: + pub fn is_standalone(&self) -> bool { + self.sysconfig_prefix() + .is_some_and(|prefix| prefix == Path::new("/install")) + } + /// Return the [`Layout`] environment used to install wheels into this interpreter. pub fn layout(&self) -> Layout { Layout { @@ -605,6 +625,7 @@ struct InterpreterInfo { sys_executable: PathBuf, sys_path: Vec, stdlib: PathBuf, + sysconfig_prefix: Option, pointer_size: PointerSize, gil_disabled: bool, } diff --git a/crates/uv-virtualenv/src/virtualenv.rs b/crates/uv-virtualenv/src/virtualenv.rs index 70f0ca22e06d..d11ecc1fb134 100644 --- a/crates/uv-virtualenv/src/virtualenv.rs +++ b/crates/uv-virtualenv/src/virtualenv.rs @@ -57,31 +57,20 @@ pub(crate) fn create( // considered the "base" for the virtual environment. This is typically the Python executable // from the [`Interpreter`]; however, if the interpreter is a virtual environment itself, then // the base Python executable is the Python executable of the interpreter's base interpreter. - let base_python = if cfg!(unix) { - // On Unix, follow symlinks to resolve the base interpreter, since the Python executable in - // a virtual environment is a symlink to the base interpreter. - uv_fs::canonicalize_executable(interpreter.sys_executable())? - } else if cfg!(windows) { - // On Windows, follow `virtualenv`. If we're in a virtual environment, use - // `sys._base_executable` if it exists; if not, use `sys.base_prefix`. For example, with - // Python installed from the Windows Store, `sys.base_prefix` is slightly "incorrect". + let base_python = if cfg!(unix) && interpreter.is_standalone() { + // In `python-build-standalone`, a symlinked interpreter will return its own executable path + // as `sys._base_executable`. Using the symlinked path as the base Python executable is + // incorrect, since it will cause `home` to point to something that is _not_ a Python + // installation. // - // If we're _not_ in a virtual environment, use the interpreter's executable, since it's - // already a "system Python". We canonicalize the path to ensure that it's real and - // consistent, though we don't expect any symlinks on Windows. - if interpreter.is_virtualenv() { - if let Some(base_executable) = interpreter.sys_base_executable() { - base_executable.to_path_buf() - } else { - // Assume `python.exe`, though the exact executable name is never used (below) on - // Windows, only its parent directory. - interpreter.sys_base_prefix().join("python.exe") - } - } else { - interpreter.sys_executable().to_path_buf() - } + // Instead, we want to fully resolve the symlink to the actual Python executable. + uv_fs::canonicalize_executable(interpreter.sys_executable())? } else { - unimplemented!("Only Windows and Unix are supported") + std::path::absolute( + interpreter + .sys_base_executable() + .unwrap_or(interpreter.sys_executable()), + )? }; // Validate the existing location. From 264c58a3e440f8ce45022297e2094e13e65c3b29 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Thu, 7 Nov 2024 14:18:13 -0600 Subject: [PATCH 09/19] Make `--allow-insecure-host` a global option (#8476) Not verifying the certificates of certain hosts should be supported for all kinds of HTTPS connections, so we're making it a global option, just like native tls. This fixes the remaining places using a client but were not configuring allow insecure host. Fixes #6983 (i think) Closes #6983 --------- Co-authored-by: Charlie Marsh --- crates/uv-cli/src/lib.rs | 137 ++------ crates/uv-cli/src/options.rs | 35 -- crates/uv-settings/src/settings.rs | 58 +-- crates/uv/src/commands/build_frontend.rs | 7 +- crates/uv/src/commands/pip/compile.rs | 4 +- crates/uv/src/commands/pip/install.rs | 4 +- crates/uv/src/commands/pip/sync.rs | 4 +- crates/uv/src/commands/pip/uninstall.rs | 4 +- crates/uv/src/commands/project/add.rs | 14 +- crates/uv/src/commands/project/environment.rs | 5 +- crates/uv/src/commands/project/export.rs | 5 +- crates/uv/src/commands/project/init.rs | 15 +- crates/uv/src/commands/project/lock.rs | 11 +- crates/uv/src/commands/project/mod.rs | 20 +- crates/uv/src/commands/project/remove.rs | 5 + crates/uv/src/commands/project/run.rs | 22 +- crates/uv/src/commands/project/sync.rs | 8 +- crates/uv/src/commands/project/tree.rs | 10 +- crates/uv/src/commands/publish.rs | 6 +- crates/uv/src/commands/python/install.rs | 3 + crates/uv/src/commands/tool/install.rs | 15 +- crates/uv/src/commands/tool/run.rs | 14 +- crates/uv/src/commands/tool/upgrade.rs | 11 +- crates/uv/src/commands/venv.rs | 9 +- crates/uv/src/lib.rs | 29 +- crates/uv/src/settings.rs | 78 ++--- crates/uv/tests/it/help.rs | 329 ++++++++++++------ crates/uv/tests/it/show_settings.rs | 81 +++-- docs/reference/cli.md | 247 +++++++++++-- docs/reference/settings.md | 33 -- uv.schema.json | 10 - 31 files changed, 711 insertions(+), 522 deletions(-) diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index d9074451f913..74a7f967db74 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -205,6 +205,26 @@ pub struct GlobalArgs { #[arg(global = true, long, overrides_with("offline"), hide = true)] pub no_offline: bool, + /// Allow insecure connections to a host. + /// + /// Can be provided multiple times. + /// + /// Expects to receive either a hostname (e.g., `localhost`), a host-port pair (e.g., + /// `localhost:8080`), or a URL (e.g., `https://localhost`). + /// + /// WARNING: Hosts included in this list will not be verified against the system's certificate + /// store. Only use `--allow-insecure-host` in a secure network with verified sources, as it + /// bypasses SSL verification and could expose you to MITM attacks. + #[arg( + global = true, + long, + alias = "trusted-host", + env = EnvVars::UV_INSECURE_HOST, + value_delimiter = ' ', + value_parser = parse_insecure_host, + )] + pub allow_insecure_host: Option>>, + /// Whether to enable experimental, preview features. /// /// Preview features may change without warning. @@ -1768,25 +1788,6 @@ pub struct PipUninstallArgs { #[arg(long, value_enum, env = EnvVars::UV_KEYRING_PROVIDER)] pub keyring_provider: Option, - /// Allow insecure connections to a host. - /// - /// Can be provided multiple times. - /// - /// Expects to receive either a hostname (e.g., `localhost`), a host-port pair (e.g., - /// `localhost:8080`), or a URL (e.g., `https://localhost`). - /// - /// WARNING: Hosts included in this list will not be verified against the system's certificate - /// store. Only use `--allow-insecure-host` in a secure network with verified sources, as it - /// bypasses SSL verification and could expose you to MITM attacks. - #[arg( - long, - alias = "trusted-host", - env = EnvVars::UV_INSECURE_HOST, - value_delimiter = ' ', - value_parser = parse_insecure_host, - )] - pub allow_insecure_host: Option>>, - /// Use the system Python to uninstall packages. /// /// By default, uv uninstalls from the virtual environment in the current working directory or @@ -2370,25 +2371,6 @@ pub struct VenvArgs { #[arg(long, value_enum, env = EnvVars::UV_KEYRING_PROVIDER)] pub keyring_provider: Option, - /// Allow insecure connections to a host. - /// - /// Can be provided multiple times. - /// - /// Expects to receive either a hostname (e.g., `localhost`), a host-port pair (e.g., - /// `localhost:8080`), or a URL (e.g., `https://localhost`). - /// - /// WARNING: Hosts included in this list will not be verified against the system's certificate - /// store. Only use `--allow-insecure-host` in a secure network with verified sources, as it - /// bypasses SSL verification and could expose you to MITM attacks. - #[arg( - long, - alias = "trusted-host", - env = EnvVars::UV_INSECURE_HOST, - value_delimiter = ' ', - value_parser = parse_insecure_host, - )] - pub allow_insecure_host: Option>>, - /// Limit candidate packages to those that were uploaded prior to the given date. /// /// Accepts both RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`) and local dates in the same @@ -4284,26 +4266,6 @@ pub struct InstallerArgs { )] pub keyring_provider: Option, - /// Allow insecure connections to a host. - /// - /// Can be provided multiple times. - /// - /// Expects to receive either a hostname (e.g., `localhost`), a host-port pair (e.g., - /// `localhost:8080`), or a URL (e.g., `https://localhost`). - /// - /// WARNING: Hosts included in this list will not be verified against the system's certificate - /// store. Only use `--allow-insecure-host` in a secure network with verified sources, as it - /// bypasses SSL verification and could expose you to MITM attacks. - #[arg( - long, - alias = "trusted-host", - env = EnvVars::UV_INSECURE_HOST, - value_delimiter = ' ', - value_parser = parse_insecure_host, - help_heading = "Index options" - )] - pub allow_insecure_host: Option>>, - /// Settings to pass to the PEP 517 build backend, specified as `KEY=VALUE` pairs. #[arg( long, @@ -4446,26 +4408,6 @@ pub struct ResolverArgs { )] pub keyring_provider: Option, - /// Allow insecure connections to a host. - /// - /// Can be provided multiple times. - /// - /// Expects to receive either a hostname (e.g., `localhost`), a host-port pair (e.g., - /// `localhost:8080`), or a URL (e.g., `https://localhost`). - /// - /// WARNING: Hosts included in this list will not be verified against the system's certificate - /// store. Only use `--allow-insecure-host` in a secure network with verified sources, as it - /// bypasses SSL verification and could expose you to MITM attacks. - #[arg( - long, - alias = "trusted-host", - env = EnvVars::UV_INSECURE_HOST, - value_delimiter = ' ', - value_parser = parse_insecure_host, - help_heading = "Index options" - )] - pub allow_insecure_host: Option>>, - /// The strategy to use when selecting between the different compatible versions for a given /// package requirement. /// @@ -4638,26 +4580,6 @@ pub struct ResolverInstallerArgs { )] pub keyring_provider: Option, - /// Allow insecure connections to a host. - /// - /// Can be provided multiple times. - /// - /// Expects to receive either a hostname (e.g., `localhost`), a host-port pair (e.g., - /// `localhost:8080`), or a URL (e.g., `https://localhost`). - /// - /// WARNING: Hosts included in this list will not be verified against the system's certificate - /// store. Only use `--allow-insecure-host` in a secure network with verified sources, as it - /// bypasses SSL verification and could expose you to MITM attacks. - #[arg( - long, - alias = "trusted-host", - env = EnvVars::UV_INSECURE_HOST, - value_delimiter = ' ', - value_parser = parse_insecure_host, - help_heading = "Index options" - )] - pub allow_insecure_host: Option>>, - /// The strategy to use when selecting between the different compatible versions for a given /// package requirement. /// @@ -4864,25 +4786,6 @@ pub struct PublishArgs { #[arg(long, value_enum, env = EnvVars::UV_KEYRING_PROVIDER)] pub keyring_provider: Option, - /// Allow insecure connections to a host. - /// - /// Can be provided multiple times. - /// - /// Expects to receive either a hostname (e.g., `localhost`), a host-port pair (e.g., - /// `localhost:8080`), or a URL (e.g., `https://localhost`). - /// - /// WARNING: Hosts included in this list will not be verified against the system's certificate - /// store. Only use `--allow-insecure-host` in a secure network with verified sources, as it - /// bypasses SSL verification and could expose you to MITM attacks. - #[arg( - long, - alias = "trusted-host", - env = EnvVars::UV_INSECURE_HOST, - value_delimiter = ' ', - value_parser = parse_insecure_host, - )] - pub allow_insecure_host: Option>>, - /// Check an index URL for existing files to skip duplicate uploads. /// /// This option allows retrying publishing that failed after only some, but not all files have diff --git a/crates/uv-cli/src/options.rs b/crates/uv-cli/src/options.rs index 404129793856..771585990835 100644 --- a/crates/uv-cli/src/options.rs +++ b/crates/uv-cli/src/options.rs @@ -40,7 +40,6 @@ impl From for PipOptions { upgrade_package, index_strategy, keyring_provider, - allow_insecure_host, resolution, prerelease, pre, @@ -58,12 +57,6 @@ impl From for PipOptions { upgrade_package: Some(upgrade_package), index_strategy, keyring_provider, - allow_insecure_host: allow_insecure_host.map(|allow_insecure_host| { - allow_insecure_host - .into_iter() - .filter_map(Maybe::into_option) - .collect() - }), resolution, prerelease: if pre { Some(PrereleaseMode::Allow) @@ -91,7 +84,6 @@ impl From for PipOptions { reinstall_package, index_strategy, keyring_provider, - allow_insecure_host, config_setting, no_build_isolation, build_isolation, @@ -107,12 +99,6 @@ impl From for PipOptions { reinstall_package: Some(reinstall_package), index_strategy, keyring_provider, - allow_insecure_host: allow_insecure_host.map(|allow_insecure_host| { - allow_insecure_host - .into_iter() - .filter_map(Maybe::into_option) - .collect() - }), config_settings: config_setting .map(|config_settings| config_settings.into_iter().collect::()), no_build_isolation: flag(no_build_isolation, build_isolation), @@ -137,7 +123,6 @@ impl From for PipOptions { reinstall_package, index_strategy, keyring_provider, - allow_insecure_host, resolution, prerelease, pre, @@ -159,12 +144,6 @@ impl From for PipOptions { reinstall_package: Some(reinstall_package), index_strategy, keyring_provider, - allow_insecure_host: allow_insecure_host.map(|allow_insecure_host| { - allow_insecure_host - .into_iter() - .filter_map(Maybe::into_option) - .collect() - }), resolution, prerelease: if pre { Some(PrereleaseMode::Allow) @@ -235,7 +214,6 @@ pub fn resolver_options( upgrade_package, index_strategy, keyring_provider, - allow_insecure_host, resolution, prerelease, pre, @@ -289,12 +267,6 @@ pub fn resolver_options( upgrade_package: Some(upgrade_package), index_strategy, keyring_provider, - allow_insecure_host: allow_insecure_host.map(|allow_insecure_host| { - allow_insecure_host - .into_iter() - .filter_map(Maybe::into_option) - .collect() - }), resolution, prerelease: if pre { Some(PrereleaseMode::Allow) @@ -331,7 +303,6 @@ pub fn resolver_installer_options( reinstall_package, index_strategy, keyring_provider, - allow_insecure_host, resolution, prerelease, pre, @@ -397,12 +368,6 @@ pub fn resolver_installer_options( }, index_strategy, keyring_provider, - allow_insecure_host: allow_insecure_host.map(|allow_insecure_host| { - allow_insecure_host - .into_iter() - .filter_map(Maybe::into_option) - .collect() - }), resolution, prerelease: if pre { Some(PrereleaseMode::Allow) diff --git a/crates/uv-settings/src/settings.rs b/crates/uv-settings/src/settings.rs index 5e0ee6051f0d..bab2d0d1c02a 100644 --- a/crates/uv-settings/src/settings.rs +++ b/crates/uv-settings/src/settings.rs @@ -246,6 +246,22 @@ pub struct GlobalOptions { "# )] pub concurrent_installs: Option, + /// Allow insecure connections to host. + /// + /// Expects to receive either a hostname (e.g., `localhost`), a host-port pair (e.g., + /// `localhost:8080`), or a URL (e.g., `https://localhost`). + /// + /// WARNING: Hosts included in this list will not be verified against the system's certificate + /// store. Only use `--allow-insecure-host` in a secure network with verified sources, as it + /// bypasses SSL verification and could expose you to MITM attacks. + #[option( + default = "[]", + value_type = "list[str]", + example = r#" + allow-insecure-host = ["localhost:8080"] + "# + )] + pub allow_insecure_host: Option>, } /// Settings relevant to all installer operations. @@ -258,7 +274,6 @@ pub struct InstallerOptions { pub find_links: Option>, pub index_strategy: Option, pub keyring_provider: Option, - pub allow_insecure_host: Option>, pub config_settings: Option, pub exclude_newer: Option, pub link_mode: Option, @@ -283,7 +298,6 @@ pub struct ResolverOptions { pub find_links: Option>, pub index_strategy: Option, pub keyring_provider: Option, - pub allow_insecure_host: Option>, pub resolution: Option, pub prerelease: Option, pub dependency_metadata: Option>, @@ -435,22 +449,6 @@ pub struct ResolverInstallerOptions { "# )] pub keyring_provider: Option, - /// Allow insecure connections to host. - /// - /// Expects to receive either a hostname (e.g., `localhost`), a host-port pair (e.g., - /// `localhost:8080`), or a URL (e.g., `https://localhost`). - /// - /// WARNING: Hosts included in this list will not be verified against the system's certificate - /// store. Only use `--allow-insecure-host` in a secure network with verified sources, as it - /// bypasses SSL verification and could expose you to MITM attacks. - #[option( - default = "[]", - value_type = "list[str]", - example = r#" - allow-insecure-host = ["localhost:8080"] - "# - )] - pub allow_insecure_host: Option>, /// The strategy to use when selecting between the different compatible versions for a given /// package requirement. /// @@ -847,22 +845,6 @@ pub struct PipOptions { "# )] pub keyring_provider: Option, - /// Allow insecure connections to host. - /// - /// Expects to receive either a hostname (e.g., `localhost`), a host-port pair (e.g., - /// `localhost:8080`), or a URL (e.g., `https://localhost`). - /// - /// WARNING: Hosts included in this list will not be verified against the system's certificate - /// store. Only use `--allow-insecure-host` in a secure network with verified sources, as it - /// bypasses SSL verification and could expose you to MITM attacks. - #[option( - default = "[]", - value_type = "list[str]", - example = r#" - allow-insecure-host = ["localhost:8080"] - "# - )] - pub allow_insecure_host: Option>, /// Don't build source distributions. /// /// When enabled, resolving will not run arbitrary Python code. The cached wheels of @@ -1374,7 +1356,6 @@ impl From for ResolverOptions { find_links: value.find_links, index_strategy: value.index_strategy, keyring_provider: value.keyring_provider, - allow_insecure_host: value.allow_insecure_host, resolution: value.resolution, prerelease: value.prerelease, dependency_metadata: value.dependency_metadata, @@ -1404,7 +1385,6 @@ impl From for InstallerOptions { find_links: value.find_links, index_strategy: value.index_strategy, keyring_provider: value.keyring_provider, - allow_insecure_host: value.allow_insecure_host, config_settings: value.config_settings, exclude_newer: value.exclude_newer, link_mode: value.link_mode, @@ -1438,7 +1418,6 @@ pub struct ToolOptions { pub find_links: Option>, pub index_strategy: Option, pub keyring_provider: Option, - pub allow_insecure_host: Option>, pub resolution: Option, pub prerelease: Option, pub dependency_metadata: Option>, @@ -1465,7 +1444,6 @@ impl From for ToolOptions { find_links: value.find_links, index_strategy: value.index_strategy, keyring_provider: value.keyring_provider, - allow_insecure_host: value.allow_insecure_host, resolution: value.resolution, prerelease: value.prerelease, dependency_metadata: value.dependency_metadata, @@ -1494,7 +1472,6 @@ impl From for ResolverInstallerOptions { find_links: value.find_links, index_strategy: value.index_strategy, keyring_provider: value.keyring_provider, - allow_insecure_host: value.allow_insecure_host, resolution: value.resolution, prerelease: value.prerelease, dependency_metadata: value.dependency_metadata, @@ -1656,6 +1633,8 @@ impl From for Options { concurrent_downloads, concurrent_builds, concurrent_installs, + // Used twice for backwards compatibility + allow_insecure_host: allow_insecure_host.clone(), }, top_level: ResolverInstallerOptions { index, @@ -1665,7 +1644,6 @@ impl From for Options { find_links, index_strategy, keyring_provider, - allow_insecure_host, resolution, prerelease, dependency_metadata, diff --git a/crates/uv/src/commands/build_frontend.rs b/crates/uv/src/commands/build_frontend.rs index 4823fdf573f3..602c59493518 100644 --- a/crates/uv/src/commands/build_frontend.rs +++ b/crates/uv/src/commands/build_frontend.rs @@ -58,6 +58,7 @@ pub(crate) async fn build_frontend( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result { @@ -80,6 +81,7 @@ pub(crate) async fn build_frontend( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -120,6 +122,7 @@ async fn build_impl( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result { @@ -128,7 +131,6 @@ async fn build_impl( index_locations, index_strategy, keyring_provider, - allow_insecure_host, resolution: _, prerelease: _, dependency_metadata, @@ -144,7 +146,8 @@ async fn build_impl( let client_builder = BaseClientBuilder::default() .connectivity(connectivity) - .native_tls(native_tls); + .native_tls(native_tls) + .allow_insecure_host(allow_insecure_host.to_vec()); // Determine the source to build. let src = if let Some(src) = src { diff --git a/crates/uv/src/commands/pip/compile.rs b/crates/uv/src/commands/pip/compile.rs index b3a92a67ab00..59ba8a26b97c 100644 --- a/crates/uv/src/commands/pip/compile.rs +++ b/crates/uv/src/commands/pip/compile.rs @@ -76,7 +76,7 @@ pub(crate) async fn pip_compile( index_strategy: IndexStrategy, dependency_metadata: DependencyMetadata, keyring_provider: KeyringProviderType, - allow_insecure_host: Vec, + allow_insecure_host: &[TrustedHost], config_settings: ConfigSettings, connectivity: Connectivity, no_build_isolation: bool, @@ -110,7 +110,7 @@ pub(crate) async fn pip_compile( .connectivity(connectivity) .native_tls(native_tls) .keyring(keyring_provider) - .allow_insecure_host(allow_insecure_host); + .allow_insecure_host(allow_insecure_host.to_vec()); // Read all requirements from the provided sources. let RequirementsSpecification { diff --git a/crates/uv/src/commands/pip/install.rs b/crates/uv/src/commands/pip/install.rs index 7f45f3001f99..13610f7ef98d 100644 --- a/crates/uv/src/commands/pip/install.rs +++ b/crates/uv/src/commands/pip/install.rs @@ -56,7 +56,6 @@ pub(crate) async fn pip_install( index_strategy: IndexStrategy, dependency_metadata: DependencyMetadata, keyring_provider: KeyringProviderType, - allow_insecure_host: Vec, reinstall: Reinstall, link_mode: LinkMode, compile: bool, @@ -79,6 +78,7 @@ pub(crate) async fn pip_install( prefix: Option, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: Cache, dry_run: bool, printer: Printer, @@ -89,7 +89,7 @@ pub(crate) async fn pip_install( .connectivity(connectivity) .native_tls(native_tls) .keyring(keyring_provider) - .allow_insecure_host(allow_insecure_host); + .allow_insecure_host(allow_insecure_host.to_vec()); // Read all requirements from the provided sources. let RequirementsSpecification { diff --git a/crates/uv/src/commands/pip/sync.rs b/crates/uv/src/commands/pip/sync.rs index 22d73d09417b..26b8175658d0 100644 --- a/crates/uv/src/commands/pip/sync.rs +++ b/crates/uv/src/commands/pip/sync.rs @@ -48,7 +48,6 @@ pub(crate) async fn pip_sync( index_strategy: IndexStrategy, dependency_metadata: DependencyMetadata, keyring_provider: KeyringProviderType, - allow_insecure_host: Vec, allow_empty_requirements: bool, connectivity: Connectivity, config_settings: &ConfigSettings, @@ -67,6 +66,7 @@ pub(crate) async fn pip_sync( sources: SourceStrategy, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: Cache, dry_run: bool, printer: Printer, @@ -75,7 +75,7 @@ pub(crate) async fn pip_sync( .connectivity(connectivity) .native_tls(native_tls) .keyring(keyring_provider) - .allow_insecure_host(allow_insecure_host); + .allow_insecure_host(allow_insecure_host.to_vec()); // Initialize a few defaults. let overrides = &[]; diff --git a/crates/uv/src/commands/pip/uninstall.rs b/crates/uv/src/commands/pip/uninstall.rs index d831eeb4a132..462bfc749c21 100644 --- a/crates/uv/src/commands/pip/uninstall.rs +++ b/crates/uv/src/commands/pip/uninstall.rs @@ -34,7 +34,7 @@ pub(crate) async fn pip_uninstall( connectivity: Connectivity, native_tls: bool, keyring_provider: KeyringProviderType, - allow_insecure_host: Vec, + allow_insecure_host: &[TrustedHost], printer: Printer, ) -> Result { let start = std::time::Instant::now(); @@ -43,7 +43,7 @@ pub(crate) async fn pip_uninstall( .connectivity(connectivity) .native_tls(native_tls) .keyring(keyring_provider) - .allow_insecure_host(allow_insecure_host); + .allow_insecure_host(allow_insecure_host.to_vec()); // Read all requirements from the provided sources. let spec = RequirementsSpecification::from_simple_sources(sources, &client_builder).await?; diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs index 3a5dfbf6b3d3..943d82b30bf1 100644 --- a/crates/uv/src/commands/project/add.rs +++ b/crates/uv/src/commands/project/add.rs @@ -15,6 +15,7 @@ use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClient use uv_configuration::{ Concurrency, Constraints, DevGroupsManifest, DevGroupsSpecification, DevMode, EditableMode, ExtrasSpecification, GroupsSpecification, InstallOptions, LowerBound, SourceStrategy, + TrustedHost, }; use uv_dispatch::BuildDispatch; use uv_distribution::DistributionDatabase; @@ -74,6 +75,7 @@ pub(crate) async fn add( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result { @@ -124,7 +126,8 @@ pub(crate) async fn add( let client_builder = BaseClientBuilder::new() .connectivity(connectivity) - .native_tls(native_tls); + .native_tls(native_tls) + .allow_insecure_host(allow_insecure_host.to_vec()); // If we found a script, add to the existing metadata. Otherwise, create a new inline // metadata tag. @@ -217,6 +220,7 @@ pub(crate) async fn add( python_downloads, connectivity, native_tls, + allow_insecure_host, cache, printer, ) @@ -228,7 +232,8 @@ pub(crate) async fn add( let client_builder = BaseClientBuilder::new() .connectivity(connectivity) .native_tls(native_tls) - .keyring(settings.keyring_provider); + .keyring(settings.keyring_provider) + .allow_insecure_host(allow_insecure_host.to_vec()); // Read the requirements. let RequirementsSpecification { requirements, .. } = @@ -636,6 +641,7 @@ pub(crate) async fn add( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -696,6 +702,7 @@ async fn lock_and_sync( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result<(), ProjectError> { @@ -715,6 +722,7 @@ async fn lock_and_sync( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -832,6 +840,7 @@ async fn lock_and_sync( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -895,6 +904,7 @@ async fn lock_and_sync( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) diff --git a/crates/uv/src/commands/project/environment.rs b/crates/uv/src/commands/project/environment.rs index c49711576174..7a5eb8e17d72 100644 --- a/crates/uv/src/commands/project/environment.rs +++ b/crates/uv/src/commands/project/environment.rs @@ -10,7 +10,7 @@ use crate::settings::ResolverInstallerSettings; use uv_cache::{Cache, CacheBucket}; use uv_cache_key::{cache_digest, hash_digest}; use uv_client::Connectivity; -use uv_configuration::Concurrency; +use uv_configuration::{Concurrency, TrustedHost}; use uv_distribution_types::Resolution; use uv_python::{Interpreter, PythonEnvironment}; @@ -37,6 +37,7 @@ impl CachedEnvironment { connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result { @@ -66,6 +67,7 @@ impl CachedEnvironment { connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -116,6 +118,7 @@ impl CachedEnvironment { connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) diff --git a/crates/uv/src/commands/project/export.rs b/crates/uv/src/commands/project/export.rs index e00efc7b00b6..c03fff53b332 100644 --- a/crates/uv/src/commands/project/export.rs +++ b/crates/uv/src/commands/project/export.rs @@ -9,7 +9,7 @@ use uv_cache::Cache; use uv_client::Connectivity; use uv_configuration::{ Concurrency, DevGroupsSpecification, EditableMode, ExportFormat, ExtrasSpecification, - InstallOptions, LowerBound, + InstallOptions, LowerBound, TrustedHost, }; use uv_normalize::PackageName; use uv_python::{PythonDownloads, PythonPreference, PythonRequest}; @@ -48,6 +48,7 @@ pub(crate) async fn export( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], quiet: bool, cache: &Cache, printer: Printer, @@ -103,6 +104,7 @@ pub(crate) async fn export( python_downloads, connectivity, native_tls, + allow_insecure_host, cache, printer, ) @@ -130,6 +132,7 @@ pub(crate) async fn export( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) diff --git a/crates/uv/src/commands/project/init.rs b/crates/uv/src/commands/project/init.rs index f0144b0706d1..3b3d1d1b9cf7 100644 --- a/crates/uv/src/commands/project/init.rs +++ b/crates/uv/src/commands/project/init.rs @@ -9,7 +9,9 @@ use tracing::{debug, warn}; use uv_cache::Cache; use uv_cli::AuthorFrom; use uv_client::{BaseClientBuilder, Connectivity}; -use uv_configuration::{ProjectBuildBackend, VersionControlError, VersionControlSystem}; +use uv_configuration::{ + ProjectBuildBackend, TrustedHost, VersionControlError, VersionControlSystem, +}; use uv_fs::{Simplified, CWD}; use uv_git::GIT; use uv_pep440::Version; @@ -48,6 +50,7 @@ pub(crate) async fn init( python_downloads: PythonDownloads, connectivity: Connectivity, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result { @@ -71,6 +74,7 @@ pub(crate) async fn init( no_pin_python, package, native_tls, + allow_insecure_host, ) .await?; @@ -126,6 +130,7 @@ pub(crate) async fn init( python_downloads, connectivity, native_tls, + allow_insecure_host, cache, printer, ) @@ -177,6 +182,7 @@ async fn init_script( no_pin_python: bool, package: bool, native_tls: bool, + allow_insecure_host: &[TrustedHost], ) -> Result<()> { if no_workspace { warn_user_once!("`--no-workspace` is a no-op for Python scripts, which are standalone"); @@ -192,7 +198,8 @@ async fn init_script( } let client_builder = BaseClientBuilder::new() .connectivity(connectivity) - .native_tls(native_tls); + .native_tls(native_tls) + .allow_insecure_host(allow_insecure_host.to_vec()); let reporter = PythonDownloadReporter::single(printer); @@ -258,6 +265,7 @@ async fn init_project( python_downloads: PythonDownloads, connectivity: Connectivity, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result<()> { @@ -307,7 +315,8 @@ async fn init_project( let reporter = PythonDownloadReporter::single(printer); let client_builder = BaseClientBuilder::new() .connectivity(connectivity) - .native_tls(native_tls); + .native_tls(native_tls) + .allow_insecure_host(allow_insecure_host.to_vec()); // Add a `requires-python` field to the `pyproject.toml` and return the corresponding interpreter. let (requires_python, python_request) = if let Some(request) = python.as_deref() { diff --git a/crates/uv/src/commands/project/lock.rs b/crates/uv/src/commands/project/lock.rs index 1ca451232950..c66d36cbb1df 100644 --- a/crates/uv/src/commands/project/lock.rs +++ b/crates/uv/src/commands/project/lock.rs @@ -11,7 +11,8 @@ use tracing::debug; use uv_cache::Cache; use uv_client::{Connectivity, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ - BuildOptions, Concurrency, Constraints, ExtrasSpecification, LowerBound, Reinstall, Upgrade, + BuildOptions, Concurrency, Constraints, ExtrasSpecification, LowerBound, Reinstall, + TrustedHost, Upgrade, }; use uv_dispatch::BuildDispatch; use uv_distribution::DistributionDatabase; @@ -82,6 +83,7 @@ pub(crate) async fn lock( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> anyhow::Result { @@ -101,6 +103,7 @@ pub(crate) async fn lock( python_downloads, connectivity, native_tls, + allow_insecure_host, cache, printer, ) @@ -130,6 +133,7 @@ pub(crate) async fn lock( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -204,6 +208,7 @@ pub(super) async fn do_safe_lock( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result { @@ -233,6 +238,7 @@ pub(super) async fn do_safe_lock( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -271,6 +277,7 @@ pub(super) async fn do_safe_lock( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -300,6 +307,7 @@ async fn do_lock( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result { @@ -310,7 +318,6 @@ async fn do_lock( index_locations, index_strategy, keyring_provider, - allow_insecure_host, resolution, prerelease, dependency_metadata, diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs index 7e2dfdd0a3e9..99c5b2d5d9e7 100644 --- a/crates/uv/src/commands/project/mod.rs +++ b/crates/uv/src/commands/project/mod.rs @@ -9,7 +9,7 @@ use uv_cache::Cache; use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ Concurrency, Constraints, DevGroupsSpecification, ExtrasSpecification, GroupsSpecification, - LowerBound, Reinstall, Upgrade, + LowerBound, Reinstall, TrustedHost, Upgrade, }; use uv_dispatch::BuildDispatch; use uv_distribution::DistributionDatabase; @@ -385,6 +385,7 @@ impl ProjectInterpreter { python_downloads: PythonDownloads, connectivity: Connectivity, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result { @@ -459,7 +460,8 @@ impl ProjectInterpreter { let client_builder = BaseClientBuilder::default() .connectivity(connectivity) - .native_tls(native_tls); + .native_tls(native_tls) + .allow_insecure_host(allow_insecure_host.to_vec()); let reporter = PythonDownloadReporter::single(printer); @@ -520,6 +522,7 @@ pub(crate) async fn get_or_init_environment( python_downloads: PythonDownloads, connectivity: Connectivity, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result { @@ -530,6 +533,7 @@ pub(crate) async fn get_or_init_environment( python_downloads, connectivity, native_tls, + allow_insecure_host, cache, printer, ) @@ -626,6 +630,7 @@ pub(crate) async fn resolve_names( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result, uv_requirements::Error> { @@ -650,7 +655,6 @@ pub(crate) async fn resolve_names( index_locations, index_strategy, keyring_provider, - allow_insecure_host, resolution: _, prerelease: _, dependency_metadata, @@ -680,7 +684,7 @@ pub(crate) async fn resolve_names( .index_urls(index_locations.index_urls()) .index_strategy(*index_strategy) .keyring(*keyring_provider) - .allow_insecure_host(allow_insecure_host.clone()) + .allow_insecure_host(allow_insecure_host.to_vec()) .markers(interpreter.markers()) .platform(interpreter.platform()) .build(); @@ -778,6 +782,7 @@ pub(crate) async fn resolve_environment<'a>( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result { @@ -787,7 +792,6 @@ pub(crate) async fn resolve_environment<'a>( index_locations, index_strategy, keyring_provider, - allow_insecure_host, resolution, prerelease, dependency_metadata, @@ -953,6 +957,7 @@ pub(crate) async fn sync_environment( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> anyhow::Result { @@ -960,7 +965,6 @@ pub(crate) async fn sync_environment( index_locations, index_strategy, keyring_provider, - allow_insecure_host, dependency_metadata, config_setting, no_build_isolation, @@ -1106,6 +1110,7 @@ pub(crate) async fn update_environment( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> anyhow::Result { @@ -1115,7 +1120,6 @@ pub(crate) async fn update_environment( index_locations, index_strategy, keyring_provider, - allow_insecure_host, resolution, prerelease, dependency_metadata, @@ -1186,7 +1190,7 @@ pub(crate) async fn update_environment( .index_urls(index_locations.index_urls()) .index_strategy(*index_strategy) .keyring(*keyring_provider) - .allow_insecure_host(allow_insecure_host.clone()) + .allow_insecure_host(allow_insecure_host.to_vec()) .markers(interpreter.markers()) .platform(interpreter.platform()) .build(); diff --git a/crates/uv/src/commands/project/remove.rs b/crates/uv/src/commands/project/remove.rs index 7cbf0cf15e16..b412d5767541 100644 --- a/crates/uv/src/commands/project/remove.rs +++ b/crates/uv/src/commands/project/remove.rs @@ -7,6 +7,7 @@ use uv_cache::Cache; use uv_client::Connectivity; use uv_configuration::{ Concurrency, DevGroupsManifest, EditableMode, ExtrasSpecification, InstallOptions, LowerBound, + TrustedHost, }; use uv_fs::Simplified; use uv_normalize::DEV_DEPENDENCIES; @@ -45,6 +46,7 @@ pub(crate) async fn remove( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result { @@ -190,6 +192,7 @@ pub(crate) async fn remove( python_downloads, connectivity, native_tls, + allow_insecure_host, cache, printer, ) @@ -218,6 +221,7 @@ pub(crate) async fn remove( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -262,6 +266,7 @@ pub(crate) async fn remove( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs index e308610463ed..85602bf19940 100644 --- a/crates/uv/src/commands/project/run.rs +++ b/crates/uv/src/commands/project/run.rs @@ -18,7 +18,7 @@ use uv_cli::ExternalCommand; use uv_client::{BaseClientBuilder, Connectivity}; use uv_configuration::{ Concurrency, DevGroupsSpecification, EditableMode, ExtrasSpecification, GroupsSpecification, - InstallOptions, LowerBound, SourceStrategy, + InstallOptions, LowerBound, SourceStrategy, TrustedHost, }; use uv_distribution::LoweredRequirement; use uv_fs::which::is_executable; @@ -79,6 +79,7 @@ pub(crate) async fn run( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, env_file: Vec, @@ -205,7 +206,8 @@ pub(crate) async fn run( let client_builder = BaseClientBuilder::new() .connectivity(connectivity) - .native_tls(native_tls); + .native_tls(native_tls) + .allow_insecure_host(allow_insecure_host.to_vec()); let interpreter = PythonInstallation::find_or_download( python_request.as_ref(), @@ -315,6 +317,7 @@ pub(crate) async fn run( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -523,7 +526,8 @@ pub(crate) async fn run( // base environment for the project. let client_builder = BaseClientBuilder::new() .connectivity(connectivity) - .native_tls(native_tls); + .native_tls(native_tls) + .allow_insecure_host(allow_insecure_host.to_vec()); // Resolve the Python request and requirement for the workspace. let WorkspacePython { @@ -578,6 +582,7 @@ pub(crate) async fn run( python_downloads, connectivity, native_tls, + allow_insecure_host, cache, printer, ) @@ -639,6 +644,7 @@ pub(crate) async fn run( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -707,6 +713,7 @@ pub(crate) async fn run( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -722,7 +729,8 @@ pub(crate) async fn run( let interpreter = { let client_builder = BaseClientBuilder::new() .connectivity(connectivity) - .native_tls(native_tls); + .native_tls(native_tls) + .allow_insecure_host(allow_insecure_host.to_vec()); // (1) Explicit request from user let python_request = if let Some(request) = python.as_deref() { @@ -784,7 +792,8 @@ pub(crate) async fn run( } else { let client_builder = BaseClientBuilder::new() .connectivity(connectivity) - .native_tls(native_tls); + .native_tls(native_tls) + .allow_insecure_host(allow_insecure_host.to_vec()); let spec = RequirementsSpecification::from_simple_sources(&requirements, &client_builder).await?; @@ -834,6 +843,7 @@ pub(crate) async fn run( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -1258,6 +1268,7 @@ impl RunCommand { script: bool, connectivity: Connectivity, native_tls: bool, + allow_insecure_host: &[TrustedHost], ) -> anyhow::Result { let (target, args) = command.split(); let Some(target) = target else { @@ -1288,6 +1299,7 @@ impl RunCommand { let client = BaseClientBuilder::new() .connectivity(connectivity) .native_tls(native_tls) + .allow_insecure_host(allow_insecure_host.to_vec()) .build(); let response = client.for_host(&url).get(url.clone()).send().await?; diff --git a/crates/uv/src/commands/project/sync.rs b/crates/uv/src/commands/project/sync.rs index 36f8935fd275..2fadc0db2f98 100644 --- a/crates/uv/src/commands/project/sync.rs +++ b/crates/uv/src/commands/project/sync.rs @@ -10,7 +10,7 @@ use uv_cache::Cache; use uv_client::{Connectivity, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ Concurrency, Constraints, DevGroupsManifest, DevGroupsSpecification, EditableMode, - ExtrasSpecification, HashCheckingMode, InstallOptions, LowerBound, + ExtrasSpecification, HashCheckingMode, InstallOptions, LowerBound, TrustedHost, }; use uv_dispatch::BuildDispatch; use uv_distribution_types::{DirectorySourceDist, Dist, Index, ResolvedDist, SourceDist}; @@ -58,6 +58,7 @@ pub(crate) async fn sync( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result { @@ -116,6 +117,7 @@ pub(crate) async fn sync( python_downloads, connectivity, native_tls, + allow_insecure_host, cache, printer, ) @@ -143,6 +145,7 @@ pub(crate) async fn sync( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -209,6 +212,7 @@ pub(crate) async fn sync( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -232,6 +236,7 @@ pub(super) async fn do_sync( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result<(), ProjectError> { @@ -251,7 +256,6 @@ pub(super) async fn do_sync( index_locations, index_strategy, keyring_provider, - allow_insecure_host, dependency_metadata, config_setting, no_build_isolation, diff --git a/crates/uv/src/commands/project/tree.rs b/crates/uv/src/commands/project/tree.rs index 67cdb35267e0..a10c8ca3e42a 100644 --- a/crates/uv/src/commands/project/tree.rs +++ b/crates/uv/src/commands/project/tree.rs @@ -7,7 +7,9 @@ use futures::{stream, StreamExt}; use uv_cache::{Cache, Refresh}; use uv_cache_info::Timestamp; use uv_client::{Connectivity, RegistryClientBuilder}; -use uv_configuration::{Concurrency, DevGroupsSpecification, LowerBound, TargetTriple}; +use uv_configuration::{ + Concurrency, DevGroupsSpecification, LowerBound, TargetTriple, TrustedHost, +}; use uv_distribution_types::IndexCapabilities; use uv_pep440::Version; use uv_pep508::PackageName; @@ -49,6 +51,7 @@ pub(crate) async fn tree( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result { @@ -76,6 +79,7 @@ pub(crate) async fn tree( python_downloads, connectivity, native_tls, + allow_insecure_host, cache, printer, ) @@ -107,6 +111,7 @@ pub(crate) async fn tree( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -128,7 +133,6 @@ pub(crate) async fn tree( index_locations: _, index_strategy: _, keyring_provider, - allow_insecure_host, resolution: _, prerelease: _, dependency_metadata: _, @@ -150,7 +154,7 @@ pub(crate) async fn tree( .native_tls(native_tls) .connectivity(connectivity) .keyring(*keyring_provider) - .allow_insecure_host(allow_insecure_host.clone()) + .allow_insecure_host(allow_insecure_host.to_vec()) .build(); // Initialize the client to fetch the latest version of each package. diff --git a/crates/uv/src/commands/publish.rs b/crates/uv/src/commands/publish.rs index c4c268cf884b..3d37a4fe5210 100644 --- a/crates/uv/src/commands/publish.rs +++ b/crates/uv/src/commands/publish.rs @@ -25,7 +25,7 @@ pub(crate) async fn publish( publish_url: Url, trusted_publishing: TrustedPublishing, keyring_provider: KeyringProviderType, - allow_insecure_host: Vec, + allow_insecure_host: &[TrustedHost], username: Option, password: Option, check_url: Option, @@ -58,7 +58,7 @@ pub(crate) async fn publish( .retries(0) .keyring(keyring_provider) .native_tls(native_tls) - .allow_insecure_host(allow_insecure_host.clone()) + .allow_insecure_host(allow_insecure_host.to_vec()) // Don't try cloning the request to make an unauthenticated request first. .auth_integration(AuthIntegration::OnlyAuthenticated) // Set a very high timeout for uploads, connections are often 10x slower on upload than @@ -82,7 +82,7 @@ pub(crate) async fn publish( .connectivity(connectivity) .index_urls(index_urls) .keyring(keyring_provider) - .allow_insecure_host(allow_insecure_host.clone()); + .allow_insecure_host(allow_insecure_host.to_vec()); Some(CheckUrlClient { index_url, registry_client_builder, diff --git a/crates/uv/src/commands/python/install.rs b/crates/uv/src/commands/python/install.rs index 014cf72f5473..14f6db53ad69 100644 --- a/crates/uv/src/commands/python/install.rs +++ b/crates/uv/src/commands/python/install.rs @@ -12,6 +12,7 @@ use tracing::{debug, trace}; use uv_client::Connectivity; use uv_configuration::PreviewMode; +use uv_configuration::TrustedHost; use uv_fs::Simplified; use uv_python::downloads::{DownloadResult, ManagedPythonDownload, PythonDownloadRequest}; use uv_python::managed::{ @@ -112,6 +113,7 @@ pub(crate) async fn install( python_downloads: PythonDownloads, native_tls: bool, connectivity: Connectivity, + allow_insecure_host: &[TrustedHost], no_config: bool, preview: PreviewMode, printer: Printer, @@ -210,6 +212,7 @@ pub(crate) async fn install( let client = uv_client::BaseClientBuilder::new() .connectivity(connectivity) .native_tls(native_tls) + .allow_insecure_host(allow_insecure_host.to_vec()) .build(); let reporter = PythonDownloadReporter::new(printer, downloads.len() as u64); let mut tasks = FuturesUnordered::new(); diff --git a/crates/uv/src/commands/tool/install.rs b/crates/uv/src/commands/tool/install.rs index adb6f884ef3e..c838366f13a6 100644 --- a/crates/uv/src/commands/tool/install.rs +++ b/crates/uv/src/commands/tool/install.rs @@ -7,7 +7,7 @@ use tracing::{debug, trace}; use uv_cache::{Cache, Refresh}; use uv_cache_info::Timestamp; use uv_client::{BaseClientBuilder, Connectivity}; -use uv_configuration::{Concurrency, Upgrade}; +use uv_configuration::{Concurrency, TrustedHost, Upgrade}; use uv_distribution_types::UnresolvedRequirementSpecification; use uv_normalize::PackageName; use uv_pep440::{VersionSpecifier, VersionSpecifiers}; @@ -49,12 +49,14 @@ pub(crate) async fn install( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: Cache, printer: Printer, ) -> Result { let client_builder = BaseClientBuilder::new() .connectivity(connectivity) - .native_tls(native_tls); + .native_tls(native_tls) + .allow_insecure_host(allow_insecure_host.to_vec()); let reporter = PythonDownloadReporter::single(printer); @@ -79,7 +81,8 @@ pub(crate) async fn install( let client_builder = BaseClientBuilder::new() .connectivity(connectivity) - .native_tls(native_tls); + .native_tls(native_tls) + .allow_insecure_host(allow_insecure_host.to_vec()); // Parse the input requirement. let target = Target::parse(&package, from.as_deref()); @@ -111,6 +114,7 @@ pub(crate) async fn install( connectivity, concurrency, native_tls, + allow_insecure_host, &cache, printer, ) @@ -180,6 +184,7 @@ pub(crate) async fn install( connectivity, concurrency, native_tls, + allow_insecure_host, &cache, printer, ) @@ -229,6 +234,7 @@ pub(crate) async fn install( connectivity, concurrency, native_tls, + allow_insecure_host, &cache, printer, ) @@ -352,6 +358,7 @@ pub(crate) async fn install( connectivity, concurrency, native_tls, + allow_insecure_host, &cache, printer, ) @@ -377,6 +384,7 @@ pub(crate) async fn install( connectivity, concurrency, native_tls, + allow_insecure_host, &cache, printer, ) @@ -400,6 +408,7 @@ pub(crate) async fn install( connectivity, concurrency, native_tls, + allow_insecure_host, &cache, printer, ) diff --git a/crates/uv/src/commands/tool/run.rs b/crates/uv/src/commands/tool/run.rs index 1f2145a95ee6..43816a5d748c 100644 --- a/crates/uv/src/commands/tool/run.rs +++ b/crates/uv/src/commands/tool/run.rs @@ -14,7 +14,7 @@ use uv_cache::{Cache, Refresh}; use uv_cache_info::Timestamp; use uv_cli::ExternalCommand; use uv_client::{BaseClientBuilder, Connectivity}; -use uv_configuration::Concurrency; +use uv_configuration::{Concurrency, TrustedHost}; use uv_distribution_types::{Name, UnresolvedRequirementSpecification}; use uv_installer::{SatisfiesResult, SitePackages}; use uv_normalize::PackageName; @@ -77,6 +77,7 @@ pub(crate) async fn run( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: Cache, printer: Printer, ) -> anyhow::Result { @@ -118,6 +119,7 @@ pub(crate) async fn run( connectivity, concurrency, native_tls, + allow_insecure_host, &cache, printer, ) @@ -402,12 +404,14 @@ async fn get_or_create_environment( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result<(Requirement, PythonEnvironment), ProjectError> { let client_builder = BaseClientBuilder::new() .connectivity(connectivity) - .native_tls(native_tls); + .native_tls(native_tls) + .allow_insecure_host(allow_insecure_host.to_vec()); let reporter = PythonDownloadReporter::single(printer); @@ -475,6 +479,7 @@ async fn get_or_create_environment( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -487,7 +492,8 @@ async fn get_or_create_environment( let spec = { let client_builder = BaseClientBuilder::new() .connectivity(connectivity) - .native_tls(native_tls); + .native_tls(native_tls) + .allow_insecure_host(allow_insecure_host.to_vec()); RequirementsSpecification::from_simple_sources(with, &client_builder).await? }; @@ -504,6 +510,7 @@ async fn get_or_create_environment( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -580,6 +587,7 @@ async fn get_or_create_environment( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) diff --git a/crates/uv/src/commands/tool/upgrade.rs b/crates/uv/src/commands/tool/upgrade.rs index e575a341ef72..962da4280418 100644 --- a/crates/uv/src/commands/tool/upgrade.rs +++ b/crates/uv/src/commands/tool/upgrade.rs @@ -7,7 +7,7 @@ use tracing::debug; use uv_cache::Cache; use uv_client::{BaseClientBuilder, Connectivity}; -use uv_configuration::Concurrency; +use uv_configuration::{Concurrency, TrustedHost}; use uv_normalize::PackageName; use uv_python::{ EnvironmentPreference, Interpreter, PythonDownloads, PythonInstallation, PythonPreference, @@ -40,6 +40,7 @@ pub(crate) async fn upgrade( python_downloads: PythonDownloads, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], cache: &Cache, printer: Printer, ) -> Result { @@ -68,7 +69,8 @@ pub(crate) async fn upgrade( let reporter = PythonDownloadReporter::single(printer); let client_builder = BaseClientBuilder::new() .connectivity(connectivity) - .native_tls(native_tls); + .native_tls(native_tls) + .allow_insecure_host(allow_insecure_host.to_vec()); let python_request = python.as_deref().map(PythonRequest::parse); @@ -110,6 +112,7 @@ pub(crate) async fn upgrade( connectivity, concurrency, native_tls, + allow_insecure_host, ) .await; @@ -197,6 +200,7 @@ async fn upgrade_tool( connectivity: Connectivity, concurrency: Concurrency, native_tls: bool, + allow_insecure_host: &[TrustedHost], ) -> Result { // Ensure the tool is installed. let existing_tool_receipt = match installed_tools.get_tool_receipt(name) { @@ -268,6 +272,7 @@ async fn upgrade_tool( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -284,6 +289,7 @@ async fn upgrade_tool( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) @@ -307,6 +313,7 @@ async fn upgrade_tool( connectivity, concurrency, native_tls, + allow_insecure_host, cache, printer, ) diff --git a/crates/uv/src/commands/venv.rs b/crates/uv/src/commands/venv.rs index b1b85ee503e2..60ab7a3535f7 100644 --- a/crates/uv/src/commands/venv.rs +++ b/crates/uv/src/commands/venv.rs @@ -50,7 +50,7 @@ pub(crate) async fn venv( index_strategy: IndexStrategy, dependency_metadata: DependencyMetadata, keyring_provider: KeyringProviderType, - allow_insecure_host: Vec, + allow_insecure_host: &[TrustedHost], prompt: uv_virtualenv::Prompt, system_site_packages: bool, connectivity: Connectivity, @@ -131,7 +131,7 @@ async fn venv_impl( index_strategy: IndexStrategy, dependency_metadata: DependencyMetadata, keyring_provider: KeyringProviderType, - allow_insecure_host: Vec, + allow_insecure_host: &[TrustedHost], prompt: uv_virtualenv::Prompt, system_site_packages: bool, connectivity: Connectivity, @@ -179,7 +179,8 @@ async fn venv_impl( let client_builder = BaseClientBuilder::default() .connectivity(connectivity) - .native_tls(native_tls); + .native_tls(native_tls) + .allow_insecure_host(allow_insecure_host.to_vec()); let reporter = PythonDownloadReporter::single(printer); @@ -291,7 +292,7 @@ async fn venv_impl( .index_urls(index_locations.index_urls()) .index_strategy(index_strategy) .keyring(keyring_provider) - .allow_insecure_host(allow_insecure_host) + .allow_insecure_host(allow_insecure_host.to_vec()) .markers(interpreter.markers()) .platform(interpreter.platform()) .build(); diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index 85903e2774ad..97062b0a0e79 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -150,6 +150,7 @@ async fn run(mut cli: Cli) -> Result { *script, settings.connectivity, settings.native_tls, + &settings.allow_insecure_host, ) .await?, ) @@ -353,7 +354,7 @@ async fn run(mut cli: Cli) -> Result { args.settings.index_strategy, args.settings.dependency_metadata, args.settings.keyring_provider, - args.settings.allow_insecure_host, + &globals.allow_insecure_host, args.settings.config_setting, globals.connectivity, args.settings.no_build_isolation, @@ -421,7 +422,6 @@ async fn run(mut cli: Cli) -> Result { args.settings.index_strategy, args.settings.dependency_metadata, args.settings.keyring_provider, - args.settings.allow_insecure_host, args.settings.allow_empty_requirements, globals.connectivity, &args.settings.config_setting, @@ -440,6 +440,7 @@ async fn run(mut cli: Cli) -> Result { args.settings.sources, globals.concurrency, globals.native_tls, + &globals.allow_insecure_host, cache, args.dry_run, printer, @@ -505,7 +506,6 @@ async fn run(mut cli: Cli) -> Result { args.settings.index_strategy, args.settings.dependency_metadata, args.settings.keyring_provider, - args.settings.allow_insecure_host, args.settings.reinstall, args.settings.link_mode, args.settings.compile_bytecode, @@ -528,6 +528,7 @@ async fn run(mut cli: Cli) -> Result { args.settings.prefix, globals.concurrency, globals.native_tls, + &globals.allow_insecure_host, cache, args.dry_run, printer, @@ -565,7 +566,7 @@ async fn run(mut cli: Cli) -> Result { globals.connectivity, globals.native_tls, args.settings.keyring_provider, - args.settings.allow_insecure_host, + &globals.allow_insecure_host, printer, ) .await @@ -610,7 +611,7 @@ async fn run(mut cli: Cli) -> Result { args.settings.index_locations, args.settings.index_strategy, args.settings.keyring_provider, - args.settings.allow_insecure_host, + globals.allow_insecure_host, globals.connectivity, args.settings.strict, args.settings.exclude_newer, @@ -738,6 +739,7 @@ async fn run(mut cli: Cli) -> Result { globals.connectivity, globals.concurrency, globals.native_tls, + &globals.allow_insecure_host, &cache, printer, ) @@ -781,7 +783,7 @@ async fn run(mut cli: Cli) -> Result { args.settings.index_strategy, args.settings.dependency_metadata, args.settings.keyring_provider, - args.settings.allow_insecure_host, + &globals.allow_insecure_host, uv_virtualenv::Prompt::from_args(prompt), args.system_site_packages, globals.connectivity, @@ -910,6 +912,7 @@ async fn run(mut cli: Cli) -> Result { globals.connectivity, globals.concurrency, globals.native_tls, + &globals.allow_insecure_host, cache, printer, ) @@ -959,6 +962,7 @@ async fn run(mut cli: Cli) -> Result { globals.connectivity, globals.concurrency, globals.native_tls, + &globals.allow_insecure_host, cache, printer, )) @@ -1002,6 +1006,7 @@ async fn run(mut cli: Cli) -> Result { globals.python_downloads, globals.concurrency, globals.native_tls, + &globals.allow_insecure_host, &cache, printer, )) @@ -1068,6 +1073,7 @@ async fn run(mut cli: Cli) -> Result { globals.python_downloads, globals.native_tls, globals.connectivity, + &globals.allow_insecure_host, cli.top_level.no_config, globals.preview, printer, @@ -1158,7 +1164,6 @@ async fn run(mut cli: Cli) -> Result { publish_url, trusted_publishing, keyring_provider, - allow_insecure_host, check_url, } = PublishSettings::resolve(args, filesystem); @@ -1167,7 +1172,7 @@ async fn run(mut cli: Cli) -> Result { publish_url, trusted_publishing, keyring_provider, - allow_insecure_host, + &globals.allow_insecure_host, username, password, check_url, @@ -1272,6 +1277,7 @@ async fn run_project( globals.python_downloads, globals.connectivity, globals.native_tls, + &globals.allow_insecure_host, &cache, printer, ) @@ -1329,6 +1335,7 @@ async fn run_project( globals.connectivity, globals.concurrency, globals.native_tls, + &globals.allow_insecure_host, &cache, printer, args.env_file, @@ -1366,6 +1373,7 @@ async fn run_project( globals.connectivity, globals.concurrency, globals.native_tls, + &globals.allow_insecure_host, &cache, printer, ) @@ -1394,6 +1402,7 @@ async fn run_project( globals.connectivity, globals.concurrency, globals.native_tls, + &globals.allow_insecure_host, &cache, printer, ) @@ -1445,6 +1454,7 @@ async fn run_project( globals.connectivity, globals.concurrency, globals.native_tls, + &globals.allow_insecure_host, &cache, printer, )) @@ -1485,6 +1495,7 @@ async fn run_project( globals.connectivity, globals.concurrency, globals.native_tls, + &globals.allow_insecure_host, &cache, printer, ) @@ -1519,6 +1530,7 @@ async fn run_project( globals.connectivity, globals.concurrency, globals.native_tls, + &globals.allow_insecure_host, &cache, printer, ) @@ -1553,6 +1565,7 @@ async fn run_project( globals.connectivity, globals.concurrency, globals.native_tls, + &globals.allow_insecure_host, globals.quiet, &cache, printer, diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index a45993981e1b..4a3ff5e3d737 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -55,6 +55,7 @@ pub(crate) struct GlobalSettings { pub(crate) native_tls: bool, pub(crate) concurrency: Concurrency, pub(crate) connectivity: Connectivity, + pub(crate) allow_insecure_host: Vec, pub(crate) show_settings: bool, pub(crate) preview: PreviewMode, pub(crate) python_preference: PythonPreference, @@ -65,12 +66,6 @@ pub(crate) struct GlobalSettings { impl GlobalSettings { /// Resolve the [`GlobalSettings`] from the CLI and filesystem configuration. pub(crate) fn resolve(args: &GlobalArgs, workspace: Option<&FilesystemOptions>) -> Self { - let preview = PreviewMode::from( - flag(args.preview, args.no_preview) - .combine(workspace.and_then(|workspace| workspace.globals.preview)) - .unwrap_or(false), - ); - Self { quiet: args.quiet, verbose: args.verbose, @@ -120,8 +115,29 @@ impl GlobalSettings { } else { Connectivity::Online }, + allow_insecure_host: args + .allow_insecure_host + .as_ref() + .map(|allow_insecure_host| { + allow_insecure_host + .iter() + .filter_map(|value| value.clone().into_option()) + }) + .into_iter() + .flatten() + .chain( + workspace + .and_then(|workspace| workspace.globals.allow_insecure_host.clone()) + .into_iter() + .flatten(), + ) + .collect(), show_settings: args.show_settings, - preview, + preview: PreviewMode::from( + flag(args.preview, args.no_preview) + .combine(workspace.and_then(|workspace| workspace.globals.preview)) + .unwrap_or(false), + ), python_preference: args .python_preference .combine(workspace.and_then(|workspace| workspace.globals.python_preference)) @@ -1590,7 +1606,6 @@ impl PipUninstallSettings { requirement, python, keyring_provider, - allow_insecure_host, system, no_system, break_system_packages, @@ -1611,12 +1626,6 @@ impl PipUninstallSettings { target, prefix, keyring_provider, - allow_insecure_host: allow_insecure_host.map(|allow_insecure_host| { - allow_insecure_host - .into_iter() - .filter_map(Maybe::into_option) - .collect() - }), ..PipOptions::default() }, filesystem, @@ -1919,7 +1928,6 @@ impl VenvSettings { index_args, index_strategy, keyring_provider, - allow_insecure_host, exclude_newer, no_project, link_mode, @@ -1940,12 +1948,6 @@ impl VenvSettings { system: flag(system, no_system), index_strategy, keyring_provider, - allow_insecure_host: allow_insecure_host.map(|allow_insecure_host| { - allow_insecure_host - .into_iter() - .filter_map(Maybe::into_option) - .collect() - }), exclude_newer, link_mode, ..PipOptions::from(index_args) @@ -1965,7 +1967,6 @@ pub(crate) struct InstallerSettingsRef<'a> { pub(crate) index_locations: &'a IndexLocations, pub(crate) index_strategy: IndexStrategy, pub(crate) keyring_provider: KeyringProviderType, - pub(crate) allow_insecure_host: &'a [TrustedHost], pub(crate) dependency_metadata: &'a DependencyMetadata, pub(crate) config_setting: &'a ConfigSettings, pub(crate) no_build_isolation: bool, @@ -1988,7 +1989,6 @@ pub(crate) struct ResolverSettings { pub(crate) index_locations: IndexLocations, pub(crate) index_strategy: IndexStrategy, pub(crate) keyring_provider: KeyringProviderType, - pub(crate) allow_insecure_host: Vec, pub(crate) resolution: ResolutionMode, pub(crate) prerelease: PrereleaseMode, pub(crate) dependency_metadata: DependencyMetadata, @@ -2007,7 +2007,6 @@ pub(crate) struct ResolverSettingsRef<'a> { pub(crate) index_locations: &'a IndexLocations, pub(crate) index_strategy: IndexStrategy, pub(crate) keyring_provider: KeyringProviderType, - pub(crate) allow_insecure_host: &'a [TrustedHost], pub(crate) resolution: ResolutionMode, pub(crate) prerelease: PrereleaseMode, pub(crate) dependency_metadata: &'a DependencyMetadata, @@ -2039,7 +2038,6 @@ impl ResolverSettings { index_locations: &self.index_locations, index_strategy: self.index_strategy, keyring_provider: self.keyring_provider, - allow_insecure_host: &self.allow_insecure_host, resolution: self.resolution, prerelease: self.prerelease, dependency_metadata: &self.dependency_metadata, @@ -2081,7 +2079,6 @@ impl From for ResolverSettings { ), index_strategy: value.index_strategy.unwrap_or_default(), keyring_provider: value.keyring_provider.unwrap_or_default(), - allow_insecure_host: value.allow_insecure_host.unwrap_or_default(), config_setting: value.config_settings.unwrap_or_default(), no_build_isolation: value.no_build_isolation.unwrap_or_default(), no_build_isolation_package: value.no_build_isolation_package.unwrap_or_default(), @@ -2110,7 +2107,6 @@ pub(crate) struct ResolverInstallerSettingsRef<'a> { pub(crate) index_locations: &'a IndexLocations, pub(crate) index_strategy: IndexStrategy, pub(crate) keyring_provider: KeyringProviderType, - pub(crate) allow_insecure_host: &'a [TrustedHost], pub(crate) resolution: ResolutionMode, pub(crate) prerelease: PrereleaseMode, pub(crate) dependency_metadata: &'a DependencyMetadata, @@ -2137,7 +2133,6 @@ pub(crate) struct ResolverInstallerSettings { pub(crate) index_locations: IndexLocations, pub(crate) index_strategy: IndexStrategy, pub(crate) keyring_provider: KeyringProviderType, - pub(crate) allow_insecure_host: Vec, pub(crate) resolution: ResolutionMode, pub(crate) prerelease: PrereleaseMode, pub(crate) dependency_metadata: DependencyMetadata, @@ -2174,7 +2169,6 @@ impl ResolverInstallerSettings { index_locations: &self.index_locations, index_strategy: self.index_strategy, keyring_provider: self.keyring_provider, - allow_insecure_host: &self.allow_insecure_host, resolution: self.resolution, prerelease: self.prerelease, dependency_metadata: &self.dependency_metadata, @@ -2218,7 +2212,6 @@ impl From for ResolverInstallerSettings { ), index_strategy: value.index_strategy.unwrap_or_default(), keyring_provider: value.keyring_provider.unwrap_or_default(), - allow_insecure_host: value.allow_insecure_host.unwrap_or_default(), config_setting: value.config_settings.unwrap_or_default(), no_build_isolation: value.no_build_isolation.unwrap_or_default(), no_build_isolation_package: value.no_build_isolation_package.unwrap_or_default(), @@ -2263,7 +2256,6 @@ pub(crate) struct PipSettings { pub(crate) prefix: Option, pub(crate) index_strategy: IndexStrategy, pub(crate) keyring_provider: KeyringProviderType, - pub(crate) allow_insecure_host: Vec, pub(crate) no_build_isolation: bool, pub(crate) no_build_isolation_package: Vec, pub(crate) build_options: BuildOptions, @@ -2320,7 +2312,6 @@ impl PipSettings { find_links, index_strategy, keyring_provider, - allow_insecure_host, no_build, no_binary, only_binary, @@ -2372,7 +2363,6 @@ impl PipSettings { find_links: top_level_find_links, index_strategy: top_level_index_strategy, keyring_provider: top_level_keyring_provider, - allow_insecure_host: top_level_allow_insecure_host, resolution: top_level_resolution, prerelease: top_level_prerelease, dependency_metadata: top_level_dependency_metadata, @@ -2404,7 +2394,6 @@ impl PipSettings { let find_links = find_links.combine(top_level_find_links); let index_strategy = index_strategy.combine(top_level_index_strategy); let keyring_provider = keyring_provider.combine(top_level_keyring_provider); - let allow_insecure_host = allow_insecure_host.combine(top_level_allow_insecure_host); let resolution = resolution.combine(top_level_resolution); let prerelease = prerelease.combine(top_level_prerelease); let dependency_metadata = dependency_metadata.combine(top_level_dependency_metadata); @@ -2480,10 +2469,6 @@ impl PipSettings { .keyring_provider .combine(keyring_provider) .unwrap_or_default(), - allow_insecure_host: args - .allow_insecure_host - .combine(allow_insecure_host) - .unwrap_or_default(), generate_hashes: args .generate_hashes .combine(generate_hashes) @@ -2597,7 +2582,6 @@ impl<'a> From> for ResolverSettingsRef<'a> { index_locations: settings.index_locations, index_strategy: settings.index_strategy, keyring_provider: settings.keyring_provider, - allow_insecure_host: settings.allow_insecure_host, resolution: settings.resolution, prerelease: settings.prerelease, dependency_metadata: settings.dependency_metadata, @@ -2619,7 +2603,6 @@ impl<'a> From> for InstallerSettingsRef<'a> { index_locations: settings.index_locations, index_strategy: settings.index_strategy, keyring_provider: settings.keyring_provider, - allow_insecure_host: settings.allow_insecure_host, dependency_metadata: settings.dependency_metadata, config_setting: settings.config_setting, no_build_isolation: settings.no_build_isolation, @@ -2647,7 +2630,6 @@ pub(crate) struct PublishSettings { pub(crate) publish_url: Url, pub(crate) trusted_publishing: TrustedPublishing, pub(crate) keyring_provider: KeyringProviderType, - pub(crate) allow_insecure_host: Vec, pub(crate) check_url: Option, } @@ -2665,9 +2647,7 @@ impl PublishSettings { trusted_publishing, } = publish; let ResolverInstallerOptions { - keyring_provider, - allow_insecure_host, - .. + keyring_provider, .. } = top_level; // Tokens are encoded in the same way as username/password @@ -2692,16 +2672,6 @@ impl PublishSettings { .keyring_provider .combine(keyring_provider) .unwrap_or_default(), - allow_insecure_host: args - .allow_insecure_host - .map(|allow_insecure_host| { - allow_insecure_host - .into_iter() - .filter_map(Maybe::into_option) - .collect() - }) - .combine(allow_insecure_host) - .unwrap_or_default(), check_url: args.check_url, } } diff --git a/crates/uv/tests/it/help.rs b/crates/uv/tests/it/help.rs index dfa88fbad3df..1ec6a7edf922 100644 --- a/crates/uv/tests/it/help.rs +++ b/crates/uv/tests/it/help.rs @@ -47,22 +47,33 @@ fn help() { Disable automatic downloads of Python. [env: "UV_PYTHON_DOWNLOADS=never"] Global options: - -q, --quiet Do not print any output - -v, --verbose... Use verbose output - --color Control colors in output [default: auto] [possible values: auto, - always, never] - --native-tls Whether to load TLS certificates from the platform's native - certificate store [env: UV_NATIVE_TLS=] - --offline Disable network access - --no-progress Hide all progress outputs [env: UV_NO_PROGRESS=] - --directory Change to the given directory prior to running the command - --project Run the command within the given project directory - --config-file The path to a `uv.toml` file to use for configuration [env: - UV_CONFIG_FILE=] - --no-config Avoid discovering configuration files (`pyproject.toml`, - `uv.toml`) [env: UV_NO_CONFIG=] - -h, --help Display the concise help for this command - -V, --version Display the uv version + -q, --quiet + Do not print any output + -v, --verbose... + Use verbose output + --color + Control colors in output [default: auto] [possible values: auto, always, never] + --native-tls + Whether to load TLS certificates from the platform's native certificate store [env: + UV_NATIVE_TLS=] + --offline + Disable network access + --allow-insecure-host + Allow insecure connections to a host [env: UV_INSECURE_HOST=] + --no-progress + Hide all progress outputs [env: UV_NO_PROGRESS=] + --directory + Change to the given directory prior to running the command + --project + Run the command within the given project directory + --config-file + The path to a `uv.toml` file to use for configuration [env: UV_CONFIG_FILE=] + --no-config + Avoid discovering configuration files (`pyproject.toml`, `uv.toml`) [env: UV_NO_CONFIG=] + -h, --help + Display the concise help for this command + -V, --version + Display the uv version Use `uv help ` for more information on a specific command. @@ -116,22 +127,33 @@ fn help_flag() { Disable automatic downloads of Python. [env: "UV_PYTHON_DOWNLOADS=never"] Global options: - -q, --quiet Do not print any output - -v, --verbose... Use verbose output - --color Control colors in output [default: auto] [possible values: auto, - always, never] - --native-tls Whether to load TLS certificates from the platform's native - certificate store [env: UV_NATIVE_TLS=] - --offline Disable network access - --no-progress Hide all progress outputs [env: UV_NO_PROGRESS=] - --directory Change to the given directory prior to running the command - --project Run the command within the given project directory - --config-file The path to a `uv.toml` file to use for configuration [env: - UV_CONFIG_FILE=] - --no-config Avoid discovering configuration files (`pyproject.toml`, - `uv.toml`) [env: UV_NO_CONFIG=] - -h, --help Display the concise help for this command - -V, --version Display the uv version + -q, --quiet + Do not print any output + -v, --verbose... + Use verbose output + --color + Control colors in output [default: auto] [possible values: auto, always, never] + --native-tls + Whether to load TLS certificates from the platform's native certificate store [env: + UV_NATIVE_TLS=] + --offline + Disable network access + --allow-insecure-host + Allow insecure connections to a host [env: UV_INSECURE_HOST=] + --no-progress + Hide all progress outputs [env: UV_NO_PROGRESS=] + --directory + Change to the given directory prior to running the command + --project + Run the command within the given project directory + --config-file + The path to a `uv.toml` file to use for configuration [env: UV_CONFIG_FILE=] + --no-config + Avoid discovering configuration files (`pyproject.toml`, `uv.toml`) [env: UV_NO_CONFIG=] + -h, --help + Display the concise help for this command + -V, --version + Display the uv version Use `uv help` for more details. @@ -184,22 +206,33 @@ fn help_short_flag() { Disable automatic downloads of Python. [env: "UV_PYTHON_DOWNLOADS=never"] Global options: - -q, --quiet Do not print any output - -v, --verbose... Use verbose output - --color Control colors in output [default: auto] [possible values: auto, - always, never] - --native-tls Whether to load TLS certificates from the platform's native - certificate store [env: UV_NATIVE_TLS=] - --offline Disable network access - --no-progress Hide all progress outputs [env: UV_NO_PROGRESS=] - --directory Change to the given directory prior to running the command - --project Run the command within the given project directory - --config-file The path to a `uv.toml` file to use for configuration [env: - UV_CONFIG_FILE=] - --no-config Avoid discovering configuration files (`pyproject.toml`, - `uv.toml`) [env: UV_NO_CONFIG=] - -h, --help Display the concise help for this command - -V, --version Display the uv version + -q, --quiet + Do not print any output + -v, --verbose... + Use verbose output + --color + Control colors in output [default: auto] [possible values: auto, always, never] + --native-tls + Whether to load TLS certificates from the platform's native certificate store [env: + UV_NATIVE_TLS=] + --offline + Disable network access + --allow-insecure-host + Allow insecure connections to a host [env: UV_INSECURE_HOST=] + --no-progress + Hide all progress outputs [env: UV_NO_PROGRESS=] + --directory + Change to the given directory prior to running the command + --project + Run the command within the given project directory + --config-file + The path to a `uv.toml` file to use for configuration [env: UV_CONFIG_FILE=] + --no-config + Avoid discovering configuration files (`pyproject.toml`, `uv.toml`) [env: UV_NO_CONFIG=] + -h, --help + Display the concise help for this command + -V, --version + Display the uv version Use `uv help` for more details. @@ -342,6 +375,20 @@ fn help_subcommand() { When disabled, uv will only use locally cached data and locally available files. + --allow-insecure-host + Allow insecure connections to a host. + + Can be provided multiple times. + + Expects to receive either a hostname (e.g., `localhost`), a host-port pair (e.g., + `localhost:8080`), or a URL (e.g., `https://localhost`). + + WARNING: Hosts included in this list will not be verified against the system's certificate + store. Only use `--allow-insecure-host` in a secure network with verified sources, as it + bypasses SSL verification and could expose you to MITM attacks. + + [env: UV_INSECURE_HOST=] + --no-progress Hide all progress outputs. @@ -525,6 +572,20 @@ fn help_subsubcommand() { When disabled, uv will only use locally cached data and locally available files. + --allow-insecure-host + Allow insecure connections to a host. + + Can be provided multiple times. + + Expects to receive either a hostname (e.g., `localhost`), a host-port pair (e.g., + `localhost:8080`), or a URL (e.g., `https://localhost`). + + WARNING: Hosts included in this list will not be verified against the system's certificate + store. Only use `--allow-insecure-host` in a secure network with verified sources, as it + bypasses SSL verification and could expose you to MITM attacks. + + [env: UV_INSECURE_HOST=] + --no-progress Hide all progress outputs. @@ -613,22 +674,33 @@ fn help_flag_subcommand() { Disable automatic downloads of Python. [env: "UV_PYTHON_DOWNLOADS=never"] Global options: - -q, --quiet Do not print any output - -v, --verbose... Use verbose output - --color Control colors in output [default: auto] [possible values: auto, - always, never] - --native-tls Whether to load TLS certificates from the platform's native - certificate store [env: UV_NATIVE_TLS=] - --offline Disable network access - --no-progress Hide all progress outputs [env: UV_NO_PROGRESS=] - --directory Change to the given directory prior to running the command - --project Run the command within the given project directory - --config-file The path to a `uv.toml` file to use for configuration [env: - UV_CONFIG_FILE=] - --no-config Avoid discovering configuration files (`pyproject.toml`, - `uv.toml`) [env: UV_NO_CONFIG=] - -h, --help Display the concise help for this command - -V, --version Display the uv version + -q, --quiet + Do not print any output + -v, --verbose... + Use verbose output + --color + Control colors in output [default: auto] [possible values: auto, always, never] + --native-tls + Whether to load TLS certificates from the platform's native certificate store [env: + UV_NATIVE_TLS=] + --offline + Disable network access + --allow-insecure-host + Allow insecure connections to a host [env: UV_INSECURE_HOST=] + --no-progress + Hide all progress outputs [env: UV_NO_PROGRESS=] + --directory + Change to the given directory prior to running the command + --project + Run the command within the given project directory + --config-file + The path to a `uv.toml` file to use for configuration [env: UV_CONFIG_FILE=] + --no-config + Avoid discovering configuration files (`pyproject.toml`, `uv.toml`) [env: UV_NO_CONFIG=] + -h, --help + Display the concise help for this command + -V, --version + Display the uv version Use `uv help python` for more details. @@ -668,22 +740,33 @@ fn help_flag_subsubcommand() { Disable automatic downloads of Python. [env: "UV_PYTHON_DOWNLOADS=never"] Global options: - -q, --quiet Do not print any output - -v, --verbose... Use verbose output - --color Control colors in output [default: auto] [possible values: auto, - always, never] - --native-tls Whether to load TLS certificates from the platform's native - certificate store [env: UV_NATIVE_TLS=] - --offline Disable network access - --no-progress Hide all progress outputs [env: UV_NO_PROGRESS=] - --directory Change to the given directory prior to running the command - --project Run the command within the given project directory - --config-file The path to a `uv.toml` file to use for configuration [env: - UV_CONFIG_FILE=] - --no-config Avoid discovering configuration files (`pyproject.toml`, - `uv.toml`) [env: UV_NO_CONFIG=] - -h, --help Display the concise help for this command - -V, --version Display the uv version + -q, --quiet + Do not print any output + -v, --verbose... + Use verbose output + --color + Control colors in output [default: auto] [possible values: auto, always, never] + --native-tls + Whether to load TLS certificates from the platform's native certificate store [env: + UV_NATIVE_TLS=] + --offline + Disable network access + --allow-insecure-host + Allow insecure connections to a host [env: UV_INSECURE_HOST=] + --no-progress + Hide all progress outputs [env: UV_NO_PROGRESS=] + --directory + Change to the given directory prior to running the command + --project + Run the command within the given project directory + --config-file + The path to a `uv.toml` file to use for configuration [env: UV_CONFIG_FILE=] + --no-config + Avoid discovering configuration files (`pyproject.toml`, `uv.toml`) [env: UV_NO_CONFIG=] + -h, --help + Display the concise help for this command + -V, --version + Display the uv version ----- stderr ----- "###); @@ -814,22 +897,33 @@ fn help_with_global_option() { Disable automatic downloads of Python. [env: "UV_PYTHON_DOWNLOADS=never"] Global options: - -q, --quiet Do not print any output - -v, --verbose... Use verbose output - --color Control colors in output [default: auto] [possible values: auto, - always, never] - --native-tls Whether to load TLS certificates from the platform's native - certificate store [env: UV_NATIVE_TLS=] - --offline Disable network access - --no-progress Hide all progress outputs [env: UV_NO_PROGRESS=] - --directory Change to the given directory prior to running the command - --project Run the command within the given project directory - --config-file The path to a `uv.toml` file to use for configuration [env: - UV_CONFIG_FILE=] - --no-config Avoid discovering configuration files (`pyproject.toml`, - `uv.toml`) [env: UV_NO_CONFIG=] - -h, --help Display the concise help for this command - -V, --version Display the uv version + -q, --quiet + Do not print any output + -v, --verbose... + Use verbose output + --color + Control colors in output [default: auto] [possible values: auto, always, never] + --native-tls + Whether to load TLS certificates from the platform's native certificate store [env: + UV_NATIVE_TLS=] + --offline + Disable network access + --allow-insecure-host + Allow insecure connections to a host [env: UV_INSECURE_HOST=] + --no-progress + Hide all progress outputs [env: UV_NO_PROGRESS=] + --directory + Change to the given directory prior to running the command + --project + Run the command within the given project directory + --config-file + The path to a `uv.toml` file to use for configuration [env: UV_CONFIG_FILE=] + --no-config + Avoid discovering configuration files (`pyproject.toml`, `uv.toml`) [env: UV_NO_CONFIG=] + -h, --help + Display the concise help for this command + -V, --version + Display the uv version Use `uv help ` for more information on a specific command. @@ -919,22 +1013,33 @@ fn help_with_no_pager() { Disable automatic downloads of Python. [env: "UV_PYTHON_DOWNLOADS=never"] Global options: - -q, --quiet Do not print any output - -v, --verbose... Use verbose output - --color Control colors in output [default: auto] [possible values: auto, - always, never] - --native-tls Whether to load TLS certificates from the platform's native - certificate store [env: UV_NATIVE_TLS=] - --offline Disable network access - --no-progress Hide all progress outputs [env: UV_NO_PROGRESS=] - --directory Change to the given directory prior to running the command - --project Run the command within the given project directory - --config-file The path to a `uv.toml` file to use for configuration [env: - UV_CONFIG_FILE=] - --no-config Avoid discovering configuration files (`pyproject.toml`, - `uv.toml`) [env: UV_NO_CONFIG=] - -h, --help Display the concise help for this command - -V, --version Display the uv version + -q, --quiet + Do not print any output + -v, --verbose... + Use verbose output + --color + Control colors in output [default: auto] [possible values: auto, always, never] + --native-tls + Whether to load TLS certificates from the platform's native certificate store [env: + UV_NATIVE_TLS=] + --offline + Disable network access + --allow-insecure-host + Allow insecure connections to a host [env: UV_INSECURE_HOST=] + --no-progress + Hide all progress outputs [env: UV_NO_PROGRESS=] + --directory + Change to the given directory prior to running the command + --project + Run the command within the given project directory + --config-file + The path to a `uv.toml` file to use for configuration [env: UV_CONFIG_FILE=] + --no-config + Avoid discovering configuration files (`pyproject.toml`, `uv.toml`) [env: UV_NO_CONFIG=] + -h, --help + Display the concise help for this command + -V, --version + Display the uv version Use `uv help ` for more information on a specific command. diff --git a/crates/uv/tests/it/show_settings.rs b/crates/uv/tests/it/show_settings.rs index 4275db0ac491..a435be824d4e 100644 --- a/crates/uv/tests/it/show_settings.rs +++ b/crates/uv/tests/it/show_settings.rs @@ -54,6 +54,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -129,7 +130,6 @@ fn resolve_uv_toml() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -201,6 +201,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -276,7 +277,6 @@ fn resolve_uv_toml() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -349,6 +349,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -424,7 +425,6 @@ fn resolve_uv_toml() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -529,6 +529,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -604,7 +605,6 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -678,6 +678,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -724,7 +725,6 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -807,6 +807,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -882,7 +883,6 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -979,6 +979,7 @@ fn resolve_index_url() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -1082,7 +1083,6 @@ fn resolve_index_url() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -1156,6 +1156,7 @@ fn resolve_index_url() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -1289,7 +1290,6 @@ fn resolve_index_url() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -1386,6 +1386,7 @@ fn resolve_find_links() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -1461,7 +1462,6 @@ fn resolve_find_links() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -1557,6 +1557,7 @@ fn resolve_top_level() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -1603,7 +1604,6 @@ fn resolve_top_level() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -1692,6 +1692,7 @@ fn resolve_top_level() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -1795,7 +1796,6 @@ fn resolve_top_level() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -1867,6 +1867,7 @@ fn resolve_top_level() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -1970,7 +1971,6 @@ fn resolve_top_level() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -2066,6 +2066,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -2112,7 +2113,6 @@ fn resolve_user_configuration() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -2191,6 +2191,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -2237,7 +2238,6 @@ fn resolve_user_configuration() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -2316,6 +2316,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -2362,7 +2363,6 @@ fn resolve_user_configuration() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -2443,6 +2443,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -2489,7 +2490,6 @@ fn resolve_user_configuration() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -2589,6 +2589,7 @@ fn resolve_tool() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -2624,7 +2625,6 @@ fn resolve_tool() -> anyhow::Result<()> { find_links: None, index_strategy: None, keyring_provider: None, - allow_insecure_host: None, resolution: Some( LowestDirect, ), @@ -2660,7 +2660,6 @@ fn resolve_tool() -> anyhow::Result<()> { }, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], resolution: LowestDirect, prerelease: IfNecessaryOrExplicit, dependency_metadata: DependencyMetadata( @@ -2747,6 +2746,7 @@ fn resolve_poetry_toml() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -2793,7 +2793,6 @@ fn resolve_poetry_toml() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -2900,6 +2899,7 @@ fn resolve_both() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -2975,7 +2975,6 @@ fn resolve_both() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -3074,6 +3073,7 @@ fn resolve_config_file() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -3149,7 +3149,6 @@ fn resolve_config_file() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -3322,6 +3321,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -3368,7 +3368,6 @@ fn resolve_skip_empty() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -3450,6 +3449,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -3496,7 +3496,6 @@ fn resolve_skip_empty() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -3586,6 +3585,18 @@ fn allow_insecure_host() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [ + Host { + scheme: None, + host: "google.com", + port: None, + }, + Host { + scheme: None, + host: "example.com", + port: None, + }, + ], show_settings: true, preview: Disabled, python_preference: Managed, @@ -3632,18 +3643,6 @@ fn allow_insecure_host() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [ - Host { - scheme: None, - host: "google.com", - port: None, - }, - Host { - scheme: None, - host: "example.com", - port: None, - }, - ], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -3736,6 +3735,7 @@ fn index_priority() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -3841,7 +3841,6 @@ fn index_priority() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -3913,6 +3912,7 @@ fn index_priority() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -4018,7 +4018,6 @@ fn index_priority() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -4096,6 +4095,7 @@ fn index_priority() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -4201,7 +4201,6 @@ fn index_priority() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -4274,6 +4273,7 @@ fn index_priority() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -4379,7 +4379,6 @@ fn index_priority() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -4459,6 +4458,7 @@ fn index_priority() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -4564,7 +4564,6 @@ fn index_priority() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -4637,6 +4636,7 @@ fn index_priority() -> anyhow::Result<()> { installs: 8, }, connectivity: Online, + allow_insecure_host: [], show_settings: true, preview: Disabled, python_preference: Managed, @@ -4742,7 +4742,6 @@ fn index_priority() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, - allow_insecure_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 483df86120f7..254c5b064fe9 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -485,7 +485,16 @@ uv init [OPTIONS] [PATH]

Options

-
--app

Create a project for an application.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--app

Create a project for an application.

This is the default behavior if --lib is not requested.

@@ -3698,7 +3707,16 @@ uv tool list [OPTIONS]

Options

-
--cache-dir cache-dir

Path to the cache directory.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -3798,6 +3816,15 @@ uv tool uninstall [OPTIONS] ...
--all

Uninstall all tools

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -3910,7 +3937,16 @@ uv tool update-shell [OPTIONS]

Options

-
--cache-dir cache-dir

Path to the cache directory.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -4024,7 +4060,16 @@ uv tool dir [OPTIONS]

Options

-
--bin

Show the directory into which uv tool will install executables.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--bin

Show the directory into which uv tool will install executables.

By default, uv tool dir shows the directory into which the tool Python environments themselves are installed, rather than the directory containing the linked executables.

@@ -4226,6 +4271,15 @@ uv python list [OPTIONS]

By default, only the latest patch version is shown for each minor version.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -4356,7 +4410,16 @@ uv python install [OPTIONS] [TARGETS]...

Options

-
--cache-dir cache-dir

Path to the cache directory.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -4484,7 +4547,16 @@ uv python find [OPTIONS] [REQUEST]

Options

-
--cache-dir cache-dir

Path to the cache directory.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -4615,7 +4687,16 @@ uv python pin [OPTIONS] [REQUEST]

Options

-
--cache-dir cache-dir

Path to the cache directory.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -4737,7 +4818,16 @@ uv python dir [OPTIONS]

Options

-
--bin

Show the directory into which uv python will install Python executables.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--bin

Show the directory into which uv python will install Python executables.

Note that this directory is only used when installing Python with preview mode enabled.

@@ -4869,6 +4959,15 @@ uv python uninstall [OPTIONS] ...
--all

Uninstall all managed Python versions

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -6369,7 +6468,16 @@ uv pip freeze [OPTIONS]

Options

-
--cache-dir cache-dir

Path to the cache directory.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -6493,7 +6601,16 @@ uv pip list [OPTIONS]

Options

-
--cache-dir cache-dir

Path to the cache directory.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -6643,7 +6760,16 @@ uv pip show [OPTIONS] [PACKAGE]...

Options

-
--cache-dir cache-dir

Path to the cache directory.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -6767,7 +6893,16 @@ uv pip tree [OPTIONS]

Options

-
--cache-dir cache-dir

Path to the cache directory.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -6904,7 +7039,16 @@ uv pip check [OPTIONS]

Options

-
--cache-dir cache-dir

Path to the cache directory.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -7821,7 +7965,16 @@ uv cache clean [OPTIONS] [PACKAGE]...

Options

-
--cache-dir cache-dir

Path to the cache directory.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -7927,7 +8080,16 @@ uv cache prune [OPTIONS]

Options

-
--cache-dir cache-dir

Path to the cache directory.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -8047,7 +8209,16 @@ uv cache dir [OPTIONS]

Options

-
--cache-dir cache-dir

Path to the cache directory.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -8175,7 +8346,16 @@ uv self update [OPTIONS] [TARGET_VERSION]

Options

-
--cache-dir cache-dir

Path to the cache directory.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -8284,7 +8464,16 @@ uv version [OPTIONS]

Options

-
--cache-dir cache-dir

Path to the cache directory.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

@@ -8396,7 +8585,16 @@ uv generate-shell-completion [OPTIONS]

Options

-
--directory directory

Change to the given directory prior to running the command.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--directory directory

Change to the given directory prior to running the command.

Relative paths are resolved with the given directory as the base.

@@ -8430,7 +8628,16 @@ uv help [OPTIONS] [COMMAND]...

Options

-
--cache-dir cache-dir

Path to the cache directory.

+
--allow-insecure-host allow-insecure-host

Allow insecure connections to a host.

+ +

Can be provided multiple times.

+ +

Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).

+ +

WARNING: Hosts included in this list will not be verified against the system’s certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.

+ +

May also be set with the UV_INSECURE_HOST environment variable.

+
--cache-dir cache-dir

Path to the cache directory.

Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.

diff --git a/docs/reference/settings.md b/docs/reference/settings.md index 43672ae1ebb5..d8801f5598da 100644 --- a/docs/reference/settings.md +++ b/docs/reference/settings.md @@ -1505,39 +1505,6 @@ packages. --- -#### [`allow-insecure-host`](#pip_allow-insecure-host) {: #pip_allow-insecure-host } - - -Allow insecure connections to host. - -Expects to receive either a hostname (e.g., `localhost`), a host-port pair (e.g., -`localhost:8080`), or a URL (e.g., `https://localhost`). - -WARNING: Hosts included in this list will not be verified against the system's certificate -store. Only use `--allow-insecure-host` in a secure network with verified sources, as it -bypasses SSL verification and could expose you to MITM attacks. - -**Default value**: `[]` - -**Type**: `list[str]` - -**Example usage**: - -=== "pyproject.toml" - - ```toml - [tool.uv.pip] - allow-insecure-host = ["localhost:8080"] - ``` -=== "uv.toml" - - ```toml - [pip] - allow-insecure-host = ["localhost:8080"] - ``` - ---- - #### [`annotation-style`](#pip_annotation-style) {: #pip_annotation-style } diff --git a/uv.schema.json b/uv.schema.json index 15ed7a0e03af..ecf4e8b620ce 100644 --- a/uv.schema.json +++ b/uv.schema.json @@ -745,16 +745,6 @@ "null" ] }, - "allow-insecure-host": { - "description": "Allow insecure connections to host.\n\nExpects to receive either a hostname (e.g., `localhost`), a host-port pair (e.g., `localhost:8080`), or a URL (e.g., `https://localhost`).\n\nWARNING: Hosts included in this list will not be verified against the system's certificate store. Only use `--allow-insecure-host` in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/TrustedHost" - } - }, "annotation-style": { "description": "The style of the annotation comments included in the output file, used to indicate the source of each package.", "anyOf": [ From 535bd8fc24189b2f29ebdf47f1c14e12f70ad04d Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Mon, 4 Nov 2024 13:48:13 -0600 Subject: [PATCH 10/19] Discover and respect `.python-version` files in parent directories (#6370) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Uses #6369 for test coverage. Updates version file discovery to search up into parent directories. Also refactors Python request determination to avoid duplicating the user request / version file / workspace lookup logic in every command (this supersedes the work started in https://github.com/astral-sh/uv/pull/6372). There is a bit of remaining work here, mostly around documentation. There are some edge-cases where we don't use the refactored request utility, like `uv build` — I'm not sure how I'm going to handle that yet as it needs a separate root directory. --- crates/uv-python/src/lib.rs | 1 + crates/uv-python/src/version_files.rs | 107 ++++++++--- crates/uv-scripts/src/lib.rs | 17 +- crates/uv-workspace/src/workspace.rs | 4 + crates/uv/src/commands/build_frontend.rs | 12 +- crates/uv/src/commands/project/add.rs | 56 +++--- crates/uv/src/commands/project/export.rs | 3 + crates/uv/src/commands/project/init.rs | 49 ++++- crates/uv/src/commands/project/lock.rs | 3 + crates/uv/src/commands/project/mod.rs | 216 ++++++++++++++++++++--- crates/uv/src/commands/project/remove.rs | 2 + crates/uv/src/commands/project/run.rs | 94 ++++------ crates/uv/src/commands/project/sync.rs | 2 + crates/uv/src/commands/project/tree.rs | 3 + crates/uv/src/commands/python/find.rs | 90 +++++----- crates/uv/src/commands/python/install.rs | 32 ++-- crates/uv/src/commands/python/pin.rs | 5 +- crates/uv/src/commands/venv.rs | 61 +++---- crates/uv/src/lib.rs | 7 + crates/uv/tests/it/init.rs | 37 ++++ crates/uv/tests/it/python_find.rs | 106 ++++++++++- crates/uv/tests/it/sync.rs | 124 +++++++++++++ crates/uv/tests/it/venv.rs | 14 ++ 23 files changed, 800 insertions(+), 245 deletions(-) diff --git a/crates/uv-python/src/lib.rs b/crates/uv-python/src/lib.rs index c12ce93ad487..3c293a17381b 100644 --- a/crates/uv-python/src/lib.rs +++ b/crates/uv-python/src/lib.rs @@ -17,6 +17,7 @@ pub use crate::prefix::Prefix; pub use crate::python_version::PythonVersion; pub use crate::target::Target; pub use crate::version_files::{ + DiscoveryOptions as VersionFileDiscoveryOptions, FilePreference as VersionFilePreference, PythonVersionFile, PYTHON_VERSIONS_FILENAME, PYTHON_VERSION_FILENAME, }; pub use crate::virtualenv::{Error as VirtualEnvError, PyVenvConfiguration, VirtualEnvironment}; diff --git a/crates/uv-python/src/version_files.rs b/crates/uv-python/src/version_files.rs index b697f0cce886..4e243b07e8c2 100644 --- a/crates/uv-python/src/version_files.rs +++ b/crates/uv-python/src/version_files.rs @@ -4,6 +4,7 @@ use std::path::{Path, PathBuf}; use fs_err as fs; use itertools::Itertools; use tracing::debug; +use uv_fs::Simplified; use crate::PythonRequest; @@ -22,38 +23,91 @@ pub struct PythonVersionFile { versions: Vec, } +/// Whether to prefer the `.python-version` or `.python-versions` file. +#[derive(Debug, Clone, Copy, Default)] +pub enum FilePreference { + #[default] + Version, + Versions, +} + +#[derive(Debug, Default, Clone)] +pub struct DiscoveryOptions<'a> { + /// The path to stop discovery at. + stop_discovery_at: Option<&'a Path>, + /// When `no_config` is set, Python version files will be ignored. + /// + /// Discovery will still run in order to display a log about the ignored file. + no_config: bool, + preference: FilePreference, +} + +impl<'a> DiscoveryOptions<'a> { + #[must_use] + pub fn with_no_config(self, no_config: bool) -> Self { + Self { no_config, ..self } + } + + #[must_use] + pub fn with_preference(self, preference: FilePreference) -> Self { + Self { preference, ..self } + } + + #[must_use] + pub fn with_stop_discovery_at(self, stop_discovery_at: Option<&'a Path>) -> Self { + Self { + stop_discovery_at, + ..self + } + } +} + impl PythonVersionFile { - /// Find a Python version file in the given directory. + /// Find a Python version file in the given directory or any of its parents. pub async fn discover( working_directory: impl AsRef, - // TODO(zanieb): Create a `DiscoverySettings` struct for these options - no_config: bool, - prefer_versions: bool, + options: &DiscoveryOptions<'_>, ) -> Result, std::io::Error> { - let versions_path = working_directory.as_ref().join(PYTHON_VERSIONS_FILENAME); - let version_path = working_directory.as_ref().join(PYTHON_VERSION_FILENAME); - - if no_config { - if version_path.exists() { - debug!("Ignoring `.python-version` file due to `--no-config`"); - } else if versions_path.exists() { - debug!("Ignoring `.python-versions` file due to `--no-config`"); - }; + let Some(path) = Self::find_nearest(working_directory, options) else { + return Ok(None); + }; + + if options.no_config { + debug!( + "Ignoring Python version file at `{}` due to `--no-config`", + path.user_display() + ); return Ok(None); } - let paths = if prefer_versions { - [versions_path, version_path] - } else { - [version_path, versions_path] + // Uses `try_from_path` instead of `from_path` to avoid TOCTOU failures. + Self::try_from_path(path).await + } + + fn find_nearest(path: impl AsRef, options: &DiscoveryOptions<'_>) -> Option { + path.as_ref() + .ancestors() + .take_while(|path| { + // Only walk up the given directory, if any. + options + .stop_discovery_at + .and_then(Path::parent) + .map(|stop_discovery_at| stop_discovery_at != *path) + .unwrap_or(true) + }) + .find_map(|path| Self::find_in_directory(path, options)) + } + + fn find_in_directory(path: &Path, options: &DiscoveryOptions<'_>) -> Option { + let version_path = path.join(PYTHON_VERSION_FILENAME); + let versions_path = path.join(PYTHON_VERSIONS_FILENAME); + + let paths = match options.preference { + FilePreference::Versions => [versions_path, version_path], + FilePreference::Version => [version_path, versions_path], }; - for path in paths { - if let Some(result) = Self::try_from_path(path).await? { - return Ok(Some(result)); - }; - } - Ok(None) + paths.into_iter().find(|path| path.is_file()) } /// Try to read a Python version file at the given path. @@ -62,7 +116,10 @@ impl PythonVersionFile { pub async fn try_from_path(path: PathBuf) -> Result, std::io::Error> { match fs::tokio::read_to_string(&path).await { Ok(content) => { - debug!("Reading requests from `{}`", path.display()); + debug!( + "Reading Python requests from version file at `{}`", + path.display() + ); let versions = content .lines() .filter(|line| { @@ -104,7 +161,7 @@ impl PythonVersionFile { } } - /// Return the first version declared in the file, if any. + /// Return the first request declared in the file, if any. pub fn version(&self) -> Option<&PythonRequest> { self.versions.first() } diff --git a/crates/uv-scripts/src/lib.rs b/crates/uv-scripts/src/lib.rs index 689227475b39..48843920648a 100644 --- a/crates/uv-scripts/src/lib.rs +++ b/crates/uv-scripts/src/lib.rs @@ -44,10 +44,19 @@ impl Pep723Item { Self::Remote(metadata) => metadata, } } + + /// Return the path of the PEP 723 item, if any. + pub fn path(&self) -> Option<&Path> { + match self { + Self::Script(script) => Some(&script.path), + Self::Stdin(_) => None, + Self::Remote(_) => None, + } + } } /// A PEP 723 script, including its [`Pep723Metadata`]. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Pep723Script { /// The path to the Python script. pub path: PathBuf, @@ -188,7 +197,7 @@ impl Pep723Script { /// PEP 723 metadata as parsed from a `script` comment block. /// /// See: -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] #[serde(rename_all = "kebab-case")] pub struct Pep723Metadata { pub dependencies: Option>>, @@ -248,13 +257,13 @@ impl FromStr for Pep723Metadata { } } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "kebab-case")] pub struct Tool { pub uv: Option, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] #[serde(deny_unknown_fields)] pub struct ToolUv { #[serde(flatten)] diff --git a/crates/uv-workspace/src/workspace.rs b/crates/uv-workspace/src/workspace.rs index 59368fffb92c..7b2fba0f09ec 100644 --- a/crates/uv-workspace/src/workspace.rs +++ b/crates/uv-workspace/src/workspace.rs @@ -925,6 +925,7 @@ impl ProjectWorkspace { // Only walk up the given directory, if any. options .stop_discovery_at + .and_then(Path::parent) .map(|stop_discovery_at| stop_discovery_at != *path) .unwrap_or(true) }) @@ -1127,6 +1128,7 @@ async fn find_workspace( // Only walk up the given directory, if any. options .stop_discovery_at + .and_then(Path::parent) .map(|stop_discovery_at| stop_discovery_at != *path) .unwrap_or(true) }) @@ -1219,6 +1221,7 @@ pub fn check_nested_workspaces(inner_workspace_root: &Path, options: &DiscoveryO // Only walk up the given directory, if any. options .stop_discovery_at + .and_then(Path::parent) .map(|stop_discovery_at| stop_discovery_at != *path) .unwrap_or(true) }) @@ -1385,6 +1388,7 @@ impl VirtualProject { // Only walk up the given directory, if any. options .stop_discovery_at + .and_then(Path::parent) .map(|stop_discovery_at| stop_discovery_at != *path) .unwrap_or(true) }) diff --git a/crates/uv/src/commands/build_frontend.rs b/crates/uv/src/commands/build_frontend.rs index 602c59493518..76ee538aaadf 100644 --- a/crates/uv/src/commands/build_frontend.rs +++ b/crates/uv/src/commands/build_frontend.rs @@ -23,7 +23,8 @@ use uv_fs::Simplified; use uv_normalize::PackageName; use uv_python::{ EnvironmentPreference, PythonDownloads, PythonEnvironment, PythonInstallation, - PythonPreference, PythonRequest, PythonVariant, PythonVersionFile, VersionRequest, + PythonPreference, PythonRequest, PythonVariant, PythonVersionFile, VersionFileDiscoveryOptions, + VersionRequest, }; use uv_requirements::RequirementsSource; use uv_resolver::{ExcludeNewer, FlatIndex, RequiresPython}; @@ -391,9 +392,12 @@ async fn build_package( // (2) Request from `.python-version` if interpreter_request.is_none() { - interpreter_request = PythonVersionFile::discover(source.directory(), no_config, false) - .await? - .and_then(PythonVersionFile::into_version); + interpreter_request = PythonVersionFile::discover( + source.directory(), + &VersionFileDiscoveryOptions::default().with_no_config(no_config), + ) + .await? + .and_then(PythonVersionFile::into_version); } // (3) `Requires-Python` in `pyproject.toml` diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs index 943d82b30bf1..ceaa7798c098 100644 --- a/crates/uv/src/commands/project/add.rs +++ b/crates/uv/src/commands/project/add.rs @@ -27,11 +27,11 @@ use uv_pep508::{ExtraName, Requirement, UnnamedRequirement, VersionOrUrl}; use uv_pypi_types::{redact_credentials, ParsedUrl, RequirementSource, VerbatimParsedUrl}; use uv_python::{ EnvironmentPreference, Interpreter, PythonDownloads, PythonEnvironment, PythonInstallation, - PythonPreference, PythonRequest, PythonVariant, PythonVersionFile, VersionRequest, + PythonPreference, PythonRequest, }; use uv_requirements::{NamedRequirementsResolver, RequirementsSource, RequirementsSpecification}; use uv_resolver::{FlatIndex, InstallTarget}; -use uv_scripts::Pep723Script; +use uv_scripts::{Pep723Item, Pep723Script}; use uv_types::{BuildIsolation, HashStrategy}; use uv_warnings::warn_user_once; use uv_workspace::pyproject::{DependencyType, Source, SourceError}; @@ -44,7 +44,9 @@ use crate::commands::pip::loggers::{ use crate::commands::pip::operations::Modifications; use crate::commands::pip::resolution_environment; use crate::commands::project::lock::LockMode; -use crate::commands::project::{script_python_requirement, ProjectError}; +use crate::commands::project::{ + init_script_python_requirement, validate_script_requires_python, ProjectError, ScriptPython, +}; use crate::commands::reporters::{PythonDownloadReporter, ResolverReporter}; use crate::commands::{diagnostics, pip, project, ExitStatus, SharedState}; use crate::printer::Printer; @@ -76,6 +78,7 @@ pub(crate) async fn add( concurrency: Concurrency, native_tls: bool, allow_insecure_host: &[TrustedHost], + no_config: bool, cache: &Cache, printer: Printer, ) -> Result { @@ -134,12 +137,13 @@ pub(crate) async fn add( let script = if let Some(script) = Pep723Script::read(&script).await? { script } else { - let requires_python = script_python_requirement( + let requires_python = init_script_python_requirement( python.as_deref(), project_dir, false, python_preference, python_downloads, + no_config, &client_builder, cache, &reporter, @@ -148,28 +152,17 @@ pub(crate) async fn add( Pep723Script::init(&script, requires_python.specifiers()).await? }; - let python_request = if let Some(request) = python.as_deref() { - // (1) Explicit request from user - Some(PythonRequest::parse(request)) - } else if let Some(request) = PythonVersionFile::discover(project_dir, false, false) - .await? - .and_then(PythonVersionFile::into_version) - { - // (2) Request from `.python-version` - Some(request) - } else { - // (3) `Requires-Python` in `pyproject.toml` - script - .metadata - .requires_python - .clone() - .map(|requires_python| { - PythonRequest::Version(VersionRequest::Range( - requires_python, - PythonVariant::Default, - )) - }) - }; + let ScriptPython { + source, + python_request, + requires_python, + } = ScriptPython::from_request( + python.as_deref().map(PythonRequest::parse), + None, + &Pep723Item::Script(script.clone()), + no_config, + ) + .await?; let interpreter = PythonInstallation::find_or_download( python_request.as_ref(), @@ -183,6 +176,16 @@ pub(crate) async fn add( .await? .into_interpreter(); + if let Some((requires_python, requires_python_source)) = requires_python { + validate_script_requires_python( + &interpreter, + None, + &requires_python, + &requires_python_source, + &source, + )?; + } + Target::Script(script, Box::new(interpreter)) } else { // Find the project in the workspace. @@ -221,6 +224,7 @@ pub(crate) async fn add( connectivity, native_tls, allow_insecure_host, + no_config, cache, printer, ) diff --git a/crates/uv/src/commands/project/export.rs b/crates/uv/src/commands/project/export.rs index c03fff53b332..b5aec86affdd 100644 --- a/crates/uv/src/commands/project/export.rs +++ b/crates/uv/src/commands/project/export.rs @@ -49,6 +49,7 @@ pub(crate) async fn export( concurrency: Concurrency, native_tls: bool, allow_insecure_host: &[TrustedHost], + no_config: bool, quiet: bool, cache: &Cache, printer: Printer, @@ -99,12 +100,14 @@ pub(crate) async fn export( // Find an interpreter for the project interpreter = ProjectInterpreter::discover( project.workspace(), + project_dir, python.as_deref().map(PythonRequest::parse), python_preference, python_downloads, connectivity, native_tls, allow_insecure_host, + no_config, cache, printer, ) diff --git a/crates/uv/src/commands/project/init.rs b/crates/uv/src/commands/project/init.rs index 3b3d1d1b9cf7..d6eaf5880e4d 100644 --- a/crates/uv/src/commands/project/init.rs +++ b/crates/uv/src/commands/project/init.rs @@ -18,7 +18,7 @@ use uv_pep440::Version; use uv_pep508::PackageName; use uv_python::{ EnvironmentPreference, PythonDownloads, PythonInstallation, PythonPreference, PythonRequest, - PythonVariant, PythonVersionFile, VersionRequest, + PythonVariant, PythonVersionFile, VersionFileDiscoveryOptions, VersionRequest, }; use uv_resolver::RequiresPython; use uv_scripts::{Pep723Script, ScriptTag}; @@ -26,7 +26,7 @@ use uv_warnings::warn_user_once; use uv_workspace::pyproject_mut::{DependencyTarget, PyProjectTomlMut}; use uv_workspace::{DiscoveryOptions, MemberDiscovery, Workspace, WorkspaceError}; -use crate::commands::project::{find_requires_python, script_python_requirement}; +use crate::commands::project::{find_requires_python, init_script_python_requirement}; use crate::commands::reporters::PythonDownloadReporter; use crate::commands::ExitStatus; use crate::printer::Printer; @@ -51,6 +51,7 @@ pub(crate) async fn init( connectivity: Connectivity, native_tls: bool, allow_insecure_host: &[TrustedHost], + no_config: bool, cache: &Cache, printer: Printer, ) -> Result { @@ -75,6 +76,7 @@ pub(crate) async fn init( package, native_tls, allow_insecure_host, + no_config, ) .await?; @@ -131,6 +133,7 @@ pub(crate) async fn init( connectivity, native_tls, allow_insecure_host, + no_config, cache, printer, ) @@ -183,6 +186,7 @@ async fn init_script( package: bool, native_tls: bool, allow_insecure_host: &[TrustedHost], + no_config: bool, ) -> Result<()> { if no_workspace { warn_user_once!("`--no-workspace` is a no-op for Python scripts, which are standalone"); @@ -226,12 +230,13 @@ async fn init_script( } }; - let requires_python = script_python_requirement( + let requires_python = init_script_python_requirement( python.as_deref(), &CWD, no_pin_python, python_preference, python_downloads, + no_config, &client_builder, cache, &reporter, @@ -266,6 +271,7 @@ async fn init_project( connectivity: Connectivity, native_tls: bool, allow_insecure_host: &[TrustedHost], + no_config: bool, cache: &Cache, printer: Printer, ) -> Result<()> { @@ -318,10 +324,35 @@ async fn init_project( .native_tls(native_tls) .allow_insecure_host(allow_insecure_host.to_vec()); - // Add a `requires-python` field to the `pyproject.toml` and return the corresponding interpreter. - let (requires_python, python_request) = if let Some(request) = python.as_deref() { + // First, determine if there is an request for Python + let python_request = if let Some(request) = python { // (1) Explicit request from user - match PythonRequest::parse(request) { + Some(PythonRequest::parse(&request)) + } else if let Some(file) = PythonVersionFile::discover( + path, + &VersionFileDiscoveryOptions::default() + .with_stop_discovery_at( + workspace + .as_ref() + .map(Workspace::install_path) + .map(PathBuf::as_ref), + ) + .with_no_config(no_config), + ) + .await? + { + // (2) Request from `.python-version` + file.into_version() + } else { + None + }; + + // Add a `requires-python` field to the `pyproject.toml` and return the corresponding interpreter. + let (requires_python, python_request) = if let Some(python_request) = python_request { + // (1) A request from the user or `.python-version` file + // This can be arbitrary, i.e., not a version — in which case we may need to resolve the + // interpreter + match python_request { PythonRequest::Version(VersionRequest::MajorMinor( major, minor, @@ -427,7 +458,7 @@ async fn init_project( } } } else if let Some(requires_python) = workspace.as_ref().and_then(find_requires_python) { - // (2) `Requires-Python` from the workspace + // (2) `requires-python` from the workspace let python_request = PythonRequest::Version(VersionRequest::Range( requires_python.specifiers().clone(), PythonVariant::Default, @@ -687,7 +718,7 @@ impl InitProjectKind { // Write .python-version if it doesn't exist. if let Some(python_request) = python_request { - if PythonVersionFile::discover(path, false, false) + if PythonVersionFile::discover(path, &VersionFileDiscoveryOptions::default()) .await? .is_none() { @@ -741,7 +772,7 @@ impl InitProjectKind { // Write .python-version if it doesn't exist. if let Some(python_request) = python_request { - if PythonVersionFile::discover(path, false, false) + if PythonVersionFile::discover(path, &VersionFileDiscoveryOptions::default()) .await? .is_none() { diff --git a/crates/uv/src/commands/project/lock.rs b/crates/uv/src/commands/project/lock.rs index c66d36cbb1df..f7ad9b9900a7 100644 --- a/crates/uv/src/commands/project/lock.rs +++ b/crates/uv/src/commands/project/lock.rs @@ -84,6 +84,7 @@ pub(crate) async fn lock( concurrency: Concurrency, native_tls: bool, allow_insecure_host: &[TrustedHost], + no_config: bool, cache: &Cache, printer: Printer, ) -> anyhow::Result { @@ -98,12 +99,14 @@ pub(crate) async fn lock( // Find an interpreter for the project interpreter = ProjectInterpreter::discover( &workspace, + project_dir, python.as_deref().map(PythonRequest::parse), python_preference, python_downloads, connectivity, native_tls, allow_insecure_host, + no_config, cache, printer, ) diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs index 99c5b2d5d9e7..75b0260410b2 100644 --- a/crates/uv/src/commands/project/mod.rs +++ b/crates/uv/src/commands/project/mod.rs @@ -16,7 +16,7 @@ use uv_distribution::DistributionDatabase; use uv_distribution_types::{ Index, Resolution, UnresolvedRequirement, UnresolvedRequirementSpecification, }; -use uv_fs::Simplified; +use uv_fs::{Simplified, CWD}; use uv_git::ResolvedRepositoryReference; use uv_installer::{SatisfiesResult, SitePackages}; use uv_normalize::{GroupName, PackageName, DEV_DEPENDENCIES}; @@ -26,7 +26,7 @@ use uv_pypi_types::Requirement; use uv_python::{ EnvironmentPreference, Interpreter, InvalidEnvironmentKind, PythonDownloads, PythonEnvironment, PythonInstallation, PythonPreference, PythonRequest, PythonVariant, PythonVersionFile, - VersionRequest, + VersionFileDiscoveryOptions, VersionRequest, }; use uv_requirements::upgrade::{read_lock_requirements, LockedRequirements}; use uv_requirements::{NamedRequirementsResolver, RequirementsSpecification}; @@ -34,6 +34,7 @@ use uv_resolver::{ FlatIndex, Lock, OptionsBuilder, PythonRequirement, RequiresPython, ResolutionGraph, ResolverEnvironment, }; +use uv_scripts::Pep723Item; use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy}; use uv_warnings::{warn_user, warn_user_once}; use uv_workspace::dependency_groups::DependencyGroupError; @@ -89,13 +90,13 @@ pub(crate) enum ProjectError { RequiresPythonProjectIncompatibility(Version, RequiresPython), #[error("The requested interpreter resolved to Python {0}, which is incompatible with the script's Python requirement: `{1}`")] - RequestedPythonScriptIncompatibility(Version, VersionSpecifiers), + RequestedPythonScriptIncompatibility(Version, RequiresPython), #[error("The Python request from `{0}` resolved to Python {1}, which is incompatible with the script's Python requirement: `{2}`")] - DotPythonVersionScriptIncompatibility(String, Version, VersionSpecifiers), + DotPythonVersionScriptIncompatibility(String, Version, RequiresPython), #[error("The resolved Python interpreter (Python {0}) is incompatible with the script's Python requirement: `{1}`")] - RequiresPythonScriptIncompatibility(Version, VersionSpecifiers), + RequiresPythonScriptIncompatibility(Version, RequiresPython), #[error("The requested interpreter resolved to Python {0}, which is incompatible with the project's Python requirement: `{1}`. However, a workspace member (`{member}`) supports Python {3}. To install the workspace member on its own, navigate to `{path}`, then run `{venv}` followed by `{install}`.", member = _2.cyan(), venv = format!("uv venv --python {_0}").green(), install = "uv pip install -e .".green(), path = _4.user_display().cyan() )] RequestedMemberIncompatibility( @@ -222,7 +223,7 @@ pub(crate) fn find_requires_python(workspace: &Workspace) -> Option, requires_python: &RequiresPython, source: &PythonRequestSource, ) -> Result<(), ProjectError> { @@ -235,7 +236,7 @@ pub(crate) fn validate_requires_python( // a library in the workspace is compatible with Python >=3.8, the user may attempt // to sync on Python 3.8. This will fail, but we should provide a more helpful error // message. - for (name, member) in workspace.packages() { + for (name, member) in workspace.into_iter().flat_map(Workspace::packages) { let Some(project) = member.pyproject_toml().project.as_ref() else { continue; }; @@ -255,7 +256,7 @@ pub(crate) fn validate_requires_python( } PythonRequestSource::DotPythonVersion(file) => { Err(ProjectError::DotPythonVersionMemberIncompatibility( - file.to_string(), + file.path().user_display().to_string(), interpreter.python_version().clone(), requires_python.clone(), name.clone(), @@ -285,7 +286,7 @@ pub(crate) fn validate_requires_python( } PythonRequestSource::DotPythonVersion(file) => { Err(ProjectError::DotPythonVersionProjectIncompatibility( - file.to_string(), + file.path().user_display().to_string(), interpreter.python_version().clone(), requires_python.clone(), )) @@ -299,6 +300,49 @@ pub(crate) fn validate_requires_python( } } +/// Returns an error if the [`Interpreter`] does not satisfy script or workspace `requires-python`. +#[allow(clippy::result_large_err)] +pub(crate) fn validate_script_requires_python( + interpreter: &Interpreter, + workspace: Option<&Workspace>, + requires_python: &RequiresPython, + requires_python_source: &RequiresPythonSource, + request_source: &PythonRequestSource, +) -> Result<(), ProjectError> { + match requires_python_source { + RequiresPythonSource::Project => { + validate_requires_python(interpreter, workspace, requires_python, request_source)?; + } + RequiresPythonSource::Script => {} + }; + + if requires_python.contains(interpreter.python_version()) { + return Ok(()); + } + + match request_source { + PythonRequestSource::UserRequest => { + Err(ProjectError::RequestedPythonScriptIncompatibility( + interpreter.python_version().clone(), + requires_python.clone(), + )) + } + PythonRequestSource::DotPythonVersion(file) => { + Err(ProjectError::DotPythonVersionScriptIncompatibility( + file.file_name().to_string(), + interpreter.python_version().clone(), + requires_python.clone(), + )) + } + PythonRequestSource::RequiresPython => { + Err(ProjectError::RequiresPythonScriptIncompatibility( + interpreter.python_version().clone(), + requires_python.clone(), + )) + } + } +} + /// An interpreter suitable for the project. #[derive(Debug)] #[allow(clippy::large_enum_variant)] @@ -314,47 +358,68 @@ pub(crate) enum PythonRequestSource { /// The request was provided by the user. UserRequest, /// The request was inferred from a `.python-version` or `.python-versions` file. - DotPythonVersion(String), + DotPythonVersion(PythonVersionFile), /// The request was inferred from a `pyproject.toml` file. RequiresPython, } +impl std::fmt::Display for PythonRequestSource { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + PythonRequestSource::UserRequest => write!(f, "explicit request"), + PythonRequestSource::DotPythonVersion(file) => { + write!(f, "version file at `{}`", file.path().user_display()) + } + PythonRequestSource::RequiresPython => write!(f, "`requires-python` metadata"), + } + } +} + /// The resolved Python request and requirement for a [`Workspace`]. #[derive(Debug, Clone)] pub(crate) struct WorkspacePython { /// The source of the Python request. - source: PythonRequestSource, + pub(crate) source: PythonRequestSource, /// The resolved Python request, computed by considering (1) any explicit request from the user /// via `--python`, (2) any implicit request from the user via `.python-version`, and (3) any /// `Requires-Python` specifier in the `pyproject.toml`. - python_request: Option, + pub(crate) python_request: Option, /// The resolved Python requirement for the project, computed by taking the intersection of all /// `Requires-Python` specifiers in the workspace. - requires_python: Option, + pub(crate) requires_python: Option, } impl WorkspacePython { /// Determine the [`WorkspacePython`] for the current [`Workspace`]. pub(crate) async fn from_request( python_request: Option, - workspace: &Workspace, + workspace: Option<&Workspace>, + project_dir: &Path, + no_config: bool, ) -> Result { - let requires_python = find_requires_python(workspace); + let requires_python = workspace.and_then(find_requires_python); + + let workspace_root = workspace.map(Workspace::install_path); let (source, python_request) = if let Some(request) = python_request { // (1) Explicit request from user let source = PythonRequestSource::UserRequest; let request = Some(request); (source, request) - } else if let Some(file) = - PythonVersionFile::discover(workspace.install_path(), false, false).await? + } else if let Some(file) = PythonVersionFile::discover( + project_dir, + &VersionFileDiscoveryOptions::default() + .with_stop_discovery_at(workspace_root.map(PathBuf::as_ref)) + .with_no_config(no_config), + ) + .await? { // (2) Request from `.python-version` - let source = PythonRequestSource::DotPythonVersion(file.file_name().to_string()); + let source = PythonRequestSource::DotPythonVersion(file.clone()); let request = file.into_version(); (source, request) } else { - // (3) `Requires-Python` in `pyproject.toml` + // (3) `requires-python` in `pyproject.toml` let request = requires_python .as_ref() .map(RequiresPython::specifiers) @@ -368,6 +433,87 @@ impl WorkspacePython { (source, request) }; + if let Some(python_request) = python_request.as_ref() { + debug!( + "Using Python request `{}` from {source}", + python_request.to_canonical_string() + ); + }; + + Ok(Self { + source, + python_request, + requires_python, + }) + } +} + +/// The source of a `Requires-Python` specifier. +#[derive(Debug, Clone)] +pub(crate) enum RequiresPythonSource { + /// From the PEP 723 inline script metadata. + Script, + /// From a `pyproject.toml` in a workspace. + Project, +} + +/// The resolved Python request and requirement for a [`Pep723Script`] +#[derive(Debug, Clone)] +pub(crate) struct ScriptPython { + /// The source of the Python request. + pub(crate) source: PythonRequestSource, + /// The resolved Python request, computed by considering (1) any explicit request from the user + /// via `--python`, (2) any implicit request from the user via `.python-version`, (3) any + /// `Requires-Python` specifier in the script metadata, and (4) any `Requires-Python` specifier + /// in the `pyproject.toml`. + pub(crate) python_request: Option, + /// The resolved Python requirement for the script and its source. + pub(crate) requires_python: Option<(RequiresPython, RequiresPythonSource)>, +} + +impl ScriptPython { + /// Determine the [`ScriptPython`] for the current [`Workspace`]. + pub(crate) async fn from_request( + python_request: Option, + workspace: Option<&Workspace>, + script: &Pep723Item, + no_config: bool, + ) -> Result { + // First, discover a requirement from the workspace + let WorkspacePython { + mut source, + mut python_request, + requires_python, + } = WorkspacePython::from_request( + python_request, + workspace, + script.path().and_then(Path::parent).unwrap_or(&**CWD), + no_config, + ) + .await?; + + // If the script has a `requires-python` specifier, prefer that over one from the workspace. + let requires_python = + if let Some(requires_python_specifiers) = script.metadata().requires_python.as_ref() { + if python_request.is_none() { + python_request = Some(PythonRequest::Version(VersionRequest::Range( + requires_python_specifiers.clone(), + PythonVariant::Default, + ))); + source = PythonRequestSource::RequiresPython; + } + Some(( + RequiresPython::from_specifiers(requires_python_specifiers), + RequiresPythonSource::Script, + )) + } else { + requires_python.map(|requirement| (requirement, RequiresPythonSource::Project)) + }; + + if let Some(python_request) = python_request.as_ref() { + debug!("Using Python request {python_request} from {source}"); + }; + Ok(Self { source, python_request, @@ -380,12 +526,14 @@ impl ProjectInterpreter { /// Discover the interpreter to use in the current [`Workspace`]. pub(crate) async fn discover( workspace: &Workspace, + project_dir: &Path, python_request: Option, python_preference: PythonPreference, python_downloads: PythonDownloads, connectivity: Connectivity, native_tls: bool, allow_insecure_host: &[TrustedHost], + no_config: bool, cache: &Cache, printer: Printer, ) -> Result { @@ -394,7 +542,8 @@ impl ProjectInterpreter { source, python_request, requires_python, - } = WorkspacePython::from_request(python_request, workspace).await?; + } = WorkspacePython::from_request(python_request, Some(workspace), project_dir, no_config) + .await?; // Read from the virtual environment first. let venv = workspace.venv(); @@ -402,11 +551,15 @@ impl ProjectInterpreter { Ok(venv) => { if python_request.as_ref().map_or(true, |request| { if request.satisfied(venv.interpreter(), cache) { - debug!("The virtual environment's Python version satisfies `{request}`"); + debug!( + "The virtual environment's Python version satisfies `{}`", + request.to_canonical_string() + ); true } else { debug!( - "The virtual environment's Python version does not satisfy `{request}`" + "The virtual environment's Python version does not satisfy `{}`", + request.to_canonical_string() ); false } @@ -499,7 +652,7 @@ impl ProjectInterpreter { } if let Some(requires_python) = requires_python.as_ref() { - validate_requires_python(&interpreter, workspace, requires_python, &source)?; + validate_requires_python(&interpreter, Some(workspace), requires_python, &source)?; } Ok(Self::Interpreter(interpreter)) @@ -523,17 +676,20 @@ pub(crate) async fn get_or_init_environment( connectivity: Connectivity, native_tls: bool, allow_insecure_host: &[TrustedHost], + no_config: bool, cache: &Cache, printer: Printer, ) -> Result { match ProjectInterpreter::discover( workspace, + workspace.install_path().as_ref(), python, python_preference, python_downloads, connectivity, native_tls, allow_insecure_host, + no_config, cache, printer, ) @@ -1326,13 +1482,14 @@ pub(crate) async fn update_environment( }) } -/// Determine the [`RequiresPython`] requirement for a PEP 723 script. -pub(crate) async fn script_python_requirement( +/// Determine the [`RequiresPython`] requirement for a new PEP 723 script. +pub(crate) async fn init_script_python_requirement( python: Option<&str>, directory: &Path, no_pin_python: bool, python_preference: PythonPreference, python_downloads: PythonDownloads, + no_config: bool, client_builder: &BaseClientBuilder<'_>, cache: &Cache, reporter: &PythonDownloadReporter, @@ -1342,9 +1499,12 @@ pub(crate) async fn script_python_requirement( PythonRequest::parse(request) } else if let (false, Some(request)) = ( no_pin_python, - PythonVersionFile::discover(directory, false, false) - .await? - .and_then(PythonVersionFile::into_version), + PythonVersionFile::discover( + directory, + &VersionFileDiscoveryOptions::default().with_no_config(no_config), + ) + .await? + .and_then(PythonVersionFile::into_version), ) { // (2) Request from `.python-version` request diff --git a/crates/uv/src/commands/project/remove.rs b/crates/uv/src/commands/project/remove.rs index b412d5767541..736827447ee5 100644 --- a/crates/uv/src/commands/project/remove.rs +++ b/crates/uv/src/commands/project/remove.rs @@ -47,6 +47,7 @@ pub(crate) async fn remove( concurrency: Concurrency, native_tls: bool, allow_insecure_host: &[TrustedHost], + no_config: bool, cache: &Cache, printer: Printer, ) -> Result { @@ -193,6 +194,7 @@ pub(crate) async fn remove( connectivity, native_tls, allow_insecure_host, + no_config, cache, printer, ) diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs index 85602bf19940..e656635a6adb 100644 --- a/crates/uv/src/commands/project/run.rs +++ b/crates/uv/src/commands/project/run.rs @@ -28,7 +28,7 @@ use uv_normalize::PackageName; use uv_python::{ EnvironmentPreference, Interpreter, PythonDownloads, PythonEnvironment, PythonInstallation, - PythonPreference, PythonRequest, PythonVariant, PythonVersionFile, VersionRequest, + PythonPreference, PythonRequest, PythonVersionFile, VersionFileDiscoveryOptions, }; use uv_requirements::{RequirementsSource, RequirementsSpecification}; use uv_resolver::{InstallTarget, Lock}; @@ -45,8 +45,8 @@ use crate::commands::pip::operations::Modifications; use crate::commands::project::environment::CachedEnvironment; use crate::commands::project::lock::LockMode; use crate::commands::project::{ - default_dependency_groups, validate_requires_python, DependencyGroupsTarget, - EnvironmentSpecification, ProjectError, PythonRequestSource, WorkspacePython, + default_dependency_groups, validate_requires_python, validate_script_requires_python, + DependencyGroupsTarget, EnvironmentSpecification, ProjectError, ScriptPython, WorkspacePython, }; use crate::commands::reporters::PythonDownloadReporter; use crate::commands::{diagnostics, project, ExitStatus, SharedState}; @@ -178,31 +178,17 @@ pub(crate) async fn run( } } - let (source, python_request) = if let Some(request) = python.as_deref() { - // (1) Explicit request from user - let source = PythonRequestSource::UserRequest; - let request = Some(PythonRequest::parse(request)); - (source, request) - } else if let Some(file) = PythonVersionFile::discover(&project_dir, false, false).await? { - // (2) Request from `.python-version` - let source = PythonRequestSource::DotPythonVersion(file.file_name().to_string()); - let request = file.into_version(); - (source, request) - } else { - // (3) `Requires-Python` in the script - let request = script - .metadata() - .requires_python - .as_ref() - .map(|requires_python| { - PythonRequest::Version(VersionRequest::Range( - requires_python.clone(), - PythonVariant::Default, - )) - }); - let source = PythonRequestSource::RequiresPython; - (source, request) - }; + let ScriptPython { + source, + python_request, + requires_python, + } = ScriptPython::from_request( + python.as_deref().map(PythonRequest::parse), + None, + &script, + no_config, + ) + .await?; let client_builder = BaseClientBuilder::new() .connectivity(connectivity) @@ -221,30 +207,18 @@ pub(crate) async fn run( .await? .into_interpreter(); - if let Some(requires_python) = script.metadata().requires_python.as_ref() { - if !requires_python.contains(interpreter.python_version()) { - let err = match source { - PythonRequestSource::UserRequest => { - ProjectError::RequestedPythonScriptIncompatibility( - interpreter.python_version().clone(), - requires_python.clone(), - ) - } - PythonRequestSource::DotPythonVersion(file) => { - ProjectError::DotPythonVersionScriptIncompatibility( - file, - interpreter.python_version().clone(), - requires_python.clone(), - ) - } - PythonRequestSource::RequiresPython => { - ProjectError::RequiresPythonScriptIncompatibility( - interpreter.python_version().clone(), - requires_python.clone(), - ) - } - }; - warn_user!("{err}"); + if let Some((requires_python, requires_python_source)) = requires_python { + match validate_script_requires_python( + &interpreter, + None, + &requires_python, + &requires_python_source, + &source, + ) { + Ok(()) => {} + Err(err) => { + warn_user!("{err}"); + } } } @@ -536,7 +510,9 @@ pub(crate) async fn run( requires_python, } = WorkspacePython::from_request( python.as_deref().map(PythonRequest::parse), - project.workspace(), + Some(project.workspace()), + project_dir, + no_config, ) .await?; @@ -555,7 +531,7 @@ pub(crate) async fn run( if let Some(requires_python) = requires_python.as_ref() { validate_requires_python( &interpreter, - project.workspace(), + Some(project.workspace()), requires_python, &source, )?; @@ -583,6 +559,7 @@ pub(crate) async fn run( connectivity, native_tls, allow_insecure_host, + no_config, cache, printer, ) @@ -737,9 +714,12 @@ pub(crate) async fn run( Some(PythonRequest::parse(request)) // (2) Request from `.python-version` } else { - PythonVersionFile::discover(&project_dir, no_config, false) - .await? - .and_then(PythonVersionFile::into_version) + PythonVersionFile::discover( + &project_dir, + &VersionFileDiscoveryOptions::default().with_no_config(no_config), + ) + .await? + .and_then(PythonVersionFile::into_version) }; let python = PythonInstallation::find_or_download( diff --git a/crates/uv/src/commands/project/sync.rs b/crates/uv/src/commands/project/sync.rs index 2fadc0db2f98..eac0f4761ad3 100644 --- a/crates/uv/src/commands/project/sync.rs +++ b/crates/uv/src/commands/project/sync.rs @@ -59,6 +59,7 @@ pub(crate) async fn sync( concurrency: Concurrency, native_tls: bool, allow_insecure_host: &[TrustedHost], + no_config: bool, cache: &Cache, printer: Printer, ) -> Result { @@ -118,6 +119,7 @@ pub(crate) async fn sync( connectivity, native_tls, allow_insecure_host, + no_config, cache, printer, ) diff --git a/crates/uv/src/commands/project/tree.rs b/crates/uv/src/commands/project/tree.rs index a10c8ca3e42a..b1da0fc457fe 100644 --- a/crates/uv/src/commands/project/tree.rs +++ b/crates/uv/src/commands/project/tree.rs @@ -52,6 +52,7 @@ pub(crate) async fn tree( concurrency: Concurrency, native_tls: bool, allow_insecure_host: &[TrustedHost], + no_config: bool, cache: &Cache, printer: Printer, ) -> Result { @@ -74,12 +75,14 @@ pub(crate) async fn tree( Some( ProjectInterpreter::discover( &workspace, + project_dir, python.as_deref().map(PythonRequest::parse), python_preference, python_downloads, connectivity, native_tls, allow_insecure_host, + no_config, cache, printer, ) diff --git a/crates/uv/src/commands/python/find.rs b/crates/uv/src/commands/python/find.rs index 06d43298ee1f..6844c0fc7247 100644 --- a/crates/uv/src/commands/python/find.rs +++ b/crates/uv/src/commands/python/find.rs @@ -4,15 +4,14 @@ use std::path::Path; use uv_cache::Cache; use uv_fs::Simplified; -use uv_python::{ - EnvironmentPreference, PythonInstallation, PythonPreference, PythonRequest, PythonVariant, - PythonVersionFile, VersionRequest, -}; -use uv_resolver::RequiresPython; -use uv_warnings::warn_user_once; +use uv_python::{EnvironmentPreference, PythonInstallation, PythonPreference, PythonRequest}; +use uv_warnings::{warn_user, warn_user_once}; use uv_workspace::{DiscoveryOptions, VirtualProject, WorkspaceError}; -use crate::commands::{project::find_requires_python, ExitStatus}; +use crate::commands::{ + project::{validate_requires_python, WorkspacePython}, + ExitStatus, +}; /// Find a Python interpreter. pub(crate) async fn find( @@ -30,50 +29,55 @@ pub(crate) async fn find( EnvironmentPreference::Any }; - // (1) Explicit request from user - let mut request = request.map(|request| PythonRequest::parse(&request)); - - // (2) Request from `.python-version` - if request.is_none() { - request = PythonVersionFile::discover(project_dir, no_config, false) - .await? - .and_then(PythonVersionFile::into_version); - } - - // (3) `Requires-Python` in `pyproject.toml` - if request.is_none() && !no_project { - let project = - match VirtualProject::discover(project_dir, &DiscoveryOptions::default()).await { - Ok(project) => Some(project), - Err(WorkspaceError::MissingProject(_)) => None, - Err(WorkspaceError::MissingPyprojectToml) => None, - Err(WorkspaceError::NonWorkspace(_)) => None, - Err(err) => { - warn_user_once!("{err}"); - None - } - }; - - if let Some(project) = project { - request = find_requires_python(project.workspace()) - .as_ref() - .map(RequiresPython::specifiers) - .map(|specifiers| { - PythonRequest::Version(VersionRequest::Range( - specifiers.clone(), - PythonVariant::Default, - )) - }); + let project = if no_project { + None + } else { + match VirtualProject::discover(project_dir, &DiscoveryOptions::default()).await { + Ok(project) => Some(project), + Err(WorkspaceError::MissingProject(_)) => None, + Err(WorkspaceError::MissingPyprojectToml) => None, + Err(WorkspaceError::NonWorkspace(_)) => None, + Err(err) => { + warn_user_once!("{err}"); + None + } } - } + }; + + let WorkspacePython { + source, + python_request, + requires_python, + } = WorkspacePython::from_request( + request.map(|request| PythonRequest::parse(&request)), + project.as_ref().map(VirtualProject::workspace), + project_dir, + no_config, + ) + .await?; let python = PythonInstallation::find( - &request.unwrap_or_default(), + &python_request.unwrap_or_default(), environment_preference, python_preference, cache, )?; + // Warn if the discovered Python version is incompatible with the current workspace + if let Some(requires_python) = requires_python { + match validate_requires_python( + python.interpreter(), + project.as_ref().map(VirtualProject::workspace), + &requires_python, + &source, + ) { + Ok(()) => {} + Err(err) => { + warn_user!("{err}"); + } + } + }; + println!( "{}", std::path::absolute(python.interpreter().sys_executable())?.simplified_display() diff --git a/crates/uv/src/commands/python/install.rs b/crates/uv/src/commands/python/install.rs index 14f6db53ad69..37e7778603b5 100644 --- a/crates/uv/src/commands/python/install.rs +++ b/crates/uv/src/commands/python/install.rs @@ -18,7 +18,10 @@ use uv_python::downloads::{DownloadResult, ManagedPythonDownload, PythonDownload use uv_python::managed::{ python_executable_dir, ManagedPythonInstallation, ManagedPythonInstallations, }; -use uv_python::{PythonDownloads, PythonInstallationKey, PythonRequest, PythonVersionFile}; +use uv_python::{ + PythonDownloads, PythonInstallationKey, PythonRequest, PythonVersionFile, + VersionFileDiscoveryOptions, VersionFilePreference, +}; use uv_shell::Shell; use uv_trampoline_builder::{Launcher, LauncherKind}; use uv_warnings::warn_user; @@ -123,17 +126,22 @@ pub(crate) async fn install( // Resolve the requests let mut is_default_install = false; let requests: Vec<_> = if targets.is_empty() { - PythonVersionFile::discover(project_dir, no_config, true) - .await? - .map(PythonVersionFile::into_versions) - .unwrap_or_else(|| { - // If no version file is found and no requests were made - is_default_install = true; - vec![PythonRequest::Default] - }) - .into_iter() - .map(InstallRequest::new) - .collect::>>()? + PythonVersionFile::discover( + project_dir, + &VersionFileDiscoveryOptions::default() + .with_no_config(no_config) + .with_preference(VersionFilePreference::Versions), + ) + .await? + .map(PythonVersionFile::into_versions) + .unwrap_or_else(|| { + // If no version file is found and no requests were made + is_default_install = true; + vec![PythonRequest::Default] + }) + .into_iter() + .map(InstallRequest::new) + .collect::>>()? } else { targets .iter() diff --git a/crates/uv/src/commands/python/pin.rs b/crates/uv/src/commands/python/pin.rs index f588e624a3e1..da43dd56cb0b 100644 --- a/crates/uv/src/commands/python/pin.rs +++ b/crates/uv/src/commands/python/pin.rs @@ -10,7 +10,7 @@ use uv_cache::Cache; use uv_fs::Simplified; use uv_python::{ EnvironmentPreference, PythonInstallation, PythonPreference, PythonRequest, PythonVersionFile, - PYTHON_VERSION_FILENAME, + VersionFileDiscoveryOptions, PYTHON_VERSION_FILENAME, }; use uv_warnings::warn_user_once; use uv_workspace::{DiscoveryOptions, VirtualProject}; @@ -40,7 +40,8 @@ pub(crate) async fn pin( } }; - let version_file = PythonVersionFile::discover(project_dir, false, false).await; + let version_file = + PythonVersionFile::discover(project_dir, &VersionFileDiscoveryOptions::default()).await; let Some(request) = request else { // Display the current pinned Python version diff --git a/crates/uv/src/commands/venv.rs b/crates/uv/src/commands/venv.rs index 60ab7a3535f7..7f2d8cbf718e 100644 --- a/crates/uv/src/commands/venv.rs +++ b/crates/uv/src/commands/venv.rs @@ -22,17 +22,16 @@ use uv_install_wheel::linker::LinkMode; use uv_pypi_types::Requirement; use uv_python::{ EnvironmentPreference, PythonDownloads, PythonInstallation, PythonPreference, PythonRequest, - PythonVariant, PythonVersionFile, VersionRequest, }; -use uv_resolver::{ExcludeNewer, FlatIndex, RequiresPython}; +use uv_resolver::{ExcludeNewer, FlatIndex}; use uv_shell::Shell; use uv_types::{BuildContext, BuildIsolation, HashStrategy}; -use uv_warnings::warn_user_once; +use uv_warnings::{warn_user, warn_user_once}; use uv_workspace::{DiscoveryOptions, VirtualProject, WorkspaceError}; use crate::commands::pip::loggers::{DefaultInstallLogger, InstallLogger}; use crate::commands::pip::operations::Changelog; -use crate::commands::project::find_requires_python; +use crate::commands::project::{validate_requires_python, WorkspacePython}; use crate::commands::reporters::PythonDownloadReporter; use crate::commands::{ExitStatus, SharedState}; use crate::printer::Printer; @@ -184,35 +183,22 @@ async fn venv_impl( let reporter = PythonDownloadReporter::single(printer); - // (1) Explicit request from user - let mut interpreter_request = python_request.map(PythonRequest::parse); - - // (2) Request from `.python-version` - if interpreter_request.is_none() { - interpreter_request = PythonVersionFile::discover(project_dir, no_config, false) - .await - .into_diagnostic()? - .and_then(PythonVersionFile::into_version); - } - - // (3) `Requires-Python` in `pyproject.toml` - if interpreter_request.is_none() { - if let Some(project) = project { - interpreter_request = find_requires_python(project.workspace()) - .as_ref() - .map(RequiresPython::specifiers) - .map(|specifiers| { - PythonRequest::Version(VersionRequest::Range( - specifiers.clone(), - PythonVariant::Default, - )) - }); - } - } + let WorkspacePython { + source, + python_request, + requires_python, + } = WorkspacePython::from_request( + python_request.map(PythonRequest::parse), + project.as_ref().map(VirtualProject::workspace), + project_dir, + no_config, + ) + .await + .into_diagnostic()?; // Locate the Python interpreter to use in the environment let python = PythonInstallation::find_or_download( - interpreter_request.as_ref(), + python_request.as_ref(), EnvironmentPreference::OnlySystem, python_preference, python_downloads, @@ -253,6 +239,21 @@ async fn venv_impl( .into_diagnostic()?; } + // Check if the discovered Python version is incompatible with the current workspace + if let Some(requires_python) = requires_python { + match validate_requires_python( + &interpreter, + project.as_ref().map(VirtualProject::workspace), + &requires_python, + &source, + ) { + Ok(()) => {} + Err(err) => { + warn_user!("{err}"); + } + } + }; + writeln!( printer.stderr(), "Creating virtual environment {}at: {}", diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index 97062b0a0e79..efc66f4fc257 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -1278,6 +1278,7 @@ async fn run_project( globals.connectivity, globals.native_tls, &globals.allow_insecure_host, + no_config, &cache, printer, ) @@ -1374,6 +1375,7 @@ async fn run_project( globals.concurrency, globals.native_tls, &globals.allow_insecure_host, + no_config, &cache, printer, ) @@ -1403,6 +1405,7 @@ async fn run_project( globals.concurrency, globals.native_tls, &globals.allow_insecure_host, + no_config, &cache, printer, ) @@ -1455,6 +1458,7 @@ async fn run_project( globals.concurrency, globals.native_tls, &globals.allow_insecure_host, + no_config, &cache, printer, )) @@ -1496,6 +1500,7 @@ async fn run_project( globals.concurrency, globals.native_tls, &globals.allow_insecure_host, + no_config, &cache, printer, ) @@ -1531,6 +1536,7 @@ async fn run_project( globals.concurrency, globals.native_tls, &globals.allow_insecure_host, + no_config, &cache, printer, ) @@ -1566,6 +1572,7 @@ async fn run_project( globals.concurrency, globals.native_tls, &globals.allow_insecure_host, + no_config, globals.quiet, &cache, printer, diff --git a/crates/uv/tests/it/init.rs b/crates/uv/tests/it/init.rs index e56bdda39ab9..a40ac347c5dc 100644 --- a/crates/uv/tests/it/init.rs +++ b/crates/uv/tests/it/init.rs @@ -2005,6 +2005,43 @@ fn init_requires_python_specifiers() -> Result<()> { Ok(()) } +/// Run `uv init`, inferring the `requires-python` from the `.python-version` file. +#[test] +fn init_requires_python_version_file() -> Result<()> { + let context = TestContext::new_with_versions(&["3.8", "3.12"]); + + context.temp_dir.child(".python-version").write_str("3.8")?; + + let child = context.temp_dir.join("foo"); + uv_snapshot!(context.filters(), context.init().current_dir(&context.temp_dir).arg(&child), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Initialized project `foo` at `[TEMP_DIR]/foo` + "###); + + let pyproject_toml = fs_err::read_to_string(child.join("pyproject.toml"))?; + insta::with_settings!({ + filters => context.filters(), + }, { + assert_snapshot!( + pyproject_toml, @r###" + [project] + name = "foo" + version = "0.1.0" + description = "Add your description here" + readme = "README.md" + requires-python = ">=3.8" + dependencies = [] + "### + ); + }); + + Ok(()) +} + /// Run `uv init` from within an unmanaged project. #[test] fn init_unmanaged() -> Result<()> { diff --git a/crates/uv/tests/it/python_find.rs b/crates/uv/tests/it/python_find.rs index aa92f83c6f97..2f0691f7cb8c 100644 --- a/crates/uv/tests/it/python_find.rs +++ b/crates/uv/tests/it/python_find.rs @@ -195,11 +195,43 @@ fn python_find_pin() { ----- stderr ----- "###); + + let child_dir = context.temp_dir.child("child"); + child_dir.create_dir_all().unwrap(); + + // We should also find pinned versions in the parent directory + uv_snapshot!(context.filters(), context.python_find().current_dir(&child_dir), @r###" + success: true + exit_code: 0 + ----- stdout ----- + [PYTHON-3.12] + + ----- stderr ----- + "###); + + uv_snapshot!(context.filters(), context.python_pin().arg("3.11").current_dir(&child_dir), @r###" + success: true + exit_code: 0 + ----- stdout ----- + Updated `.python-version` from `3.12` -> `3.11` + + ----- stderr ----- + "###); + + // Unless the child directory also has a pin + uv_snapshot!(context.filters(), context.python_find().current_dir(&child_dir), @r###" + success: true + exit_code: 0 + ----- stdout ----- + [PYTHON-3.11] + + ----- stderr ----- + "###); } #[test] fn python_find_project() { - let context: TestContext = TestContext::new_with_versions(&["3.11", "3.12"]); + let context: TestContext = TestContext::new_with_versions(&["3.10", "3.11", "3.12"]); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml @@ -207,7 +239,7 @@ fn python_find_project() { [project] name = "project" version = "0.1.0" - requires-python = ">=3.12" + requires-python = ">=3.11" dependencies = ["anyio==3.7.0"] "#}) .unwrap(); @@ -217,19 +249,20 @@ fn python_find_project() { success: true exit_code: 0 ----- stdout ----- - [PYTHON-3.12] + [PYTHON-3.11] ----- stderr ----- "###); // Unless explicitly requested - uv_snapshot!(context.filters(), context.python_find().arg("3.11"), @r###" + uv_snapshot!(context.filters(), context.python_find().arg("3.10"), @r###" success: true exit_code: 0 ----- stdout ----- - [PYTHON-3.11] + [PYTHON-3.10] ----- stderr ----- + warning: The requested interpreter resolved to Python 3.10.[X], which is incompatible with the project's Python requirement: `>=3.11` "###); // Or `--no-project` is used @@ -237,6 +270,69 @@ fn python_find_project() { success: true exit_code: 0 ----- stdout ----- + [PYTHON-3.10] + + ----- stderr ----- + "###); + + // But a pin should take precedence + uv_snapshot!(context.filters(), context.python_pin().arg("3.12"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + Pinned `.python-version` to `3.12` + + ----- stderr ----- + "###); + uv_snapshot!(context.filters(), context.python_find(), @r###" + success: true + exit_code: 0 + ----- stdout ----- + [PYTHON-3.12] + + ----- stderr ----- + "###); + + // Create a pin that's incompatible with the project + uv_snapshot!(context.filters(), context.python_pin().arg("3.10").arg("--no-workspace"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + Updated `.python-version` from `3.12` -> `3.10` + + ----- stderr ----- + "###); + + // We should warn on subsequent uses, but respect the pinned version? + uv_snapshot!(context.filters(), context.python_find(), @r###" + success: true + exit_code: 0 + ----- stdout ----- + [PYTHON-3.10] + + ----- stderr ----- + warning: The Python request from `.python-version` resolved to Python 3.10.[X], which is incompatible with the project's Python requirement: `>=3.11` + "###); + + // Unless the pin file is outside the project, in which case we should just ignore it + let child_dir = context.temp_dir.child("child"); + child_dir.create_dir_all().unwrap(); + + let pyproject_toml = child_dir.child("pyproject.toml"); + pyproject_toml + .write_str(indoc! {r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.11" + dependencies = ["anyio==3.7.0"] + "#}) + .unwrap(); + + uv_snapshot!(context.filters(), context.python_find().current_dir(&child_dir), @r###" + success: true + exit_code: 0 + ----- stdout ----- [PYTHON-3.11] ----- stderr ----- diff --git a/crates/uv/tests/it/sync.rs b/crates/uv/tests/it/sync.rs index 15a0e584c284..d4792ccf2998 100644 --- a/crates/uv/tests/it/sync.rs +++ b/crates/uv/tests/it/sync.rs @@ -2434,6 +2434,7 @@ fn sync_custom_environment_path() -> Result<()> { ----- stderr ----- Using CPython 3.11.[X] interpreter at: [PYTHON-3.11] + warning: The requested interpreter resolved to Python 3.11.[X], which is incompatible with the project's Python requirement: `>=3.12` Creating virtual environment at: foo Activate with: source foo/[BIN]/activate "###); @@ -3603,6 +3604,7 @@ fn sync_invalid_environment() -> Result<()> { ----- stderr ----- Using CPython 3.11.[X] interpreter at: [PYTHON-3.11] + warning: The requested interpreter resolved to Python 3.11.[X], which is incompatible with the project's Python requirement: `>=3.12` Creating virtual environment at: .venv Activate with: source .venv/[BIN]/activate "###); @@ -3669,6 +3671,7 @@ fn sync_invalid_environment() -> Result<()> { ----- stderr ----- Using CPython 3.11.[X] interpreter at: [PYTHON-3.11] + warning: The requested interpreter resolved to Python 3.11.[X], which is incompatible with the project's Python requirement: `>=3.12` Creating virtual environment at: .venv Activate with: source .venv/[BIN]/activate "###); @@ -3755,6 +3758,127 @@ fn sync_no_sources_missing_member() -> Result<()> { Ok(()) } +#[test] +fn sync_python_version() -> Result<()> { + let context: TestContext = TestContext::new_with_versions(&["3.10", "3.11", "3.12"]); + + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str(indoc::indoc! {r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.11" + dependencies = ["anyio==3.7.0"] + "#})?; + + // We should respect the project's required version, not the first on the path + uv_snapshot!(context.filters(), context.sync(), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Using CPython 3.11.[X] interpreter at: [PYTHON-3.11] + Creating virtual environment at: .venv + Resolved 4 packages in [TIME] + Prepared 3 packages in [TIME] + Installed 3 packages in [TIME] + + anyio==3.7.0 + + idna==3.6 + + sniffio==1.3.1 + "###); + + // Unless explicitly requested... + uv_snapshot!(context.filters(), context.sync().arg("--python").arg("3.10"), @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + Using CPython 3.10.[X] interpreter at: [PYTHON-3.10] + error: The requested interpreter resolved to Python 3.10.[X], which is incompatible with the project's Python requirement: `>=3.11` + "###); + + // But a pin should take precedence + uv_snapshot!(context.filters(), context.python_pin().arg("3.12"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + Pinned `.python-version` to `3.12` + + ----- stderr ----- + "###); + + uv_snapshot!(context.filters(), context.sync(), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Using CPython 3.12.[X] interpreter at: [PYTHON-3.12] + Removed virtual environment at: .venv + Creating virtual environment at: .venv + Resolved 4 packages in [TIME] + Installed 3 packages in [TIME] + + anyio==3.7.0 + + idna==3.6 + + sniffio==1.3.1 + "###); + + // Create a pin that's incompatible with the project + uv_snapshot!(context.filters(), context.python_pin().arg("3.10").arg("--no-workspace"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + Updated `.python-version` from `3.12` -> `3.10` + + ----- stderr ----- + "###); + + // We should warn on subsequent uses, but respect the pinned version? + uv_snapshot!(context.filters(), context.sync(), @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + Using CPython 3.10.[X] interpreter at: [PYTHON-3.10] + error: The Python request from `.python-version` resolved to Python 3.10.[X], which is incompatible with the project's Python requirement: `>=3.11` + "###); + + // Unless the pin file is outside the project, in which case we should just ignore it entirely + let child_dir = context.temp_dir.child("child"); + child_dir.create_dir_all().unwrap(); + + let pyproject_toml = child_dir.child("pyproject.toml"); + pyproject_toml + .write_str(indoc::indoc! {r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.11" + dependencies = ["anyio==3.7.0"] + "#}) + .unwrap(); + + uv_snapshot!(context.filters(), context.sync().current_dir(&child_dir), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Using CPython 3.11.[X] interpreter at: [PYTHON-3.11] + Creating virtual environment at: .venv + Resolved 4 packages in [TIME] + Installed 3 packages in [TIME] + + anyio==3.7.0 + + idna==3.6 + + sniffio==1.3.1 + "###); + + Ok(()) +} + #[test] fn sync_explicit() -> Result<()> { let context = TestContext::new("3.12"); diff --git a/crates/uv/tests/it/venv.rs b/crates/uv/tests/it/venv.rs index 4e0d9dd2f97d..5cd54a971290 100644 --- a/crates/uv/tests/it/venv.rs +++ b/crates/uv/tests/it/venv.rs @@ -475,6 +475,20 @@ fn create_venv_respects_pyproject_requires_python() -> Result<()> { context.venv.assert(predicates::path::is_dir()); + // We warn if we receive an incompatible version + uv_snapshot!(context.filters(), context.venv().arg("--python").arg("3.11"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Using CPython 3.11.[X] interpreter at: [PYTHON-3.11] + warning: The requested interpreter resolved to Python 3.11.[X], which is incompatible with the project's Python requirement: `>=3.12` + Creating virtual environment at: .venv + Activate with: source .venv/[BIN]/activate + "### + ); + Ok(()) } From 9270dd3c9938a08495ed9fdcc799378277d97d59 Mon Sep 17 00:00:00 2001 From: Eric Mark Martin Date: Tue, 5 Nov 2024 22:18:43 -0500 Subject: [PATCH 11/19] Implement PEP 440-compliant local version semantics (#8797) Implement a full working version of local version semantics. The (AFAIA) major move towards this was implemented in #2430. This added support such that the version specifier `torch==2.1.0+cpu` would install `torch@2.1.0+cpu` and consider `torch@2.1.0+cpu` a valid way to satisfy the requirement `torch==2.1.0` in further dependency resolution. In this feature, we more fully support local version semantics. Namely, we now allow `torch==2.1.0` to install `torch@2.1.0+cpu` regardless of whether `torch@2.1.0` (no local tag) actually exists. We do this by adding an internal-only `Max` value to local versions that compare greater to all other local versions. Then we can translate `torch==2.1.0` into bounds: greater than 2.1.0 with no local tag and less than 2.1.0 with the `Max` local tag. Depends on https://github.com/astral-sh/packse/pull/227. --- crates/uv-pep440/src/lib.rs | 5 +- crates/uv-pep440/src/version.rs | 134 ++- crates/uv-pep440/src/version/tests.rs | 66 +- crates/uv-pep440/src/version_ranges.rs | 25 +- crates/uv-pep440/src/version_specifier.rs | 7 +- .../uv-pep440/src/version_specifier/tests.rs | 15 +- crates/uv-python/src/discovery.rs | 4 +- crates/uv-resolver/src/error.rs | 193 ++++- crates/uv-resolver/src/resolver/mod.rs | 4 +- crates/uv/tests/it/lock_scenarios.rs | 8 +- crates/uv/tests/it/pip_compile.rs | 97 ++- crates/uv/tests/it/pip_install_scenarios.rs | 116 ++- ...it__ecosystem__transformers-lock-file.snap | 781 +++--------------- ...cosystem__transformers-uv-lock-output.snap | 4 +- scripts/scenarios/generate.py | 12 - 15 files changed, 631 insertions(+), 840 deletions(-) diff --git a/crates/uv-pep440/src/lib.rs b/crates/uv-pep440/src/lib.rs index 982d6ede93c1..6fe6c531952d 100644 --- a/crates/uv-pep440/src/lib.rs +++ b/crates/uv-pep440/src/lib.rs @@ -27,8 +27,9 @@ pub use version_ranges::{release_specifier_to_range, release_specifiers_to_ranges}; pub use { version::{ - LocalSegment, Operator, OperatorParseError, Prerelease, PrereleaseKind, Version, - VersionParseError, VersionPattern, VersionPatternParseError, MIN_VERSION, + LocalSegment, LocalVersion, LocalVersionSlice, Operator, OperatorParseError, Prerelease, + PrereleaseKind, Version, VersionParseError, VersionPattern, VersionPatternParseError, + MIN_VERSION, }, version_specifier::{ VersionSpecifier, VersionSpecifierBuildError, VersionSpecifiers, diff --git a/crates/uv-pep440/src/version.rs b/crates/uv-pep440/src/version.rs index ae7fbebcaa88..0e36d2620262 100644 --- a/crates/uv-pep440/src/version.rs +++ b/crates/uv-pep440/src/version.rs @@ -388,10 +388,10 @@ impl Version { /// Returns the local segments in this version, if any exist. #[inline] - pub fn local(&self) -> &[LocalSegment] { + pub fn local(&self) -> LocalVersionSlice { match *self.inner { VersionInner::Small { ref small } => small.local(), - VersionInner::Full { ref full } => &full.local, + VersionInner::Full { ref full } => full.local.as_slice(), } } @@ -530,15 +530,28 @@ impl Version { /// Set the local segments and return the updated version. #[inline] #[must_use] - pub fn with_local(mut self, value: Vec) -> Self { + pub fn with_local_segments(mut self, value: Vec) -> Self { if value.is_empty() { self.without_local() } else { - self.make_full().local = value; + self.make_full().local = LocalVersion::Segments(value); self } } + /// Set the local version and return the updated version. + #[inline] + #[must_use] + pub fn with_local(mut self, value: LocalVersion) -> Self { + match value { + LocalVersion::Segments(segments) => self.with_local_segments(segments), + LocalVersion::Max => { + self.make_full().local = value; + self + } + } + } + /// For PEP 440 specifier matching: "Except where specifically noted below, /// local version identifiers MUST NOT be permitted in version specifiers, /// and local version labels MUST be ignored entirely when checking if @@ -615,7 +628,7 @@ impl Version { pre: small.pre(), post: small.post(), dev: small.dev(), - local: vec![], + local: LocalVersion::Segments(vec![]), }; *self = Self { inner: Arc::new(VersionInner::Full { full }), @@ -712,14 +725,12 @@ impl std::fmt::Display for Version { let local = if self.local().is_empty() { String::new() } else { - format!( - "+{}", - self.local() - .iter() - .map(ToString::to_string) - .collect::>() - .join(".") - ) + match self.local() { + LocalVersionSlice::Segments(_) => { + format!("+{}", self.local()) + } + LocalVersionSlice::Max => String::new(), + } }; write!(f, "{epoch}{release}{pre}{post}{dev}{local}") } @@ -1195,10 +1206,10 @@ impl VersionSmall { #[inline] #[allow(clippy::unused_self)] - fn local(&self) -> &[LocalSegment] { + fn local(&self) -> LocalVersionSlice { // A "small" version is never used if the version has a non-zero number // of local segments. - &[] + LocalVersionSlice::Segments(&[]) } #[inline] @@ -1283,7 +1294,7 @@ struct VersionFull { /// /// Local versions allow multiple segments separated by periods, such as `deadbeef.1.2.3`, see /// [`LocalSegment`] for details on the semantics. - local: Vec, + local: LocalVersion, /// An internal-only segment that does not exist in PEP 440, used to /// represent the smallest possible version of a release, preceding any /// `dev`, `pre`, `post` or releases. @@ -1414,6 +1425,93 @@ impl std::fmt::Display for Prerelease { } } +/// Either a sequence of local segments or [`LocalVersion::Sentinel`], an internal-only value that +/// compares greater than all other local versions. +#[derive(Eq, PartialEq, Debug, Clone, Hash)] +#[cfg_attr( + feature = "rkyv", + derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize) +)] +#[cfg_attr(feature = "rkyv", rkyv(derive(Debug, Eq, PartialEq, PartialOrd, Ord)))] +pub enum LocalVersion { + /// A sequence of local segments. + Segments(Vec), + /// An internal-only value that compares greater to all other local versions. + Max, +} + +/// Like [`LocalVersion`], but using a slice +#[derive(Eq, PartialEq, Debug, Clone, Hash)] +pub enum LocalVersionSlice<'a> { + /// Like [`LocalVersion::Segments`] + Segments(&'a [LocalSegment]), + /// Like [`LocalVersion::Sentinel`] + Max, +} + +impl LocalVersion { + /// Convert the local version segments into a slice. + pub fn as_slice(&self) -> LocalVersionSlice<'_> { + match self { + LocalVersion::Segments(segments) => LocalVersionSlice::Segments(segments), + LocalVersion::Max => LocalVersionSlice::Max, + } + } + + /// Clear the local version segments, if they exist. + pub fn clear(&mut self) { + match self { + Self::Segments(segments) => segments.clear(), + Self::Max => *self = Self::Segments(Vec::new()), + } + } +} + +/// Output the local version identifier string. +/// +/// [`LocalVersionSlice::Max`] maps to `"[max]"` which is otherwise an illegal local +/// version because `[` and `]` are not allowed. +impl std::fmt::Display for LocalVersionSlice<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + LocalVersionSlice::Segments(segments) => { + for (i, segment) in segments.iter().enumerate() { + if i > 0 { + write!(f, ".")?; + } + write!(f, "{segment}")?; + } + Ok(()) + } + LocalVersionSlice::Max => write!(f, "[max]"), + } + } +} + +impl PartialOrd for LocalVersionSlice<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for LocalVersionSlice<'_> { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (LocalVersionSlice::Segments(lv1), LocalVersionSlice::Segments(lv2)) => lv1.cmp(lv2), + (LocalVersionSlice::Segments(_), LocalVersionSlice::Max) => Ordering::Less, + (LocalVersionSlice::Max, LocalVersionSlice::Segments(_)) => Ordering::Greater, + (LocalVersionSlice::Max, LocalVersionSlice::Max) => Ordering::Equal, + } + } +} + +impl LocalVersionSlice<'_> { + /// Whether the local version is absent + pub fn is_empty(&self) -> bool { + matches!(self, Self::Segments(&[])) + } +} + /// A part of the [local version identifier]() /// /// Local versions are a mess: @@ -1855,7 +1953,7 @@ impl<'a> Parser<'a> { .with_pre(self.pre) .with_post(self.post) .with_dev(self.dev) - .with_local(self.local); + .with_local(LocalVersion::Segments(self.local)); VersionPattern { version, wildcard: self.wildcard, @@ -2326,7 +2424,7 @@ pub(crate) fn compare_release(this: &[u64], other: &[u64]) -> Ordering { /// implementation /// /// [pep440-suffix-ordering]: https://peps.python.org/pep-0440/#summary-of-permitted-suffixes-and-relative-ordering -fn sortable_tuple(version: &Version) -> (u64, u64, Option, u64, &[LocalSegment]) { +fn sortable_tuple(version: &Version) -> (u64, u64, Option, u64, LocalVersionSlice) { // If the version is a "max" version, use a post version larger than any possible post version. let post = if version.max().is_some() { Some(u64::MAX) diff --git a/crates/uv-pep440/src/version/tests.rs b/crates/uv-pep440/src/version/tests.rs index 54aa2e51f20f..2d10a2e668a6 100644 --- a/crates/uv-pep440/src/version/tests.rs +++ b/crates/uv-pep440/src/version/tests.rs @@ -125,46 +125,50 @@ fn test_packaging_versions() { ("1.1.dev1", Version::new([1, 1]).with_dev(Some(1))), ( "1.2+123abc", - Version::new([1, 2]).with_local(vec![LocalSegment::String("123abc".to_string())]), + Version::new([1, 2]) + .with_local_segments(vec![LocalSegment::String("123abc".to_string())]), ), ( "1.2+123abc456", - Version::new([1, 2]).with_local(vec![LocalSegment::String("123abc456".to_string())]), + Version::new([1, 2]) + .with_local_segments(vec![LocalSegment::String("123abc456".to_string())]), ), ( "1.2+abc", - Version::new([1, 2]).with_local(vec![LocalSegment::String("abc".to_string())]), + Version::new([1, 2]).with_local_segments(vec![LocalSegment::String("abc".to_string())]), ), ( "1.2+abc123", - Version::new([1, 2]).with_local(vec![LocalSegment::String("abc123".to_string())]), + Version::new([1, 2]) + .with_local_segments(vec![LocalSegment::String("abc123".to_string())]), ), ( "1.2+abc123def", - Version::new([1, 2]).with_local(vec![LocalSegment::String("abc123def".to_string())]), + Version::new([1, 2]) + .with_local_segments(vec![LocalSegment::String("abc123def".to_string())]), ), ( "1.2+1234.abc", - Version::new([1, 2]).with_local(vec![ + Version::new([1, 2]).with_local_segments(vec![ LocalSegment::Number(1234), LocalSegment::String("abc".to_string()), ]), ), ( "1.2+123456", - Version::new([1, 2]).with_local(vec![LocalSegment::Number(123_456)]), + Version::new([1, 2]).with_local_segments(vec![LocalSegment::Number(123_456)]), ), ( "1.2.r32+123456", Version::new([1, 2]) .with_post(Some(32)) - .with_local(vec![LocalSegment::Number(123_456)]), + .with_local_segments(vec![LocalSegment::Number(123_456)]), ), ( "1.2.rev33+123456", Version::new([1, 2]) .with_post(Some(33)) - .with_local(vec![LocalSegment::Number(123_456)]), + .with_local_segments(vec![LocalSegment::Number(123_456)]), ), // Explicit epoch of 1 ( @@ -316,35 +320,35 @@ fn test_packaging_versions() { "1!1.2+123abc", Version::new([1, 2]) .with_epoch(1) - .with_local(vec![LocalSegment::String("123abc".to_string())]), + .with_local_segments(vec![LocalSegment::String("123abc".to_string())]), ), ( "1!1.2+123abc456", Version::new([1, 2]) .with_epoch(1) - .with_local(vec![LocalSegment::String("123abc456".to_string())]), + .with_local_segments(vec![LocalSegment::String("123abc456".to_string())]), ), ( "1!1.2+abc", Version::new([1, 2]) .with_epoch(1) - .with_local(vec![LocalSegment::String("abc".to_string())]), + .with_local_segments(vec![LocalSegment::String("abc".to_string())]), ), ( "1!1.2+abc123", Version::new([1, 2]) .with_epoch(1) - .with_local(vec![LocalSegment::String("abc123".to_string())]), + .with_local_segments(vec![LocalSegment::String("abc123".to_string())]), ), ( "1!1.2+abc123def", Version::new([1, 2]) .with_epoch(1) - .with_local(vec![LocalSegment::String("abc123def".to_string())]), + .with_local_segments(vec![LocalSegment::String("abc123def".to_string())]), ), ( "1!1.2+1234.abc", - Version::new([1, 2]).with_epoch(1).with_local(vec![ + Version::new([1, 2]).with_epoch(1).with_local_segments(vec![ LocalSegment::Number(1234), LocalSegment::String("abc".to_string()), ]), @@ -353,28 +357,28 @@ fn test_packaging_versions() { "1!1.2+123456", Version::new([1, 2]) .with_epoch(1) - .with_local(vec![LocalSegment::Number(123_456)]), + .with_local_segments(vec![LocalSegment::Number(123_456)]), ), ( "1!1.2.r32+123456", Version::new([1, 2]) .with_epoch(1) .with_post(Some(32)) - .with_local(vec![LocalSegment::Number(123_456)]), + .with_local_segments(vec![LocalSegment::Number(123_456)]), ), ( "1!1.2.rev33+123456", Version::new([1, 2]) .with_epoch(1) .with_post(Some(33)) - .with_local(vec![LocalSegment::Number(123_456)]), + .with_local_segments(vec![LocalSegment::Number(123_456)]), ), ( "98765!1.2.rev33+123456", Version::new([1, 2]) .with_epoch(98765) .with_post(Some(33)) - .with_local(vec![LocalSegment::Number(123_456)]), + .with_local_segments(vec![LocalSegment::Number(123_456)]), ), ]; for (string, structured) in versions { @@ -879,50 +883,50 @@ fn parse_version_valid() { // local tests assert_eq!( p("5+2"), - Version::new([5]).with_local(vec![LocalSegment::Number(2)]) + Version::new([5]).with_local_segments(vec![LocalSegment::Number(2)]) ); assert_eq!( p("5+a"), - Version::new([5]).with_local(vec![LocalSegment::String("a".to_string())]) + Version::new([5]).with_local_segments(vec![LocalSegment::String("a".to_string())]) ); assert_eq!( p("5+abc.123"), - Version::new([5]).with_local(vec![ + Version::new([5]).with_local_segments(vec![ LocalSegment::String("abc".to_string()), LocalSegment::Number(123), ]) ); assert_eq!( p("5+123.abc"), - Version::new([5]).with_local(vec![ + Version::new([5]).with_local_segments(vec![ LocalSegment::Number(123), LocalSegment::String("abc".to_string()), ]) ); assert_eq!( p("5+18446744073709551615.abc"), - Version::new([5]).with_local(vec![ + Version::new([5]).with_local_segments(vec![ LocalSegment::Number(18_446_744_073_709_551_615), LocalSegment::String("abc".to_string()), ]) ); assert_eq!( p("5+18446744073709551616.abc"), - Version::new([5]).with_local(vec![ + Version::new([5]).with_local_segments(vec![ LocalSegment::String("18446744073709551616".to_string()), LocalSegment::String("abc".to_string()), ]) ); assert_eq!( p("5+ABC.123"), - Version::new([5]).with_local(vec![ + Version::new([5]).with_local_segments(vec![ LocalSegment::String("abc".to_string()), LocalSegment::Number(123), ]) ); assert_eq!( p("5+ABC-123.4_5_xyz-MNO"), - Version::new([5]).with_local(vec![ + Version::new([5]).with_local_segments(vec![ LocalSegment::String("abc".to_string()), LocalSegment::Number(123), LocalSegment::Number(4), @@ -933,21 +937,21 @@ fn parse_version_valid() { ); assert_eq!( p("5.6.7+abc-00123"), - Version::new([5, 6, 7]).with_local(vec![ + Version::new([5, 6, 7]).with_local_segments(vec![ LocalSegment::String("abc".to_string()), LocalSegment::Number(123), ]) ); assert_eq!( p("5.6.7+abc-foo00123"), - Version::new([5, 6, 7]).with_local(vec![ + Version::new([5, 6, 7]).with_local_segments(vec![ LocalSegment::String("abc".to_string()), LocalSegment::String("foo00123".to_string()), ]) ); assert_eq!( p("5.6.7+abc-00123a"), - Version::new([5, 6, 7]).with_local(vec![ + Version::new([5, 6, 7]).with_local_segments(vec![ LocalSegment::String("abc".to_string()), LocalSegment::String("00123a".to_string()), ]) @@ -992,7 +996,7 @@ fn parse_version_valid() { assert_eq!(p(" 5 "), Version::new([5])); assert_eq!( p(" 5.6.7+abc.123.xyz "), - Version::new([5, 6, 7]).with_local(vec![ + Version::new([5, 6, 7]).with_local_segments(vec![ LocalSegment::String("abc".to_string()), LocalSegment::Number(123), LocalSegment::String("xyz".to_string()) diff --git a/crates/uv-pep440/src/version_ranges.rs b/crates/uv-pep440/src/version_ranges.rs index 41c45fe291e1..a05d7be039a2 100644 --- a/crates/uv-pep440/src/version_ranges.rs +++ b/crates/uv-pep440/src/version_ranges.rs @@ -2,7 +2,10 @@ use version_ranges::Ranges; -use crate::{Operator, Prerelease, Version, VersionSpecifier, VersionSpecifiers}; +use crate::{ + LocalVersion, LocalVersionSlice, Operator, Prerelease, Version, VersionSpecifier, + VersionSpecifiers, +}; impl From for Ranges { /// Convert [`VersionSpecifiers`] to a PubGrub-compatible version range, using PEP 440 @@ -22,9 +25,23 @@ impl From for Ranges { fn from(specifier: VersionSpecifier) -> Self { let VersionSpecifier { operator, version } = specifier; match operator { - Operator::Equal => Ranges::singleton(version), + Operator::Equal => match version.local() { + LocalVersionSlice::Segments(&[]) => { + let low = version; + let high = low.clone().with_local(LocalVersion::Max); + Ranges::between(low, high) + } + LocalVersionSlice::Segments(_) => Ranges::singleton(version), + LocalVersionSlice::Max => unreachable!( + "found `LocalVersionSlice::Sentinel`, which should be an internal-only value" + ), + }, Operator::ExactEqual => Ranges::singleton(version), - Operator::NotEqual => Ranges::singleton(version).complement(), + Operator::NotEqual => Ranges::from(VersionSpecifier { + operator: Operator::Equal, + version, + }) + .complement(), Operator::TildeEqual => { let [rest @ .., last, _] = version.release() else { unreachable!("~= must have at least two segments"); @@ -45,7 +62,7 @@ impl From for Ranges { Ranges::strictly_lower_than(version.with_min(Some(0))) } } - Operator::LessThanEqual => Ranges::lower_than(version), + Operator::LessThanEqual => Ranges::lower_than(version.with_local(LocalVersion::Max)), Operator::GreaterThan => { // Per PEP 440: "The exclusive ordered comparison >V MUST NOT allow a post-release of // the given version unless V itself is a post release." diff --git a/crates/uv-pep440/src/version_specifier.rs b/crates/uv-pep440/src/version_specifier.rs index 4a62541e2ef4..2a9440254cdf 100644 --- a/crates/uv-pep440/src/version_specifier.rs +++ b/crates/uv-pep440/src/version_specifier.rs @@ -652,12 +652,7 @@ impl std::fmt::Display for VersionSpecifierBuildError { operator: ref op, ref version, } => { - let local = version - .local() - .iter() - .map(ToString::to_string) - .collect::>() - .join("."); + let local = version.local(); write!( f, "Operator {op} is incompatible with versions \ diff --git a/crates/uv-pep440/src/version_specifier/tests.rs b/crates/uv-pep440/src/version_specifier/tests.rs index f853aab285c4..ea0bdecc1c49 100644 --- a/crates/uv-pep440/src/version_specifier/tests.rs +++ b/crates/uv-pep440/src/version_specifier/tests.rs @@ -579,7 +579,8 @@ fn test_invalid_specifier() { ParseErrorKind::InvalidSpecifier( BuildErrorKind::OperatorLocalCombo { operator: Operator::TildeEqual, - version: Version::new([1, 0]).with_local(vec![LocalSegment::Number(5)]), + version: Version::new([1, 0]) + .with_local_segments(vec![LocalSegment::Number(5)]), } .into(), ) @@ -591,7 +592,7 @@ fn test_invalid_specifier() { BuildErrorKind::OperatorLocalCombo { operator: Operator::GreaterThanEqual, version: Version::new([1, 0]) - .with_local(vec![LocalSegment::String("deadbeef".to_string())]), + .with_local_segments(vec![LocalSegment::String("deadbeef".to_string())]), } .into(), ) @@ -603,7 +604,7 @@ fn test_invalid_specifier() { BuildErrorKind::OperatorLocalCombo { operator: Operator::LessThanEqual, version: Version::new([1, 0]) - .with_local(vec![LocalSegment::String("abc123".to_string())]), + .with_local_segments(vec![LocalSegment::String("abc123".to_string())]), } .into(), ) @@ -615,7 +616,7 @@ fn test_invalid_specifier() { BuildErrorKind::OperatorLocalCombo { operator: Operator::GreaterThan, version: Version::new([1, 0]) - .with_local(vec![LocalSegment::String("watwat".to_string())]), + .with_local_segments(vec![LocalSegment::String("watwat".to_string())]), } .into(), ) @@ -626,8 +627,10 @@ fn test_invalid_specifier() { ParseErrorKind::InvalidSpecifier( BuildErrorKind::OperatorLocalCombo { operator: Operator::LessThan, - version: Version::new([1, 0]) - .with_local(vec![LocalSegment::Number(1), LocalSegment::Number(0)]), + version: Version::new([1, 0]).with_local_segments(vec![ + LocalSegment::Number(1), + LocalSegment::Number(0), + ]), } .into(), ) diff --git a/crates/uv-python/src/discovery.rs b/crates/uv-python/src/discovery.rs index 1dfe18383762..2195b18bd980 100644 --- a/crates/uv-python/src/discovery.rs +++ b/crates/uv-python/src/discovery.rs @@ -2125,7 +2125,9 @@ impl FromStr for VersionRequest { return Err(Error::InvalidVersionRequest(s.to_string())); } - let [uv_pep440::LocalSegment::String(local)] = version.local() else { + let uv_pep440::LocalVersionSlice::Segments([uv_pep440::LocalSegment::String(local)]) = + version.local() + else { return Err(Error::InvalidVersionRequest(s.to_string())); }; diff --git a/crates/uv-resolver/src/error.rs b/crates/uv-resolver/src/error.rs index 926af4fcf46d..6c60f69a693f 100644 --- a/crates/uv-resolver/src/error.rs +++ b/crates/uv-resolver/src/error.rs @@ -1,10 +1,20 @@ -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::{BTreeMap, BTreeSet, Bound}; use std::fmt::Formatter; use std::sync::Arc; use indexmap::IndexSet; -use pubgrub::{DefaultStringReporter, DerivationTree, Derived, External, Range, Reporter}; +use pubgrub::{ + DefaultStringReporter, DerivationTree, Derived, External, Range, Ranges, Reporter, Term, +}; use rustc_hash::FxHashMap; +use tracing::trace; + +use uv_distribution_types::{ + BuiltDist, IndexCapabilities, IndexLocations, IndexUrl, InstalledDist, SourceDist, +}; +use uv_normalize::PackageName; +use uv_pep440::{LocalVersionSlice, Version}; +use uv_static::EnvVars; use crate::candidate_selector::CandidateSelector; use crate::dependency_provider::UvDependencyProvider; @@ -16,13 +26,6 @@ use crate::resolver::{ IncompletePackage, ResolverEnvironment, UnavailablePackage, UnavailableReason, }; use crate::Options; -use tracing::trace; -use uv_distribution_types::{ - BuiltDist, IndexCapabilities, IndexLocations, IndexUrl, InstalledDist, SourceDist, -}; -use uv_normalize::PackageName; -use uv_pep440::Version; -use uv_static::EnvVars; #[derive(Debug, thiserror::Error)] pub enum ResolveError { @@ -221,6 +224,178 @@ impl NoSolutionError { .expect("derivation tree should contain at least one external term") } + /// Simplifies the version ranges on any incompatibilities to remove the `[max]` sentinel. + /// + /// The `[max]` sentinel is used to represent the maximum local version of a package, to + /// implement PEP 440 semantics for local version equality. For example, `1.0.0+foo` needs to + /// satisfy `==1.0.0`. + pub(crate) fn collapse_local_version_segments(derivation_tree: ErrorTree) -> ErrorTree { + /// Remove local versions sentinels (`+[max]`) from the interval. + fn strip_sentinel( + mut lower: Bound, + mut upper: Bound, + ) -> (Bound, Bound) { + match (&lower, &upper) { + (Bound::Unbounded, Bound::Unbounded) => {} + (Bound::Unbounded, Bound::Included(v)) => { + // `<=1.0.0+[max]` is equivalent to `<=1.0.0` + if v.local() == LocalVersionSlice::Max { + upper = Bound::Included(v.clone().without_local()); + } + } + (Bound::Unbounded, Bound::Excluded(v)) => { + // `<1.0.0+[max]` is equivalent to `<1.0.0` + if v.local() == LocalVersionSlice::Max { + upper = Bound::Excluded(v.clone().without_local()); + } + } + (Bound::Included(v), Bound::Unbounded) => { + // `>=1.0.0+[max]` is equivalent to `>1.0.0` + if v.local() == LocalVersionSlice::Max { + lower = Bound::Excluded(v.clone().without_local()); + } + } + (Bound::Included(v), Bound::Included(b)) => { + // `>=1.0.0+[max]` is equivalent to `>1.0.0` + if v.local() == LocalVersionSlice::Max { + lower = Bound::Excluded(v.clone().without_local()); + } + // `<=1.0.0+[max]` is equivalent to `<=1.0.0` + if b.local() == LocalVersionSlice::Max { + upper = Bound::Included(b.clone().without_local()); + } + } + (Bound::Included(v), Bound::Excluded(b)) => { + // `>=1.0.0+[max]` is equivalent to `>1.0.0` + if v.local() == LocalVersionSlice::Max { + lower = Bound::Excluded(v.clone().without_local()); + } + // `<1.0.0+[max]` is equivalent to `<1.0.0` + if b.local() == LocalVersionSlice::Max { + upper = Bound::Included(b.clone().without_local()); + } + } + (Bound::Excluded(v), Bound::Unbounded) => { + // `>1.0.0+[max]` is equivalent to `>1.0.0` + if v.local() == LocalVersionSlice::Max { + lower = Bound::Excluded(v.clone().without_local()); + } + } + (Bound::Excluded(v), Bound::Included(b)) => { + // `>1.0.0+[max]` is equivalent to `>1.0.0` + if v.local() == LocalVersionSlice::Max { + lower = Bound::Excluded(v.clone().without_local()); + } + // `<=1.0.0+[max]` is equivalent to `<=1.0.0` + if b.local() == LocalVersionSlice::Max { + upper = Bound::Included(b.clone().without_local()); + } + } + (Bound::Excluded(v), Bound::Excluded(b)) => { + // `>1.0.0+[max]` is equivalent to `>1.0.0` + if v.local() == LocalVersionSlice::Max { + lower = Bound::Excluded(v.clone().without_local()); + } + // `<1.0.0+[max]` is equivalent to `<1.0.0` + if b.local() == LocalVersionSlice::Max { + upper = Bound::Excluded(b.clone().without_local()); + } + } + } + (lower, upper) + } + + /// Remove local versions sentinels (`+[max]`) from the version ranges. + #[allow(clippy::needless_pass_by_value)] + fn strip_sentinels(versions: Ranges) -> Ranges { + let mut range = Ranges::empty(); + for (lower, upper) in versions.iter() { + let (lower, upper) = strip_sentinel(lower.clone(), upper.clone()); + range = range.union(&Range::from_range_bounds((lower, upper))); + } + range + } + + /// Returns `true` if the range appears to be, e.g., `>1.0.0, <1.0.0+[max]`. + fn is_sentinel(versions: &Ranges) -> bool { + versions.iter().all(|(lower, upper)| { + let (Bound::Excluded(lower), Bound::Excluded(upper)) = (lower, upper) else { + return false; + }; + if lower.local() == LocalVersionSlice::Max { + return false; + } + if upper.local() != LocalVersionSlice::Max { + return false; + } + *lower == upper.clone().without_local() + }) + } + + fn strip(derivation_tree: ErrorTree) -> Option { + match derivation_tree { + DerivationTree::External(External::NotRoot(_, _)) => Some(derivation_tree), + DerivationTree::External(External::NoVersions(package, versions)) => { + if is_sentinel(&versions) { + return None; + } + + let versions = strip_sentinels(versions); + Some(DerivationTree::External(External::NoVersions( + package, versions, + ))) + } + DerivationTree::External(External::FromDependencyOf( + package1, + versions1, + package2, + versions2, + )) => { + let versions1 = strip_sentinels(versions1); + let versions2 = strip_sentinels(versions2); + Some(DerivationTree::External(External::FromDependencyOf( + package1, versions1, package2, versions2, + ))) + } + DerivationTree::External(External::Custom(package, versions, reason)) => { + let versions = strip_sentinels(versions); + Some(DerivationTree::External(External::Custom( + package, versions, reason, + ))) + } + DerivationTree::Derived(mut derived) => { + let cause1 = strip((*derived.cause1).clone()); + let cause2 = strip((*derived.cause2).clone()); + match (cause1, cause2) { + (Some(cause1), Some(cause2)) => Some(DerivationTree::Derived(Derived { + cause1: Arc::new(cause1), + cause2: Arc::new(cause2), + terms: std::mem::take(&mut derived.terms) + .into_iter() + .map(|(pkg, term)| { + let term = match term { + Term::Positive(versions) => { + Term::Positive(strip_sentinels(versions)) + } + Term::Negative(versions) => { + Term::Negative(strip_sentinels(versions)) + } + }; + (pkg, term) + }) + .collect(), + shared_id: derived.shared_id, + })), + (Some(cause), None) | (None, Some(cause)) => Some(cause), + _ => None, + } + } + } + } + + strip(derivation_tree).expect("derivation tree should contain at least one term") + } + /// Initialize a [`NoSolutionHeader`] for this error. pub fn header(&self) -> NoSolutionHeader { NoSolutionHeader::new(self.env.clone()) diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs index ba4fdafc78c1..46b788b138c6 100644 --- a/crates/uv-resolver/src/resolver/mod.rs +++ b/crates/uv-resolver/src/resolver/mod.rs @@ -1964,7 +1964,9 @@ impl ResolverState ResolveError { - err = NoSolutionError::collapse_proxies(err); + err = NoSolutionError::collapse_local_version_segments(NoSolutionError::collapse_proxies( + err, + )); let mut unavailable_packages = FxHashMap::default(); for package in err.packages() { diff --git a/crates/uv/tests/it/lock_scenarios.rs b/crates/uv/tests/it/lock_scenarios.rs index 782902336b70..c2edb01263cf 100644 --- a/crates/uv/tests/it/lock_scenarios.rs +++ b/crates/uv/tests/it/lock_scenarios.rs @@ -2780,7 +2780,7 @@ fn fork_non_local_fork_marker_direct() -> Result<()> { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-b{sys_platform == 'darwin'}==1.0.0 depends on package-c>=2.0.0 and package-a{sys_platform == 'linux'}==1.0.0 depends on package-c<2.0.0, we can conclude that package-a{sys_platform == 'linux'}==1.0.0 and package-b{sys_platform == 'darwin'}==1.0.0 are incompatible. + ╰─▶ Because package-a{sys_platform == 'linux'}==1.0.0 depends on package-c<2.0.0 and package-b{sys_platform == 'darwin'}==1.0.0 depends on package-c>=2.0.0, we can conclude that package-a{sys_platform == 'linux'}==1.0.0 and package-b{sys_platform == 'darwin'}==1.0.0 are incompatible. And because your project depends on package-a{sys_platform == 'linux'}==1.0.0 and package-b{sys_platform == 'darwin'}==1.0.0, we can conclude that your project's requirements are unsatisfiable. "### ); @@ -2852,11 +2852,11 @@ fn fork_non_local_fork_marker_transitive() -> Result<()> { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-b==1.0.0 depends on package-c{sys_platform == 'darwin'}>=2.0.0 and only package-c{sys_platform == 'darwin'}<=2.0.0 is available, we can conclude that package-b==1.0.0 depends on package-c{sys_platform == 'darwin'}==2.0.0. - And because only the following versions of package-c{sys_platform == 'linux'} are available: + ╰─▶ Because package-a==1.0.0 depends on package-c{sys_platform == 'linux'}<2.0.0 and only the following versions of package-c{sys_platform == 'linux'} are available: package-c{sys_platform == 'linux'}==1.0.0 package-c{sys_platform == 'linux'}>2.0.0 - and package-a==1.0.0 depends on package-c{sys_platform == 'linux'}<2.0.0, we can conclude that package-a==1.0.0 and package-b==1.0.0 are incompatible. + we can conclude that package-a==1.0.0 depends on package-c{sys_platform == 'linux'}==1.0.0. + And because only package-c{sys_platform == 'darwin'}<=2.0.0 is available and package-b==1.0.0 depends on package-c{sys_platform == 'darwin'}>=2.0.0, we can conclude that package-a==1.0.0 and package-b==1.0.0 are incompatible. And because your project depends on package-a==1.0.0 and package-b==1.0.0, we can conclude that your project's requirements are unsatisfiable. "### ); diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs index 36e14a04f01b..9dc2dc474d07 100644 --- a/crates/uv/tests/it/pip_compile.rs +++ b/crates/uv/tests/it/pip_compile.rs @@ -7179,7 +7179,7 @@ fn universal_transitive_disjoint_locals() -> Result<()> { # -r requirements.in # torchvision # triton - torchvision==0.15.1 + torchvision==0.15.1+rocm5.4.2 # via -r requirements.in triton==2.0.0 ; platform_machine == 'x86_64' and platform_system == 'Linux' # via torch @@ -7452,30 +7452,33 @@ fn universal_disjoint_base_or_local_requirement() -> Result<()> { ----- stdout ----- # This file was autogenerated by uv via the following command: # uv pip compile --cache-dir [CACHE_DIR] requirements.in --universal - cmake==3.28.4 ; python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux' - # via triton + cmake==3.28.4 ; python_full_version < '3.11' or (python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux') + # via + # pytorch-triton-rocm + # triton . # via -r requirements.in filelock==3.13.1 # via + # pytorch-triton-rocm # torch # triton jinja2==3.1.3 # via torch - lit==18.1.2 ; python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux' - # via triton + lit==18.1.2 ; python_full_version < '3.11' or (python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux') + # via + # pytorch-triton-rocm + # triton markupsafe==2.1.5 # via jinja2 mpmath==1.3.0 # via sympy networkx==3.2.1 # via torch + pytorch-triton-rocm==2.0.2 ; python_full_version < '3.11' + # via torch sympy==1.12 # via torch - torch==2.0.0 ; python_full_version < '3.11' - # via - # -r requirements.in - # example torch==2.0.0+cpu ; python_full_version >= '3.13' # via # -r requirements.in @@ -7485,13 +7488,18 @@ fn universal_disjoint_base_or_local_requirement() -> Result<()> { # -r requirements.in # example # triton + torch==2.0.0+rocm5.4.2 ; python_full_version < '3.11' + # via + # -r requirements.in + # example + # pytorch-triton-rocm triton==2.0.0 ; python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux' # via torch typing-extensions==4.10.0 # via torch ----- stderr ----- - Resolved 14 packages in [TIME] + Resolved 15 packages in [TIME] "### ); @@ -7539,6 +7547,7 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> { # via -r requirements.in filelock==3.13.1 # via + # pytorch-triton-rocm # torch # triton fsspec==2024.3.1 ; platform_machine != 'x86_64' @@ -7557,6 +7566,8 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> { # via sympy networkx==3.2.1 # via torch + pytorch-triton-rocm==2.3.0 ; platform_machine != 'x86_64' + # via torch sympy==1.12 # via torch tbb==2021.11.0 ; platform_machine != 'x86_64' and platform_system == 'Windows' @@ -7566,7 +7577,7 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> { # -r requirements.in # example # triton - torch==2.3.0 ; platform_machine != 'x86_64' + torch==2.3.0+rocm6.0 ; platform_machine != 'x86_64' # via -r requirements.in triton==2.0.0 ; platform_machine == 'x86_64' and platform_system == 'Linux' # via torch @@ -7574,7 +7585,7 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> { # via torch ----- stderr ----- - Resolved 17 packages in [TIME] + Resolved 18 packages in [TIME] "### ); @@ -7613,6 +7624,7 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> { # via -r requirements.in filelock==3.13.1 # via + # pytorch-triton-rocm # torch # triton fsspec==2024.3.1 ; platform_machine != 'x86_64' @@ -7631,6 +7643,8 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> { # via sympy networkx==3.2.1 # via torch + pytorch-triton-rocm==2.3.0 ; platform_machine != 'x86_64' + # via torch sympy==1.12 # via torch tbb==2021.11.0 ; platform_machine != 'x86_64' and platform_system == 'Windows' @@ -7640,7 +7654,7 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> { # -r requirements.in # example # triton - torch==2.3.0 ; platform_machine != 'x86_64' + torch==2.3.0+rocm6.0 ; platform_machine != 'x86_64' # via -r requirements.in triton==2.0.0 ; platform_machine == 'x86_64' and platform_system == 'Linux' # via torch @@ -7648,7 +7662,7 @@ fn universal_nested_overlapping_local_requirement() -> Result<()> { # via torch ----- stderr ----- - Resolved 17 packages in [TIME] + Resolved 18 packages in [TIME] "### ); @@ -7698,6 +7712,7 @@ fn universal_nested_disjoint_local_requirement() -> Result<()> { # via -r requirements.in filelock==3.13.1 # via + # pytorch-triton-rocm # torch # triton fsspec==2024.3.1 ; os_name != 'Linux' @@ -7716,36 +7731,7 @@ fn universal_nested_disjoint_local_requirement() -> Result<()> { # via sympy networkx==3.2.1 # via torch - nvidia-cublas-cu12==12.1.3.1 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux' - # via - # nvidia-cudnn-cu12 - # nvidia-cusolver-cu12 - # torch - nvidia-cuda-cupti-cu12==12.1.105 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux' - # via torch - nvidia-cuda-nvrtc-cu12==12.1.105 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux' - # via torch - nvidia-cuda-runtime-cu12==12.1.105 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux' - # via torch - nvidia-cudnn-cu12==8.9.2.26 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux' - # via torch - nvidia-cufft-cu12==11.0.2.54 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux' - # via torch - nvidia-curand-cu12==10.3.2.106 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux' - # via torch - nvidia-cusolver-cu12==11.4.5.107 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux' - # via torch - nvidia-cusparse-cu12==12.1.0.106 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux' - # via - # nvidia-cusolver-cu12 - # torch - nvidia-nccl-cu12==2.20.5 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux' - # via torch - nvidia-nvjitlink-cu12==12.4.99 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux' - # via - # nvidia-cusolver-cu12 - # nvidia-cusparse-cu12 - nvidia-nvtx-cu12==12.1.105 ; os_name != 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux' + pytorch-triton-rocm==2.3.0 ; os_name != 'Linux' # via torch sympy==1.12 # via torch @@ -7760,7 +7746,7 @@ fn universal_nested_disjoint_local_requirement() -> Result<()> { # -r requirements.in # example # triton - torch==2.3.0 ; os_name != 'Linux' + torch==2.3.0+rocm6.0 ; os_name != 'Linux' # via -r requirements.in triton==2.0.0 ; os_name == 'Linux' and platform_machine == 'x86_64' and platform_system == 'Linux' # via torch @@ -7768,7 +7754,7 @@ fn universal_nested_disjoint_local_requirement() -> Result<()> { # via torch ----- stderr ----- - Resolved 30 packages in [TIME] + Resolved 19 packages in [TIME] "### ); @@ -8515,14 +8501,20 @@ fn universal_marker_propagation() -> Result<()> { # via requests charset-normalizer==3.3.2 # via requests + cmake==3.28.4 ; platform_machine == 'x86_64' + # via pytorch-triton-rocm filelock==3.13.1 - # via torch + # via + # pytorch-triton-rocm + # torch fsspec==2024.3.1 ; platform_machine != 'x86_64' # via torch idna==3.6 # via requests jinja2==3.1.3 # via torch + lit==18.1.2 ; platform_machine == 'x86_64' + # via pytorch-triton-rocm markupsafe==2.1.5 # via jinja2 mpmath==1.3.0 @@ -8533,15 +8525,20 @@ fn universal_marker_propagation() -> Result<()> { # via torchvision pillow==10.2.0 # via torchvision + pytorch-triton-rocm==2.0.2 ; platform_machine == 'x86_64' + # via torch + pytorch-triton-rocm==2.2.0 ; platform_machine != 'x86_64' + # via torch requests==2.31.0 # via torchvision sympy==1.12 # via torch - torch==2.0.0 ; platform_machine == 'x86_64' + torch==2.0.0+rocm5.4.2 ; platform_machine == 'x86_64' # via # -r requirements.in + # pytorch-triton-rocm # torchvision - torch==2.2.0 ; platform_machine != 'x86_64' + torch==2.2.0+rocm5.7 ; platform_machine != 'x86_64' # via # -r requirements.in # torchvision @@ -8556,7 +8553,7 @@ fn universal_marker_propagation() -> Result<()> { ----- stderr ----- warning: The requested Python version 3.8 is not available; 3.12.[X] will be used to build dependencies instead. - Resolved 19 packages in [TIME] + Resolved 23 packages in [TIME] "### ); diff --git a/crates/uv/tests/it/pip_install_scenarios.rs b/crates/uv/tests/it/pip_install_scenarios.rs index 57fc635943d7..b5fb8234d239 100644 --- a/crates/uv/tests/it/pip_install_scenarios.rs +++ b/crates/uv/tests/it/pip_install_scenarios.rs @@ -334,10 +334,10 @@ fn dependency_excludes_non_contiguous_range_of_compatible_versions() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-a==1.0.0 depends on package-b==1.0.0 and only the following versions of package-a are available: + ╰─▶ Because only the following versions of package-a are available: package-a==1.0.0 package-a>2.0.0,<=3.0.0 - we can conclude that package-a<2.0.0 depends on package-b==1.0.0. (1) + and package-a==1.0.0 depends on package-b==1.0.0, we can conclude that package-a<2.0.0 depends on package-b==1.0.0. (1) Because only the following versions of package-c are available: package-c==1.0.0 @@ -445,10 +445,10 @@ fn dependency_excludes_range_of_compatible_versions() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-a==1.0.0 depends on package-b==1.0.0 and only the following versions of package-a are available: + ╰─▶ Because only the following versions of package-a are available: package-a==1.0.0 package-a>2.0.0,<=3.0.0 - we can conclude that package-a<2.0.0 depends on package-b==1.0.0. (1) + and package-a==1.0.0 depends on package-b==1.0.0, we can conclude that package-a<2.0.0 depends on package-b==1.0.0. (1) Because only the following versions of package-c are available: package-c==1.0.0 @@ -529,17 +529,17 @@ fn excluded_only_compatible_version() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-a==1.0.0 depends on package-b==1.0.0 and only the following versions of package-a are available: + ╰─▶ Because only the following versions of package-a are available: package-a==1.0.0 package-a==2.0.0 package-a==3.0.0 - we can conclude that package-a<2.0.0 depends on package-b==1.0.0. + and package-a==1.0.0 depends on package-b==1.0.0, we can conclude that package-a<2.0.0 depends on package-b==1.0.0. And because package-a==3.0.0 depends on package-b==3.0.0, we can conclude that all of: package-a<2.0.0 package-a>2.0.0 depend on one of: - package-b<=1.0.0 - package-b>=3.0.0 + package-b==1.0.0 + package-b==3.0.0 And because you require one of: package-a<2.0.0 @@ -1276,8 +1276,10 @@ fn transitive_incompatible_with_transitive() { /// │ └── python3.8 /// ├── root /// │ └── requires a>=1.2.3 +/// │ ├── satisfied by a-1.2.3+bar /// │ └── satisfied by a-1.2.3+foo /// └── a +/// ├── a-1.2.3+bar /// └── a-1.2.3+foo /// ``` #[test] @@ -1354,8 +1356,10 @@ fn local_greater_than() { /// │ └── python3.8 /// ├── root /// │ └── requires a<=1.2.3 +/// │ ├── satisfied by a-1.2.3+bar /// │ └── satisfied by a-1.2.3+foo /// └── a +/// ├── a-1.2.3+bar /// └── a-1.2.3+foo /// ``` #[test] @@ -1369,19 +1373,22 @@ fn local_less_than_or_equal() { uv_snapshot!(filters, command(&context) .arg("local-less-than-or-equal-a<=1.2.3") , @r###" - success: false - exit_code: 1 + success: true + exit_code: 0 ----- stdout ----- ----- stderr ----- - × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.2.3+foo is available and you require package-a<=1.2.3, we can conclude that your requirements are unsatisfiable. + Resolved 1 package in [TIME] + Prepared 1 package in [TIME] + Installed 1 package in [TIME] + + package-a==1.2.3+foo "###); // The version '1.2.3+foo' satisfies the constraint '<=1.2.3'. - assert_not_installed( + assert_installed( &context.venv, "local_less_than_or_equal_a", + "1.2.3+foo", &context.temp_dir, ); } @@ -1500,14 +1507,14 @@ fn local_not_used_with_sdist() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.2.3 + + package-a==1.2.3+foo "###); // The version '1.2.3' with an sdist satisfies the constraint '==1.2.3'. assert_installed( &context.venv, "local_not_used_with_sdist_a", - "1.2.3", + "1.2.3+foo", &context.temp_dir, ); } @@ -1520,8 +1527,10 @@ fn local_not_used_with_sdist() { /// │ └── python3.8 /// ├── root /// │ └── requires a==1.2.3 +/// │ ├── satisfied by a-1.2.3+bar /// │ └── satisfied by a-1.2.3+foo /// └── a +/// ├── a-1.2.3+bar /// └── a-1.2.3+foo /// ``` #[test] @@ -1535,17 +1544,24 @@ fn local_simple() { uv_snapshot!(filters, command(&context) .arg("local-simple-a==1.2.3") , @r###" - success: false - exit_code: 1 + success: true + exit_code: 0 ----- stdout ----- ----- stderr ----- - × No solution found when resolving dependencies: - ╰─▶ Because there is no version of package-a==1.2.3 and you require package-a==1.2.3, we can conclude that your requirements are unsatisfiable. + Resolved 1 package in [TIME] + Prepared 1 package in [TIME] + Installed 1 package in [TIME] + + package-a==1.2.3+foo "###); // The version '1.2.3+foo' satisfies the constraint '==1.2.3'. - assert_not_installed(&context.venv, "local_simple_a", &context.temp_dir); + assert_installed( + &context.venv, + "local_simple_a", + "1.2.3+foo", + &context.temp_dir, + ); } /// A dependency depends on a conflicting local version of a direct dependency, but we can backtrack to a compatible version. @@ -1563,14 +1579,14 @@ fn local_simple() { /// ├── a /// │ ├── a-1.0.0 /// │ │ └── requires b==2.0.0 -/// │ │ ├── satisfied by b-2.0.0+foo -/// │ │ └── satisfied by b-2.0.0+bar +/// │ │ ├── satisfied by b-2.0.0+bar +/// │ │ └── satisfied by b-2.0.0+foo /// │ └── a-2.0.0 /// │ └── requires b==2.0.0+bar /// │ └── satisfied by b-2.0.0+bar /// └── b -/// ├── b-2.0.0+foo -/// └── b-2.0.0+bar +/// ├── b-2.0.0+bar +/// └── b-2.0.0+foo /// ``` #[test] fn local_transitive_backtrack() { @@ -1627,8 +1643,8 @@ fn local_transitive_backtrack() { /// │ └── requires b==2.0.0+bar /// │ └── satisfied by b-2.0.0+bar /// └── b -/// ├── b-2.0.0+foo -/// └── b-2.0.0+bar +/// ├── b-2.0.0+bar +/// └── b-2.0.0+foo /// ``` #[test] fn local_transitive_conflicting() { @@ -1677,9 +1693,11 @@ fn local_transitive_conflicting() { /// │ └── a-1.0.0 /// │ └── requires b==2.0.0 /// │ ├── satisfied by b-2.0.0 +/// │ ├── satisfied by b-2.0.0+bar /// │ └── satisfied by b-2.0.0+foo /// └── b /// ├── b-2.0.0 +/// ├── b-2.0.0+bar /// └── b-2.0.0+foo /// ``` #[test] @@ -1693,20 +1711,29 @@ fn local_transitive_confounding() { uv_snapshot!(filters, command(&context) .arg("local-transitive-confounding-a") , @r###" - success: false - exit_code: 1 + success: true + exit_code: 0 ----- stdout ----- ----- stderr ----- - × No solution found when resolving dependencies: - ╰─▶ Because package-b==2.0.0 has no wheels with a matching Python ABI tag and package-a==1.0.0 depends on package-b==2.0.0, we can conclude that package-a==1.0.0 cannot be used. - And because only package-a==1.0.0 is available and you require package-a, we can conclude that your requirements are unsatisfiable. + Resolved 2 packages in [TIME] + Prepared 2 packages in [TIME] + Installed 2 packages in [TIME] + + package-a==1.0.0 + + package-b==2.0.0+foo "###); // The version '2.0.0+foo' satisfies the constraint '==2.0.0'. - assert_not_installed( + assert_installed( &context.venv, "local_transitive_confounding_a", + "1.0.0", + &context.temp_dir, + ); + assert_installed( + &context.venv, + "local_transitive_confounding_b", + "2.0.0+foo", &context.temp_dir, ); } @@ -1725,8 +1752,10 @@ fn local_transitive_confounding() { /// ├── a /// │ └── a-1.0.0 /// │ └── requires b>=2.0.0 +/// │ ├── satisfied by b-2.0.0+bar /// │ └── satisfied by b-2.0.0+foo /// └── b +/// ├── b-2.0.0+bar /// └── b-2.0.0+foo /// ``` #[test] @@ -1784,6 +1813,7 @@ fn local_transitive_greater_than_or_equal() { /// │ └── requires b>2.0.0 /// │ └── unsatisfied: no matching version /// └── b +/// ├── b-2.0.0+bar /// └── b-2.0.0+foo /// ``` #[test] @@ -1834,8 +1864,10 @@ fn local_transitive_greater_than() { /// ├── a /// │ └── a-1.0.0 /// │ └── requires b<=2.0.0 +/// │ ├── satisfied by b-2.0.0+bar /// │ └── satisfied by b-2.0.0+foo /// └── b +/// ├── b-2.0.0+bar /// └── b-2.0.0+foo /// ``` #[test] @@ -1893,6 +1925,7 @@ fn local_transitive_less_than_or_equal() { /// │ └── requires b<2.0.0 /// │ └── unsatisfied: no matching version /// └── b +/// ├── b-2.0.0+bar /// └── b-2.0.0+foo /// ``` #[test] @@ -1943,9 +1976,11 @@ fn local_transitive_less_than() { /// ├── a /// │ └── a-1.0.0 /// │ └── requires b==2.0.0 -/// │ └── satisfied by b-2.0.0+foo +/// │ ├── satisfied by b-2.0.0+foo +/// │ └── satisfied by b-2.0.0+bar /// └── b -/// └── b-2.0.0+foo +/// ├── b-2.0.0+foo +/// └── b-2.0.0+bar /// ``` #[test] fn local_transitive() { @@ -2011,19 +2046,22 @@ fn local_used_without_sdist() { uv_snapshot!(filters, command(&context) .arg("local-used-without-sdist-a==1.2.3") , @r###" - success: false - exit_code: 1 + success: true + exit_code: 0 ----- stdout ----- ----- stderr ----- - × No solution found when resolving dependencies: - ╰─▶ Because package-a==1.2.3 has no wheels with a matching Python ABI tag and you require package-a==1.2.3, we can conclude that your requirements are unsatisfiable. + Resolved 1 package in [TIME] + Prepared 1 package in [TIME] + Installed 1 package in [TIME] + + package-a==1.2.3+foo "###); // The version '1.2.3+foo' satisfies the constraint '==1.2.3'. - assert_not_installed( + assert_installed( &context.venv, "local_used_without_sdist_a", + "1.2.3+foo", &context.temp_dir, ); } diff --git a/crates/uv/tests/it/snapshots/it__ecosystem__transformers-lock-file.snap b/crates/uv/tests/it/snapshots/it__ecosystem__transformers-lock-file.snap index 60ed4497ca67..c91826241a67 100644 --- a/crates/uv/tests/it/snapshots/it__ecosystem__transformers-lock-file.snap +++ b/crates/uv/tests/it/snapshots/it__ecosystem__transformers-lock-file.snap @@ -1,5 +1,5 @@ --- -source: crates/uv/tests/ecosystem.rs +source: crates/uv/tests/it/ecosystem.rs expression: lock --- version = 1 @@ -774,71 +774,26 @@ wheels = [ name = "datasets" version = "2.14.4" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'", -] dependencies = [ - { name = "aiohttp", marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "dill", marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "fsspec", version = "2024.6.1", source = { registry = "https://pypi.org/simple" }, extra = ["http"], marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "huggingface-hub", marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "multiprocess", marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "numpy", marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "packaging", marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "pandas", marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "pyarrow", marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "pyyaml", marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "requests", marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "tqdm", marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "xxhash", marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, + { name = "aiohttp" }, + { name = "dill" }, + { name = "fsspec", extra = ["http"] }, + { name = "huggingface-hub" }, + { name = "multiprocess" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pyarrow" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "xxhash" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1d/69/8cc725b5d38968fd118e4ce56a483b16e75b7793854c1a392ec4a34eeb31/datasets-2.14.4.tar.gz", hash = "sha256:ef29c2b5841de488cd343cfc26ab979bff77efa4d2285af51f1ad7db5c46a83b", size = 2178719 } wheels = [ { url = "https://files.pythonhosted.org/packages/66/f8/38298237d18d4b6a8ee5dfe390e97bed5adb8e01ec6f9680c0ddf3066728/datasets-2.14.4-py3-none-any.whl", hash = "sha256:29336bd316a7d827ccd4da2236596279b20ca2ac78f64c04c9483da7cbc2459b", size = 519335 }, ] -[[package]] -name = "datasets" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version < '3.10' and platform_machine != 'aarch64' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system != 'Linux') or (python_full_version < '3.10' and platform_machine == 'arm64' and platform_system != 'Darwin')", - "python_full_version == '3.10.*' and platform_system == 'Darwin'", - "python_full_version == '3.10.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.10.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.10.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.11.*' and platform_system == 'Darwin'", - "python_full_version == '3.11.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.11.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.12.*' and platform_system == 'Darwin'", - "python_full_version == '3.12.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.12.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version >= '3.13' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version >= '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')", -] -dependencies = [ - { name = "aiohttp", marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "dill", marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "filelock", marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "fsspec", version = "2024.5.0", source = { registry = "https://pypi.org/simple" }, extra = ["http"], marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "huggingface-hub", marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "multiprocess", marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "numpy", marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "packaging", marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "pandas", marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "pyarrow", marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "pyarrow-hotfix", marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "pyyaml", marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "requests", marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "tqdm", marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "xxhash", marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d5/59/b94bfb5f6225c4c931cd516390b3f006e232a036a48337f72889c6c9ab27/datasets-2.20.0.tar.gz", hash = "sha256:3c4dbcd27e0f642b9d41d20ff2efa721a5e04b32b2ca4009e0fc9139e324553f", size = 2225757 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/2d/963b266bb8f88492d5ab4232d74292af8beb5b6fdae97902df9e284d4c32/datasets-2.20.0-py3-none-any.whl", hash = "sha256:76ac02e3bdfff824492e20678f0b6b1b6d080515957fe834b00c2ba8d6b18e5e", size = 547777 }, -] - [[package]] name = "decorator" version = "5.1.1" @@ -940,8 +895,7 @@ wheels = [ [package.optional-dependencies] epath = [ - { name = "fsspec", version = "2024.5.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "fsspec", version = "2024.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, + { name = "fsspec" }, { name = "importlib-resources" }, { name = "typing-extensions" }, { name = "zipp" }, @@ -955,11 +909,9 @@ name = "evaluate" version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "datasets", version = "2.14.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "datasets", version = "2.20.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, + { name = "datasets" }, { name = "dill" }, - { name = "fsspec", version = "2024.5.0", source = { registry = "https://pypi.org/simple" }, extra = ["http"], marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "fsspec", version = "2024.6.1", source = { registry = "https://pypi.org/simple" }, extra = ["http"], marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, + { name = "fsspec", extra = ["http"] }, { name = "huggingface-hub" }, { name = "multiprocess" }, { name = "numpy" }, @@ -1083,38 +1035,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/61/80/ffe1da13ad9300f87c93af113edd0638c75138c42a0994becfacac078c06/flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3", size = 101735 }, ] -[[package]] -name = "flatbuffers" -version = "2.0.7" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version < '3.10' and platform_machine != 'aarch64' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system != 'Linux') or (python_full_version < '3.10' and platform_machine == 'arm64' and platform_system != 'Darwin')", - "python_full_version == '3.10.*' and platform_system == 'Darwin'", - "python_full_version == '3.10.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.10.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.10.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.11.*' and platform_system == 'Darwin'", - "python_full_version == '3.11.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.11.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.12.*' and platform_system == 'Darwin'", - "python_full_version == '3.12.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.12.*' and platform_system != 'Darwin' and platform_system != 'Linux')", -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/90/0532e737a11e1dc50e9e352c3ccc97338cb75991f83279c2edbc9234e022/flatbuffers-2.0.7.tar.gz", hash = "sha256:0ae7d69c5b82bf41962ca5fde9cc43033bc9501311d975fd5a25e8a7d29c1245", size = 22686 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/0d/b5bfb553a6ac66d6ec2b6d7f1e814a908fba7188356ac94bb36ae3d905c3/flatbuffers-2.0.7-py2.py3-none-any.whl", hash = "sha256:71e135d533be527192819aaab757c5e3d109cb10fbb01e687f6bdb7a61ad39d1", size = 26562 }, -] - [[package]] name = "flatbuffers" version = "24.3.25" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version >= '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')", -] sdist = { url = "https://files.pythonhosted.org/packages/a9/74/2df95ef84b214d2bee0886d572775a6f38793f5ca6d7630c3239c91104ac/flatbuffers-24.3.25.tar.gz", hash = "sha256:de2ec5b203f21441716617f38443e0a8ebf3d25bf0d9c0bb0ce68fa00ad546a4", size = 22139 } wheels = [ { url = "https://files.pythonhosted.org/packages/41/f0/7e988a019bc54b2dbd0ad4182ef2d53488bb02e58694cd79d61369e85900/flatbuffers-24.3.25-py2.py3-none-any.whl", hash = "sha256:8dbdec58f935f3765e4f7f3cf635ac3a77f83568138d6a2311f524ec96364812", size = 26784 }, @@ -1209,43 +1133,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/83/10/466fe96dae1bff622021ee687f68e5524d6392b0a2f80d05001cd3a451ba/frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7", size = 11552 }, ] -[[package]] -name = "fsspec" -version = "2024.5.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version < '3.10' and platform_machine != 'aarch64' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system != 'Linux') or (python_full_version < '3.10' and platform_machine == 'arm64' and platform_system != 'Darwin')", - "python_full_version == '3.10.*' and platform_system == 'Darwin'", - "python_full_version == '3.10.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.10.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.10.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.11.*' and platform_system == 'Darwin'", - "python_full_version == '3.11.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.11.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.12.*' and platform_system == 'Darwin'", - "python_full_version == '3.12.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.12.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version >= '3.13' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version >= '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')", -] -sdist = { url = "https://files.pythonhosted.org/packages/71/28/cbf337fddd6f22686b7c2639b80e006accd904db152fe333fd98f4cd8d1e/fsspec-2024.5.0.tar.gz", hash = "sha256:1d021b0b0f933e3b3029ed808eb400c08ba101ca2de4b3483fbc9ca23fcee94a", size = 400066 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/a3/16e9fe32187e9c8bc7f9b7bcd9728529faa725231a0c96f2f98714ff2fc5/fsspec-2024.5.0-py3-none-any.whl", hash = "sha256:e0fdbc446d67e182f49a70b82cf7889028a63588fde6b222521f10937b2b670c", size = 316106 }, -] - -[package.optional-dependencies] -http = [ - { name = "aiohttp", marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, -] - [[package]] name = "fsspec" version = "2024.6.1" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'", -] sdist = { url = "https://files.pythonhosted.org/packages/90/b6/eba5024a9889fcfff396db543a34bef0ab9d002278f163129f9f01005960/fsspec-2024.6.1.tar.gz", hash = "sha256:fad7d7e209dd4c1208e3bbfda706620e0da5142bebbd9c384afb95b07e798e49", size = 284584 } wheels = [ { url = "https://files.pythonhosted.org/packages/5e/44/73bea497ac69bafde2ee4269292fa3b41f1198f4bb7bbaaabde30ad29d4a/fsspec-2024.6.1-py3-none-any.whl", hash = "sha256:3cb443f8bcd2efb31295a5b9fdb02aee81d8452c80d28f97a6d0959e6cee101e", size = 177561 }, @@ -1253,7 +1144,7 @@ wheels = [ [package.optional-dependencies] http = [ - { name = "aiohttp", marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, + { name = "aiohttp" }, ] [[package]] @@ -1302,38 +1193,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e6/18/b9b2db4d763e6c9a73c758ed5bc1446d30177b5b903e165a884f1d3ca406/fugashi-1.3.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:21d2dac5b085632f1f9a24edf5d7ccaeb3272be672e4aa37a0b219fc7a3b0655", size = 507921 }, ] -[[package]] -name = "gast" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version < '3.10' and platform_machine != 'aarch64' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system != 'Linux') or (python_full_version < '3.10' and platform_machine == 'arm64' and platform_system != 'Darwin')", - "python_full_version == '3.10.*' and platform_system == 'Darwin'", - "python_full_version == '3.10.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.10.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.10.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.11.*' and platform_system == 'Darwin'", - "python_full_version == '3.11.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.11.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.12.*' and platform_system == 'Darwin'", - "python_full_version == '3.12.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.12.*' and platform_system != 'Darwin' and platform_system != 'Linux')", -] -sdist = { url = "https://files.pythonhosted.org/packages/83/4a/07c7e59cef23fb147454663c3271c21da68ba2ab141427c20548ae5a8a4d/gast-0.4.0.tar.gz", hash = "sha256:40feb7b8b8434785585ab224d1568b857edb18297e5a3047f1ba012bc83b42c1", size = 13804 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/48/583c032b79ae5b3daa02225a675aeb673e58d2cb698e78510feceb11958c/gast-0.4.0-py3-none-any.whl", hash = "sha256:b7adcdd5adbebf1adf17378da5ba3f543684dbec47b1cda1f3997e573cd542c4", size = 9824 }, -] - [[package]] name = "gast" version = "0.6.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version >= '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')", -] sdist = { url = "https://files.pythonhosted.org/packages/3c/14/c566f5ca00c115db7725263408ff952b8ae6d6a4e792ef9c84e77d9af7a1/gast-0.6.0.tar.gz", hash = "sha256:88fc5300d32c7ac6ca7b515310862f71e6fdf2c029bbec7c66c0f5dd47b6b1fb", size = 27708 } wheels = [ { url = "https://files.pythonhosted.org/packages/a3/61/8001b38461d751cd1a0c3a6ae84346796a5758123f3ed97a1b121dfbf4f3/gast-0.6.0-py3-none-any.whl", hash = "sha256:52b182313f7330389f72b069ba00f174cfe2a06411099547288839c6cbafbd54", size = 21173 }, @@ -1368,9 +1231,9 @@ name = "google-auth" version = "2.33.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cachetools", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "pyasn1-modules", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "rsa", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, + { name = "cachetools" }, + { name = "pyasn1-modules" }, + { name = "rsa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/28/4d/626b37c6bcc1f211aef23f47c49375072c0cb19148627d98c85e099acbc8/google_auth-2.33.0.tar.gz", hash = "sha256:d6a52342160d7290e334b4d47ba390767e4438ad0d45b7630774533e82655b95", size = 257157 } wheels = [ @@ -1382,8 +1245,8 @@ name = "google-auth-oauthlib" version = "1.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "google-auth", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "requests-oauthlib", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, + { name = "google-auth" }, + { name = "requests-oauthlib" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cc/0f/1772edb8d75ecf6280f1c7f51cbcebe274e8b17878b382f63738fd96cee5/google_auth_oauthlib-1.2.1.tar.gz", hash = "sha256:afd0cad092a2eaa53cd8e8298557d6de1034c6cb4a740500b5357b648af97263", size = 24970 } wheels = [ @@ -1560,8 +1423,7 @@ version = "0.24.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, - { name = "fsspec", version = "2024.5.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "fsspec", version = "2024.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, + { name = "fsspec" }, { name = "packaging" }, { name = "pyyaml" }, { name = "requests" }, @@ -1683,8 +1545,7 @@ version = "0.4.13" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, - { name = "ml-dtypes", version = "0.3.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "ml-dtypes", version = "0.4.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, + { name = "ml-dtypes" }, { name = "numpy" }, { name = "opt-einsum" }, { name = "scipy" }, @@ -1696,8 +1557,7 @@ name = "jaxlib" version = "0.4.13" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ml-dtypes", version = "0.3.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "ml-dtypes", version = "0.4.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, + { name = "ml-dtypes" }, { name = "numpy" }, { name = "scipy" }, ] @@ -1820,37 +1680,10 @@ version = "0.2.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/97/76/6a7479e3546d34ee46fc960c8d0ccf281f26ed18956d17f4ec5dcb1fa377/kenlm-0.2.0.tar.gz", hash = "sha256:c2dc1dc09d3c150e6a1777ef0fd5cac3688e1dc1cc13e41472d0284e5e88ac7f", size = 427425 } -[[package]] -name = "keras" -version = "2.7.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version < '3.10' and platform_machine != 'aarch64' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system != 'Linux') or (python_full_version < '3.10' and platform_machine == 'arm64' and platform_system != 'Darwin')", - "python_full_version == '3.10.*' and platform_system == 'Darwin'", - "python_full_version == '3.10.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.10.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.10.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.11.*' and platform_system == 'Darwin'", - "python_full_version == '3.11.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.11.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.12.*' and platform_system == 'Darwin'", - "python_full_version == '3.12.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.12.*' and platform_system != 'Darwin' and platform_system != 'Linux')", -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/8b/065f94ba03282fa41b2d76942b87a180a9913312c4611ea7d6508fbbc114/keras-2.7.0-py2.py3-none-any.whl", hash = "sha256:0c33ae1f728064ca0d35dfba999e9c316f03623bf5688c82fb83cc74a80ea248", size = 1332171 }, -] - [[package]] name = "keras" version = "2.15.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version >= '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')", -] sdist = { url = "https://files.pythonhosted.org/packages/b5/03/80072f4ee46e3c77e95b06d684fadf90a67759e4e9f1d86a563e0965c71a/keras-2.15.0.tar.gz", hash = "sha256:81871d298c064dc4ac6b58440fdae67bfcf47c8d7ad28580fab401834c06a575", size = 1252015 } wheels = [ { url = "https://files.pythonhosted.org/packages/fc/a7/0d4490de967a67f68a538cc9cdb259bff971c4b5787f7765dc7c8f118f71/keras-2.15.0-py3-none-any.whl", hash = "sha256:2dcc6d2e30cf9c951064b63c1f4c404b966c59caf09e01f3549138ec8ee0dd1f", size = 1710438 }, @@ -1867,27 +1700,13 @@ dependencies = [ { name = "packaging" }, { name = "regex" }, { name = "rich" }, - { name = "tensorflow-text", version = "2.7.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' and platform_system != 'Darwin'" }, - { name = "tensorflow-text", version = "2.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13' and platform_system != 'Darwin'" }, + { name = "tensorflow-text", marker = "platform_system != 'Darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/da/bf/7f34bfd78555f8ce68f51f6583b4a91a279e34dee2013047e338529c3f8a/keras_nlp-0.14.4.tar.gz", hash = "sha256:abd5886efc60d52f0970ac43d3791c87624bfa8f7a7048a66f9dbcb2d1d28771", size = 331838 } wheels = [ { url = "https://files.pythonhosted.org/packages/0e/e9/abbca8edef533acc7deec437742eb9c8c542d03534cab3bee7835bd97f3f/keras_nlp-0.14.4-py3-none-any.whl", hash = "sha256:13664b96c4551f4734074d502a961e8f994ee2ce15c7e94e472b33f1d4bf109c", size = 572242 }, ] -[[package]] -name = "keras-preprocessing" -version = "1.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "six", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5e/f1/b44337faca48874333769a29398fe4666686733c8880aa160b9fd5dfe600/Keras_Preprocessing-1.1.2.tar.gz", hash = "sha256:add82567c50c8bc648c14195bf544a5ce7c1f76761536956c3d2978970179ef3", size = 163598 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/4c/7c3275a01e12ef9368a892926ab932b33bb13d55794881e3573482b378a7/Keras_Preprocessing-1.1.2-py2.py3-none-any.whl", hash = "sha256:7b82029b130ff61cc99b55f3bd27427df4838576838c5b2f65940e4fcec99a7b", size = 42581 }, -] - [[package]] name = "language-tags" version = "1.2.0" @@ -2179,14 +1998,8 @@ wheels = [ name = "ml-dtypes" version = "0.3.2" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version >= '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')", -] dependencies = [ - { name = "numpy", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/39/7d/8d85fcba868758b3a546e6914e727abd8f29ea6918079f816975c9eecd63/ml_dtypes-0.3.2.tar.gz", hash = "sha256:533059bc5f1764fac071ef54598db358c167c51a718f68f5bb55e3dee79d2967", size = 692014 } wheels = [ @@ -2208,46 +2021,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/38/3c/5d058a50340759423b25cb99f930cb3691fc30ebe86d53fdf1bff55c2d71/ml_dtypes-0.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:7ba8e1fafc7fff3e643f453bffa7d082df1678a73286ce8187d3e825e776eb94", size = 127704 }, ] -[[package]] -name = "ml-dtypes" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version < '3.10' and platform_machine != 'aarch64' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system != 'Linux') or (python_full_version < '3.10' and platform_machine == 'arm64' and platform_system != 'Darwin')", - "python_full_version == '3.10.*' and platform_system == 'Darwin'", - "python_full_version == '3.10.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.10.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.10.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.11.*' and platform_system == 'Darwin'", - "python_full_version == '3.11.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.11.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.12.*' and platform_system == 'Darwin'", - "python_full_version == '3.12.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.12.*' and platform_system != 'Darwin' and platform_system != 'Linux')", -] -dependencies = [ - { name = "numpy", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dd/50/17ab8a66d66bdf55ff6dea6fe2df424061cee65c6d772abc871bb563f91b/ml_dtypes-0.4.0.tar.gz", hash = "sha256:eaf197e72f4f7176a19fe3cb8b61846b38c6757607e7bf9cd4b1d84cd3e74deb", size = 692650 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/26/62b6c86ecbe59dbb960be9b134b1d153cc9e0b9c54c8f19b63759403f59c/ml_dtypes-0.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:93afe37f3a879d652ec9ef1fc47612388890660a2657fbb5747256c3b818fd81", size = 390928 }, - { url = "https://files.pythonhosted.org/packages/f4/7d/1e84fa0db717f9fd27d19649f67bd01df1e3f92e041d58b918b39e1898a4/ml_dtypes-0.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bb83fd064db43e67e67d021e547698af4c8d5c6190f2e9b1c53c09f6ff5531d", size = 2184075 }, - { url = "https://files.pythonhosted.org/packages/9d/15/e5af59287e712b26ce776f00911c45c97ac9f4cd82d46500602cc94127ed/ml_dtypes-0.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03e7cda6ef164eed0abb31df69d2c00c3a5ab3e2610b6d4c42183a43329c72a5", size = 2158374 }, - { url = "https://files.pythonhosted.org/packages/ea/31/cc9b87fbbb3f4bf2cb1a4aeb7648bd6d6c558dc3f60e1bd21958f18ddf71/ml_dtypes-0.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:a15d96d090aebb55ee85173d1775ae325a001aab607a76c8ea0b964ccd6b5364", size = 126622 }, - { url = "https://files.pythonhosted.org/packages/42/6b/b2fa3e2386c2b7dde43f12b83c67f6e583039141dfbb58e5c8fd365a5a7d/ml_dtypes-0.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bdf689be7351cc3c95110c910c1b864002f113e682e44508910c849e144f3df1", size = 390927 }, - { url = "https://files.pythonhosted.org/packages/17/9b/6c655eae05ba3edb30cb03e116dfbe722775b26234b16ed0a14007c871ed/ml_dtypes-0.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c83e4d443962d891d51669ff241d5aaad10a8d3d37a81c5532a45419885d591c", size = 2186867 }, - { url = "https://files.pythonhosted.org/packages/84/17/a936d3dfad84d028ba8539a93167274b7dcd7985e0d9df487e94a62f9428/ml_dtypes-0.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1e2f4237b459a63c97c2c9f449baa637d7e4c20addff6a9bac486f22432f3b6", size = 2161456 }, - { url = "https://files.pythonhosted.org/packages/f0/36/290745178e5776f7416818abc1334c1b19afb93c7c87fd1bef3cc99f84ca/ml_dtypes-0.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:75b4faf99d0711b81f393db36d210b4255fd419f6f790bc6c1b461f95ffb7a9e", size = 126751 }, - { url = "https://files.pythonhosted.org/packages/30/9d/890e8c9cb556cec121f784fd84089e1e52939ba6eabf5dc62f6435db28d6/ml_dtypes-0.4.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ee9f91d4c4f9959a7e1051c141dc565f39e54435618152219769e24f5e9a4d06", size = 394380 }, - { url = "https://files.pythonhosted.org/packages/37/d5/3f3085b3a155e1b84c7fc680f05538d31cf01b835aa19cb17edd4994693f/ml_dtypes-0.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad6849a2db386b38e4d54fe13eb3293464561780531a918f8ef4c8169170dd49", size = 2181698 }, - { url = "https://files.pythonhosted.org/packages/8c/ef/5635b60d444db9c949b32d4e1a0a30b3ac237afbd71cce8bd1ccfb145723/ml_dtypes-0.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa32979ebfde3a0d7c947cafbf79edc1ec77ac05ad0780ee86c1d8df70f2259", size = 2158784 }, - { url = "https://files.pythonhosted.org/packages/0f/b7/7cfca987ca898b64c0b7d185e957fbd8dccb64fe5ae9e44f68ec83371df5/ml_dtypes-0.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:3b67ec73a697c88c1122038e0de46520e48dc2ec876d42cf61bc5efe3c0b7675", size = 127498 }, - { url = "https://files.pythonhosted.org/packages/4f/df/455704233905ce4fab09b2a80d81ab61d850d530b7ae68acb7f8ef99d349/ml_dtypes-0.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:41affb38fdfe146e3db226cf2953021184d6f0c4ffab52136613e9601706e368", size = 390888 }, - { url = "https://files.pythonhosted.org/packages/50/96/13d7c3cc82d5ef597279216cf56ff461f8b57e7096a3ef10246a83ca80c0/ml_dtypes-0.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43cf4356a0fe2eeac6d289018d0734e17a403bdf1fd911953c125dd0358edcc0", size = 2181059 }, - { url = "https://files.pythonhosted.org/packages/23/1c/06b52d3dcd75a81f6ca1e56514db6b21fe928f159cc5302428c1fed46562/ml_dtypes-0.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1724ddcdf5edbaf615a62110af47407f1719b8d02e68ccee60683acb5f74da1", size = 2156133 }, - { url = "https://files.pythonhosted.org/packages/9a/83/a26cb6635ffd9bee8af8d5cbb3feb71b782f8729ac1df7034cc017d8e9fd/ml_dtypes-0.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:723af6346447268a3cf0b7356e963d80ecb5732b5279b2aa3fa4b9fc8297c85e", size = 126734 }, -] - [[package]] name = "mpmath" version = "1.3.0" @@ -2727,8 +2500,7 @@ version = "1.18.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coloredlogs" }, - { name = "flatbuffers", version = "2.0.7", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "flatbuffers", version = "24.3.25", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, + { name = "flatbuffers" }, { name = "numpy" }, { name = "packaging" }, { name = "protobuf" }, @@ -3222,15 +2994,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/f6/b75d4816c32f1618ed31a005ee635dd1d91d8164495d94f2ea092f594661/pyarrow-17.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:42bf93249a083aca230ba7e2786c5f673507fa97bbd9725a1e2754715151a204", size = 25148611 }, ] -[[package]] -name = "pyarrow-hotfix" -version = "0.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/0a/71da7b0db0c7078d4cf34ecf0c70ded5ed29decc06612097474e0114f4cc/pyarrow_hotfix-0.6.tar.gz", hash = "sha256:79d3e030f7ff890d408a100ac16d6f00b14d44a502d7897cd9fc3e3a534e9945", size = 9754 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/f4/9ec2222f5f5f8ea04f66f184caafd991a39c8782e31f5b0266f101cb68ca/pyarrow_hotfix-0.6-py3-none-any.whl", hash = "sha256:dcc9ae2d220dff0083be6a9aa8e0cdee5182ad358d4931fce825c545e5c89178", size = 7888 }, -] - [[package]] name = "pyasn1" version = "0.6.0" @@ -3245,7 +3008,7 @@ name = "pyasn1-modules" version = "0.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyasn1", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, + { name = "pyasn1" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f7/00/e7bd1dec10667e3f2be602686537969a7ac92b0a7c5165be2e5875dc3971/pyasn1_modules-0.4.0.tar.gz", hash = "sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6", size = 307859 } wheels = [ @@ -3622,8 +3385,7 @@ wheels = [ [package.optional-dependencies] tune = [ - { name = "fsspec", version = "2024.5.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "fsspec", version = "2024.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, + { name = "fsspec" }, { name = "pandas" }, { name = "pyarrow" }, { name = "requests" }, @@ -3746,8 +3508,8 @@ name = "requests-oauthlib" version = "2.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "oauthlib", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "requests", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, + { name = "oauthlib" }, + { name = "requests" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650 } wheels = [ @@ -3934,7 +3696,7 @@ name = "rsa" version = "4.9" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyasn1", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, + { name = "pyasn1" }, ] sdist = { url = "https://files.pythonhosted.org/packages/aa/65/7d973b89c4d2351d7fb232c2e452547ddfa243e93131e7cfa766da627b52/rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21", size = 29711 } wheels = [ @@ -4512,62 +4274,24 @@ wheels = [ name = "tensorboard" version = "2.15.2" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version >= '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')", -] dependencies = [ - { name = "absl-py", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "google-auth", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "google-auth-oauthlib", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "grpcio", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "markdown", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "numpy", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "protobuf", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "requests", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "setuptools", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "six", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tensorboard-data-server", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "werkzeug", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, + { name = "absl-py" }, + { name = "google-auth" }, + { name = "google-auth-oauthlib" }, + { name = "grpcio" }, + { name = "markdown" }, + { name = "numpy" }, + { name = "protobuf" }, + { name = "requests" }, + { name = "setuptools" }, + { name = "six" }, + { name = "tensorboard-data-server" }, + { name = "werkzeug" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/37/12/f6e9b9dcc310263cbd3948274e286538bd6800fd0c268850788f14a0c6d0/tensorboard-2.15.2-py3-none-any.whl", hash = "sha256:a6f6443728064d962caea6d34653e220e34ef8df764cb06a8212c17e1a8f0622", size = 5539713 }, ] -[[package]] -name = "tensorboard" -version = "2.17.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version < '3.10' and platform_machine != 'aarch64' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system != 'Linux') or (python_full_version < '3.10' and platform_machine == 'arm64' and platform_system != 'Darwin')", - "python_full_version == '3.10.*' and platform_system == 'Darwin'", - "python_full_version == '3.10.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.10.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.10.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.11.*' and platform_system == 'Darwin'", - "python_full_version == '3.11.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.11.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.12.*' and platform_system == 'Darwin'", - "python_full_version == '3.12.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.12.*' and platform_system != 'Darwin' and platform_system != 'Linux')", -] -dependencies = [ - { name = "absl-py", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "grpcio", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "markdown", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "numpy", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "protobuf", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "setuptools", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "six", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorboard-data-server", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "werkzeug", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/32/2e8545fb0592f33e3aca5951e8b01008b76d61b440658cbdc37b4eaebf0b/tensorboard-2.17.0-py3-none-any.whl", hash = "sha256:859a499a9b1fb68a058858964486627100b71fcb21646861c61d31846a6478fb", size = 5502603 }, -] - [[package]] name = "tensorboard-data-server" version = "0.7.2" @@ -4592,85 +4316,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/44/71/f3e7c9b2ab67e28c572ab4e9d5fa3499e0d252650f96d8a3a03e26677f53/tensorboardX-2.6.2.2-py2.py3-none-any.whl", hash = "sha256:160025acbf759ede23fd3526ae9d9bfbfd8b68eb16c38a010ebe326dc6395db8", size = 101700 }, ] -[[package]] -name = "tensorflow" -version = "2.7.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version < '3.10' and platform_machine != 'aarch64' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system != 'Linux') or (python_full_version < '3.10' and platform_machine == 'arm64' and platform_system != 'Darwin')", - "python_full_version == '3.10.*' and platform_system == 'Darwin'", - "python_full_version == '3.10.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.10.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.10.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.11.*' and platform_system == 'Darwin'", - "python_full_version == '3.11.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.11.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.12.*' and platform_system == 'Darwin'", - "python_full_version == '3.12.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.12.*' and platform_system != 'Darwin' and platform_system != 'Linux')", -] -dependencies = [ - { name = "absl-py", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "astunparse", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "flatbuffers", version = "2.0.7", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "gast", version = "0.4.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "google-pasta", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "grpcio", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "h5py", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "keras", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "keras-preprocessing", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "libclang", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "numpy", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "opt-einsum", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "protobuf", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "six", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorboard", version = "2.17.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorflow-estimator", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorflow-io-gcs-filesystem", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "termcolor", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "typing-extensions", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "wheel", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "wrapt", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/87/a0d05183bac49f2c246663ee740d311ee9bd812dd39062a99b1627bf7d8e/tensorflow-2.7.2-cp39-cp39-macosx_10_11_x86_64.whl", hash = "sha256:99a85258505130b1f3ce203f11b3bb3c95442edefec96c0b221ce0d6541a92c8", size = 213094106 }, - { url = "https://files.pythonhosted.org/packages/67/9a/b1e7019fa9bb08098b73bff0c014219ae2b0cdfd1d2411c38199ec63c699/tensorflow-2.7.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:bb2115796e2e7738de1844aead493bc1f1122ff1fae65fc691bb1ce5a0fdd2aa", size = 495573354 }, - { url = "https://files.pythonhosted.org/packages/9b/10/4818933a306e65335d6e7d28ddc9d38d26fb0bf759a968f9134ec3d8fb9a/tensorflow-2.7.2-cp39-cp39-win_amd64.whl", hash = "sha256:3e35d046d0be677239b5e832854bcb291ca79388fa406d9d7547bc3377a7efbb", size = 436716480 }, -] - [[package]] name = "tensorflow" version = "2.15.1" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version >= '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')", -] dependencies = [ - { name = "absl-py", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "astunparse", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "flatbuffers", version = "24.3.25", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "gast", version = "0.6.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "google-pasta", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "grpcio", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "h5py", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "keras", version = "2.15.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "libclang", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "ml-dtypes", version = "0.3.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "numpy", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "opt-einsum", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "packaging", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "protobuf", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "setuptools", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "six", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tensorboard", version = "2.15.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tensorflow-estimator", version = "2.15.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tensorflow-io-gcs-filesystem", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "termcolor", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "typing-extensions", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "wrapt", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, + { name = "absl-py" }, + { name = "astunparse" }, + { name = "flatbuffers" }, + { name = "gast" }, + { name = "google-pasta" }, + { name = "grpcio" }, + { name = "h5py" }, + { name = "keras" }, + { name = "libclang" }, + { name = "ml-dtypes" }, + { name = "numpy" }, + { name = "opt-einsum" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "setuptools" }, + { name = "six" }, + { name = "tensorboard" }, + { name = "tensorflow-estimator" }, + { name = "tensorflow-io-gcs-filesystem" }, + { name = "termcolor" }, + { name = "typing-extensions" }, + { name = "wrapt" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/9c/d3/904d5bf64305218ce19f81ff3b2cb872cf434a558443b4a9a5357924637a/tensorflow-2.15.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:91b51a507007d63a70b65be307d701088d15042a6399c0e2312b53072226e909", size = 236439313 }, @@ -4690,85 +4362,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a5/ef/a9fe22fabd5e11bda4e322daec40d8798a504fd2ee5725a56ba786503452/tensorflow-2.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:aaf3cfa290597ebbdf19d1a78729e3f555e459506cd58f8d7399359ac5e02a05", size = 2095 }, ] -[[package]] -name = "tensorflow-cpu" -version = "2.7.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version < '3.10' and platform_machine != 'aarch64' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system != 'Linux') or (python_full_version < '3.10' and platform_machine == 'arm64' and platform_system != 'Darwin')", - "python_full_version == '3.10.*' and platform_system == 'Darwin'", - "python_full_version == '3.10.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.10.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.10.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.11.*' and platform_system == 'Darwin'", - "python_full_version == '3.11.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.11.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.12.*' and platform_system == 'Darwin'", - "python_full_version == '3.12.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.12.*' and platform_system != 'Darwin' and platform_system != 'Linux')", -] -dependencies = [ - { name = "absl-py", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "astunparse", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "flatbuffers", version = "2.0.7", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "gast", version = "0.4.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "google-pasta", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "grpcio", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "h5py", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "keras", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "keras-preprocessing", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "libclang", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "numpy", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "opt-einsum", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "protobuf", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "six", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorboard", version = "2.17.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorflow-estimator", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorflow-io-gcs-filesystem", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "termcolor", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "typing-extensions", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "wheel", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "wrapt", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/55/b4697a6df0bff4d75d8aed38469d4c7e7c9669237391b58faca3fdc6257e/tensorflow_cpu-2.7.2-cp39-cp39-macosx_10_11_x86_64.whl", hash = "sha256:b6b51b49812fa062267f0547960d5cd54e23bfccd610960d2d37da802702e20a", size = 213094159 }, - { url = "https://files.pythonhosted.org/packages/5b/ca/f2c3cf65a0771ed900111170a3e62c365b7e36d2c0837ddbefa6acfdfe30/tensorflow_cpu-2.7.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:570cf87ddb909b105a8f884dc0b7015dbb17e06c1da37410930f68ab2a7449aa", size = 185774433 }, - { url = "https://files.pythonhosted.org/packages/65/04/5cf7cff5b9a168be856fccb86b11dbde13f54b261e9008e9ef342009dfb5/tensorflow_cpu-2.7.2-cp39-cp39-win_amd64.whl", hash = "sha256:503c4ba43e929dc24b6ec71ac6d717442b8ecffbc1e2ce2e1a21f23cb13ceb90", size = 242914610 }, -] - [[package]] name = "tensorflow-cpu" version = "2.15.1" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version >= '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')", -] dependencies = [ - { name = "absl-py", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "astunparse", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "flatbuffers", version = "24.3.25", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "gast", version = "0.6.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "google-pasta", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "grpcio", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "h5py", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "keras", version = "2.15.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "libclang", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "ml-dtypes", version = "0.3.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "numpy", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "opt-einsum", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "packaging", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "protobuf", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "setuptools", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "six", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tensorboard", version = "2.15.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tensorflow-estimator", version = "2.15.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tensorflow-io-gcs-filesystem", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "termcolor", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "typing-extensions", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "wrapt", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, + { name = "absl-py" }, + { name = "astunparse" }, + { name = "flatbuffers" }, + { name = "gast" }, + { name = "google-pasta" }, + { name = "grpcio" }, + { name = "h5py" }, + { name = "keras" }, + { name = "libclang" }, + { name = "ml-dtypes" }, + { name = "numpy" }, + { name = "opt-einsum" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "setuptools" }, + { name = "six" }, + { name = "tensorboard" }, + { name = "tensorflow-estimator" }, + { name = "tensorflow-io-gcs-filesystem" }, + { name = "termcolor" }, + { name = "typing-extensions" }, + { name = "wrapt" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/1f/cc/dfb0195934918847611d0d1e0a2ad3f1f8a77876a9139b8976ebdd72854c/tensorflow_cpu-2.15.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:f211b011e812f827f5452b1d5f19865645c65df6e2a07d71118480c40887133e", size = 236439366 }, @@ -4782,37 +4402,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bd/cb/1358e60835aad684311cfab10e36375c09a8a627ed22f357ddc9f0556ca3/tensorflow_cpu-2.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:951d78693b61239464bee5ae9c20b6c845d82ae0a2092ee5abebb96b5e2db02e", size = 2133 }, ] -[[package]] -name = "tensorflow-estimator" -version = "2.7.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version < '3.10' and platform_machine != 'aarch64' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system != 'Linux') or (python_full_version < '3.10' and platform_machine == 'arm64' and platform_system != 'Darwin')", - "python_full_version == '3.10.*' and platform_system == 'Darwin'", - "python_full_version == '3.10.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.10.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.10.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.11.*' and platform_system == 'Darwin'", - "python_full_version == '3.11.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.11.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.12.*' and platform_system == 'Darwin'", - "python_full_version == '3.12.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.12.*' and platform_system != 'Darwin' and platform_system != 'Linux')", -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/de/3a71ad41b87f9dd424e3aec3b0794a60f169fa7e9a9a1e3dd44290b86dd6/tensorflow_estimator-2.7.0-py2.py3-none-any.whl", hash = "sha256:325b5a224864379242b7b76c6987ca544239be82579d33e68ec7c2bda57abc9d", size = 463110 }, -] - [[package]] name = "tensorflow-estimator" version = "2.15.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version >= '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')", -] wheels = [ { url = "https://files.pythonhosted.org/packages/b6/c8/2f823c8958d5342eafc6dd3e922f0cc4fcf8c2e0460284cc462dae3b60a0/tensorflow_estimator-2.15.0-py2.py3-none-any.whl", hash = "sha256:aedf21eec7fb2dc91150fc91a1ce12bc44dbb72278a08b58e79ff87c9e28f153", size = 441974 }, ] @@ -4824,8 +4417,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, { name = "protobuf" }, - { name = "tf-keras", version = "2.15.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tf-keras", version = "2.15.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, + { name = "tf-keras" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/e5/50/00dba77925bf2a0a1e45d7bcf8a69a1d2534fb4bb277d9010bd148d2235e/tensorflow_hub-0.16.1-py2.py3-none-any.whl", hash = "sha256:e10c184b3d08daeafada11ffea2dd46781725b6bef01fad1f74d6634ad05311f", size = 30771 }, @@ -4864,47 +4456,14 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3a/54/95b9459cd48d92a0522c8dd59955210e51747a46461bcedb64a9a77ba822/tensorflow_macos-2.15.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:cca3c9ba5b96face05716792cb1bcc70d84c5e0c34bfb7735b39c65d0334b699", size = 2158 }, ] -[[package]] -name = "tensorflow-text" -version = "2.7.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version < '3.10' and platform_machine != 'aarch64' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system != 'Linux') or (python_full_version < '3.10' and platform_machine == 'arm64' and platform_system != 'Darwin')", - "python_full_version == '3.10.*' and platform_system == 'Darwin'", - "python_full_version == '3.10.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.10.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.10.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.11.*' and platform_system == 'Darwin'", - "python_full_version == '3.11.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.11.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.12.*' and platform_system == 'Darwin'", - "python_full_version == '3.12.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.12.*' and platform_system != 'Darwin' and platform_system != 'Linux')", -] -dependencies = [ - { name = "tensorflow", version = "2.7.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorflow-hub", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/cc/3b8925fe8c14e558156d72c734fdb580569d3fcad67ce7dab71f62c048e2/tensorflow_text-2.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:53b5666c9ac019120cbc0f54ed3841b14660a1278cee1b071173253e008e9c07", size = 3979140 }, - { url = "https://files.pythonhosted.org/packages/42/c4/6915c7354cab16563eef4036232bb412a3f3b0d6054b2dddde77c9c9f7ae/tensorflow_text-2.7.3-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:16cdf7ff4202c81dbcbd782d2f77896c586832b270bf6ebed228f5ff6c00486d", size = 4883925 }, - { url = "https://files.pythonhosted.org/packages/72/1c/006bf7894330ae6c750707127afe9bfb5f0c6408cd8149abd7cf6d4cc40b/tensorflow_text-2.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:53fa5c9b83120dad5e54fea49c44548e3d526bd6a618b1d9c2d9acf2a3484394", size = 2494542 }, -] - [[package]] name = "tensorflow-text" version = "2.15.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version >= '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')", -] dependencies = [ - { name = "tensorflow", version = "2.15.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and platform_machine != 'arm64') or (python_full_version >= '3.13' and platform_system != 'Darwin')" }, - { name = "tensorflow-hub", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tensorflow-macos", marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or (python_full_version >= '3.13' and platform_machine == 'arm64' and platform_system == 'Darwin')" }, + { name = "tensorflow", marker = "platform_machine != 'arm64' or platform_system != 'Darwin'" }, + { name = "tensorflow-hub" }, + { name = "tensorflow-macos", marker = "platform_machine == 'arm64' and platform_system == 'Darwin'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/63/0f/d260a5cc7d86d25eb67bb919f957106b76af4a039f064526290d9cf5d93e/tensorflow_text-2.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db09ada839eb92aa23afc6c4e37257e6665d64ae048cfdce6374b5aa33f8f006", size = 6441513 }, @@ -4920,8 +4479,7 @@ name = "tensorstore" version = "0.1.64" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ml-dtypes", version = "0.3.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "ml-dtypes", version = "0.4.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, + { name = "ml-dtypes" }, { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ce/b7/04d19901451da377f03a6e1ae3d9edf0b43af93309f558abf28b2e5aaceb/tensorstore-0.1.64.tar.gz", hash = "sha256:7fa89e90876fb5377efc54f3f37326a6fb83ec9e1326565819a75a4e80949886", size = 6510000 } @@ -4966,40 +4524,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a6/a5/c0b6468d3824fe3fde30dbb5e1f687b291608f9473681bbf7dabbf5a87d7/text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", size = 78154 }, ] -[[package]] -name = "tf-keras" -version = "2.15.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version < '3.10' and platform_machine != 'aarch64' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system != 'Linux') or (python_full_version < '3.10' and platform_machine == 'arm64' and platform_system != 'Darwin')", - "python_full_version == '3.10.*' and platform_system == 'Darwin'", - "python_full_version == '3.10.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.10.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.10.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.11.*' and platform_system == 'Darwin'", - "python_full_version == '3.11.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.11.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.12.*' and platform_system == 'Darwin'", - "python_full_version == '3.12.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.12.*' and platform_system != 'Darwin' and platform_system != 'Linux')", -] -sdist = { url = "https://files.pythonhosted.org/packages/ce/0c/36054828137226dc3a559b525640f296a99ee8eb38beca32b36d29bb303b/tf_keras-2.15.0.tar.gz", hash = "sha256:d7559c2ba40667679fcb2105d3e4b68bbc07ecafbf1037462ce7b3974c3c6798", size = 1250420 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/19/26/ca8a6cca61f2a44f1e7ee71ebdb9c8dfbc4371f418db811cdca4641f6daa/tf_keras-2.15.0-py3-none-any.whl", hash = "sha256:48607ee60a4d1fa7c09d6a44293a803faf3136e7a43f92df089ac094117547d2", size = 1714973 }, -] - [[package]] name = "tf-keras" version = "2.15.1" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version >= '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')", -] dependencies = [ - { name = "tensorflow", version = "2.15.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, + { name = "tensorflow" }, ] sdist = { url = "https://files.pythonhosted.org/packages/03/a3/72e49c210fe545159c98842f110f024195f8efefc2e310f8eac77e3d599e/tf_keras-2.15.1.tar.gz", hash = "sha256:40ab605cecc7759c657cb2bccd9efaacd6fc2369a6c1eba8053890afeac46886", size = 1251021 } wheels = [ @@ -5010,52 +4540,17 @@ wheels = [ name = "tf2onnx" version = "1.8.4" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'", - "python_full_version == '3.10.*' and platform_system == 'Darwin'", - "python_full_version == '3.10.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.10.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.10.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.11.*' and platform_system == 'Darwin'", - "python_full_version == '3.11.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.11.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version == '3.12.*' and platform_system == 'Darwin'", - "python_full_version == '3.12.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.12.*' and platform_system != 'Darwin' and platform_system != 'Linux')", - "python_full_version >= '3.13' and platform_system == 'Darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version >= '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')", -] dependencies = [ - { name = "flatbuffers", version = "2.0.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' and python_full_version < '3.13'" }, - { name = "flatbuffers", version = "24.3.25", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "numpy", marker = "(platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.10'" }, - { name = "onnx", marker = "(platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.10'" }, - { name = "requests", marker = "(platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.10'" }, - { name = "six", marker = "(platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.10'" }, + { name = "flatbuffers" }, + { name = "numpy" }, + { name = "onnx" }, + { name = "requests" }, + { name = "six" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/db/32/33ce509a79c207a39cf04bfa3ec3353da15d1e6553a6ad912f117cc29130/tf2onnx-1.8.4-py3-none-any.whl", hash = "sha256:1ebabb96c914da76e23222b6107a8b248a024bf259d77f027e6690099512d457", size = 345298 }, ] -[[package]] -name = "tf2onnx" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system == 'Linux'", - "(python_full_version < '3.10' and platform_machine != 'aarch64' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system != 'Linux') or (python_full_version < '3.10' and platform_machine == 'arm64' and platform_system != 'Darwin')", -] -dependencies = [ - { name = "flatbuffers", version = "2.0.7", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin')" }, - { name = "numpy", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin')" }, - { name = "onnx", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin')" }, - { name = "requests", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin')" }, - { name = "six", marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin')" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/de/debad55cb4454d1117b58029c44c04cb993c29b8317d2d609178dbce4a72/tf2onnx-1.14.0-py3-none-any.whl", hash = "sha256:a9721a38020260e5ee9d6396295edbbfcaedd22c07cfd6f2cda2698defde9b63", size = 451228 }, -] - [[package]] name = "threadpoolctl" version = "3.5.0" @@ -5194,8 +4689,7 @@ version = "2.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, - { name = "fsspec", version = "2024.5.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, - { name = "fsspec", version = "2024.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, + { name = "fsspec" }, { name = "jinja2" }, { name = "networkx" }, { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, @@ -5334,8 +4828,7 @@ accelerate = [ ] agents = [ { name = "accelerate" }, - { name = "datasets", version = "2.14.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "datasets", version = "2.20.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, + { name = "datasets" }, { name = "diffusers" }, { name = "opencv-python" }, { name = "pillow" }, @@ -5363,12 +4856,9 @@ all = [ { name = "ray", extra = ["tune"] }, { name = "sentencepiece" }, { name = "sigopt" }, - { name = "tensorflow", version = "2.7.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorflow", version = "2.15.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tensorflow-text", version = "2.7.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorflow-text", version = "2.15.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tf2onnx", version = "1.8.4", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.10'" }, - { name = "tf2onnx", version = "1.14.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin')" }, + { name = "tensorflow" }, + { name = "tensorflow-text" }, + { name = "tf2onnx" }, { name = "timm" }, { name = "tokenizers" }, { name = "torch" }, @@ -5392,8 +4882,7 @@ deepspeed-testing = [ { name = "accelerate" }, { name = "beautifulsoup4" }, { name = "cookiecutter" }, - { name = "datasets", version = "2.14.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "datasets", version = "2.20.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, + { name = "datasets" }, { name = "deepspeed" }, { name = "dill" }, { name = "evaluate" }, @@ -5415,8 +4904,7 @@ deepspeed-testing = [ { name = "sacrebleu" }, { name = "sacremoses" }, { name = "sentencepiece" }, - { name = "tensorboard", version = "2.15.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tensorboard", version = "2.17.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, + { name = "tensorboard" }, { name = "timeout-decorator" }, ] dev-dependencies = [ @@ -5425,8 +4913,7 @@ dev-dependencies = [ { name = "beautifulsoup4" }, { name = "codecarbon" }, { name = "cookiecutter" }, - { name = "datasets", version = "2.14.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "datasets", version = "2.20.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, + { name = "datasets" }, { name = "decord" }, { name = "dill" }, { name = "evaluate" }, @@ -5470,14 +4957,10 @@ dev-dependencies = [ { name = "sigopt" }, { name = "sudachidict-core" }, { name = "sudachipy" }, - { name = "tensorboard", version = "2.15.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tensorboard", version = "2.17.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorflow", version = "2.7.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorflow", version = "2.15.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tensorflow-text", version = "2.7.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorflow-text", version = "2.15.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tf2onnx", version = "1.8.4", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.10'" }, - { name = "tf2onnx", version = "1.14.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin')" }, + { name = "tensorboard" }, + { name = "tensorflow" }, + { name = "tensorflow-text" }, + { name = "tf2onnx" }, { name = "timeout-decorator" }, { name = "timm" }, { name = "tokenizers" }, @@ -5510,12 +4993,9 @@ docs = [ { name = "ray", extra = ["tune"] }, { name = "sentencepiece" }, { name = "sigopt" }, - { name = "tensorflow", version = "2.7.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorflow", version = "2.15.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tensorflow-text", version = "2.7.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorflow-text", version = "2.15.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tf2onnx", version = "1.8.4", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.10'" }, - { name = "tf2onnx", version = "1.14.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin')" }, + { name = "tensorflow" }, + { name = "tensorflow-text" }, + { name = "tf2onnx" }, { name = "timm" }, { name = "tokenizers" }, { name = "torch" }, @@ -5561,8 +5041,7 @@ onnx = [ { name = "onnxconverter-common" }, { name = "onnxruntime" }, { name = "onnxruntime-tools" }, - { name = "tf2onnx", version = "1.8.4", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.10'" }, - { name = "tf2onnx", version = "1.14.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin')" }, + { name = "tf2onnx" }, ] onnxruntime = [ { name = "onnxruntime" }, @@ -5572,8 +5051,7 @@ optuna = [ { name = "optuna" }, ] quality = [ - { name = "datasets", version = "2.14.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "datasets", version = "2.20.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, + { name = "datasets" }, { name = "gitpython" }, { name = "hf-doc-builder" }, { name = "isort" }, @@ -5584,8 +5062,7 @@ ray = [ { name = "ray", extra = ["tune"] }, ] retrieval = [ - { name = "datasets", version = "2.14.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'" }, - { name = "datasets", version = "2.20.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'arm64' or platform_system != 'Darwin' or python_full_version >= '3.10'" }, + { name = "datasets" }, { name = "faiss-cpu" }, ] sagemaker = [ @@ -5617,22 +5094,16 @@ speech = [ tf = [ { name = "keras-nlp" }, { name = "onnxconverter-common" }, - { name = "tensorflow", version = "2.7.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorflow", version = "2.15.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tensorflow-text", version = "2.7.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorflow-text", version = "2.15.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tf2onnx", version = "1.8.4", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.10'" }, - { name = "tf2onnx", version = "1.14.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin')" }, + { name = "tensorflow" }, + { name = "tensorflow-text" }, + { name = "tf2onnx" }, ] tf-cpu = [ { name = "keras-nlp" }, { name = "onnxconverter-common" }, - { name = "tensorflow-cpu", version = "2.7.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorflow-cpu", version = "2.15.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tensorflow-text", version = "2.7.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin') or (python_full_version >= '3.10' and python_full_version < '3.13')" }, - { name = "tensorflow-text", version = "2.15.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.13'" }, - { name = "tf2onnx", version = "1.8.4", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'arm64' and platform_system == 'Darwin') or python_full_version >= '3.10'" }, - { name = "tf2onnx", version = "1.14.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_system != 'Darwin')" }, + { name = "tensorflow-cpu" }, + { name = "tensorflow-text" }, + { name = "tf2onnx" }, ] tf-speech = [ { name = "kenlm" }, @@ -5955,7 +5426,7 @@ name = "triton" version = "3.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "filelock" }, + { name = "filelock", marker = "python_full_version < '3.13'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/45/27/14cc3101409b9b4b9241d2ba7deaa93535a217a211c86c4cc7151fb12181/triton-3.0.0-1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e1efef76935b2febc365bfadf74bcb65a6f959a9872e5bddf44cc9e0adce1e1a", size = 209376304 }, diff --git a/crates/uv/tests/it/snapshots/it__ecosystem__transformers-uv-lock-output.snap b/crates/uv/tests/it/snapshots/it__ecosystem__transformers-uv-lock-output.snap index 3f50a5c1cb07..a906748312d4 100644 --- a/crates/uv/tests/it/snapshots/it__ecosystem__transformers-uv-lock-output.snap +++ b/crates/uv/tests/it/snapshots/it__ecosystem__transformers-uv-lock-output.snap @@ -1,5 +1,5 @@ --- -source: crates/uv/tests/ecosystem.rs +source: crates/uv/tests/it/ecosystem.rs expression: snapshot --- success: true @@ -7,4 +7,4 @@ exit_code: 0 ----- stdout ----- ----- stderr ----- -Resolved 296 packages in [TIME] +Resolved 281 packages in [TIME] diff --git a/scripts/scenarios/generate.py b/scripts/scenarios/generate.py index af1db5ff47e4..382729fab9f8 100755 --- a/scripts/scenarios/generate.py +++ b/scripts/scenarios/generate.py @@ -152,18 +152,6 @@ def main(scenarios: list[Path], snapshot_update: bool = True): else: scenario["python_patch"] = False - # We don't yet support local versions that aren't expressed as direct dependencies. - for scenario in data["scenarios"]: - expected = scenario["expected"] - - if scenario["name"] in ( - "local-less-than-or-equal", - "local-simple", - "local-transitive-confounding", - "local-used-without-sdist", - ): - expected["satisfiable"] = False - # Split scenarios into `install`, `compile` and `lock` cases install_scenarios = [] compile_scenarios = [] From 93d7563533dbd1b3ca66f2f0617a9a42bc2081e3 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 5 Nov 2024 22:26:31 -0500 Subject: [PATCH 12/19] Remove all special-casing for local version identifiers (#8818) After https://github.com/astral-sh/uv/pull/8797, we have spec-compliant handling for local version identifiers and can completely remove all the special-casing around it. --- crates/uv-requirements/src/lookahead.rs | 2 +- crates/uv-resolver/src/manifest.rs | 1 - crates/uv-resolver/src/resolver/locals.rs | 201 ------------------ .../uv-resolver/src/resolver/locals/tests.rs | 130 ----------- crates/uv-resolver/src/resolver/mod.rs | 36 +--- docs/pip/compatibility.md | 29 --- 6 files changed, 3 insertions(+), 396 deletions(-) delete mode 100644 crates/uv-resolver/src/resolver/locals.rs delete mode 100644 crates/uv-resolver/src/resolver/locals/tests.rs diff --git a/crates/uv-requirements/src/lookahead.rs b/crates/uv-requirements/src/lookahead.rs index fd1b252b0e7d..1f6c7cc64fe0 100644 --- a/crates/uv-requirements/src/lookahead.rs +++ b/crates/uv-requirements/src/lookahead.rs @@ -18,7 +18,7 @@ use crate::{required_dist, Error}; /// A resolver for resolving lookahead requirements from direct URLs. /// /// The resolver extends certain privileges to "first-party" requirements. For example, first-party -/// requirements are allowed to contain direct URL references, local version specifiers, and more. +/// requirements are allowed to contain direct URL references. /// /// The lookahead resolver resolves requirements recursively for direct URLs, so that the resolver /// can treat them as first-party dependencies for the purpose of analyzing their specifiers. diff --git a/crates/uv-resolver/src/manifest.rs b/crates/uv-resolver/src/manifest.rs index ed5c517f9107..21fbe1285065 100644 --- a/crates/uv-resolver/src/manifest.rs +++ b/crates/uv-resolver/src/manifest.rs @@ -106,7 +106,6 @@ impl Manifest { /// - Determining which requirements should allow yanked versions. /// - Determining which requirements should allow pre-release versions (e.g., `torch>=2.2.0a1`). /// - Determining which requirements should allow direct URLs (e.g., `torch @ https://...`). - /// - Determining which requirements should allow local version specifiers (e.g., `torch==2.2.0+cpu`). pub fn requirements<'a>( &'a self, env: &'a ResolverEnvironment, diff --git a/crates/uv-resolver/src/resolver/locals.rs b/crates/uv-resolver/src/resolver/locals.rs deleted file mode 100644 index de7a2261953f..000000000000 --- a/crates/uv-resolver/src/resolver/locals.rs +++ /dev/null @@ -1,201 +0,0 @@ -use std::str::FromStr; - -use uv_distribution_filename::{SourceDistFilename, WheelFilename}; -use uv_distribution_types::RemoteSource; -use uv_pep440::{Operator, Version, VersionSpecifier, VersionSpecifierBuildError}; -use uv_pep508::PackageName; -use uv_pypi_types::RequirementSource; - -use crate::resolver::ForkMap; -use crate::{DependencyMode, Manifest, ResolverEnvironment}; - -/// A map of package names to their associated, required local versions across all forks. -#[derive(Debug, Default, Clone)] -pub(crate) struct Locals(ForkMap); - -impl Locals { - /// Determine the set of permitted local versions in the [`Manifest`]. - pub(crate) fn from_manifest( - manifest: &Manifest, - env: &ResolverEnvironment, - dependencies: DependencyMode, - ) -> Self { - let mut locals = ForkMap::default(); - - // Add all direct requirements and constraints. There's no need to look for conflicts, - // since conflicts will be enforced by the solver. - for requirement in manifest.requirements(env, dependencies) { - if let Some(local) = from_source(&requirement.source) { - locals.add(&requirement, local); - } - } - - Self(locals) - } - - /// Return a list of local versions that are compatible with a package in the given fork. - pub(crate) fn get( - &self, - package_name: &PackageName, - env: &ResolverEnvironment, - ) -> Vec<&Version> { - self.0.get(package_name, env) - } - - /// Given a specifier that may include the version _without_ a local segment, return a specifier - /// that includes the local segment from the expected version. - pub(crate) fn map( - local: &Version, - specifier: &VersionSpecifier, - ) -> Result { - match specifier.operator() { - Operator::Equal | Operator::EqualStar => { - // Given `foo==1.0.0`, if the local version is `1.0.0+local`, map to - // `foo==1.0.0+local`. - // - // This has the intended effect of allowing `1.0.0+local`. - if is_compatible(local, specifier.version()) { - VersionSpecifier::from_version(Operator::Equal, local.clone()) - } else { - Ok(specifier.clone()) - } - } - Operator::NotEqual | Operator::NotEqualStar => { - // Given `foo!=1.0.0`, if the local version is `1.0.0+local`, map to - // `foo!=1.0.0+local`. - // - // This has the intended effect of disallowing `1.0.0+local`. - // - // There's no risk of accidentally including `foo @ 1.0.0` in the resolution, since - // we _know_ `foo @ 1.0.0+local` is required and would therefore conflict. - if is_compatible(local, specifier.version()) { - VersionSpecifier::from_version(Operator::NotEqual, local.clone()) - } else { - Ok(specifier.clone()) - } - } - Operator::LessThanEqual => { - // Given `foo<=1.0.0`, if the local version is `1.0.0+local`, map to - // `foo==1.0.0+local`. - // - // This has the intended effect of allowing `1.0.0+local`. - // - // Since `foo==1.0.0+local` is already required, we know that to satisfy - // `foo<=1.0.0`, we _must_ satisfy `foo==1.0.0+local`. We _could_ map to - // `foo<=1.0.0+local`, but local versions are _not_ allowed in exclusive ordered - // specifiers, so introducing `foo<=1.0.0+local` would risk breaking invariants. - if is_compatible(local, specifier.version()) { - VersionSpecifier::from_version(Operator::Equal, local.clone()) - } else { - Ok(specifier.clone()) - } - } - Operator::GreaterThan => { - // Given `foo>1.0.0`, `foo @ 1.0.0+local` is already (correctly) disallowed. - Ok(specifier.clone()) - } - Operator::ExactEqual => { - // Given `foo===1.0.0`, `1.0.0+local` is already (correctly) disallowed. - Ok(specifier.clone()) - } - Operator::TildeEqual => { - // Given `foo~=1.0.0`, `foo~=1.0.0+local` is already (correctly) allowed. - Ok(specifier.clone()) - } - Operator::LessThan => { - // Given `foo<1.0.0`, `1.0.0+local` is already (correctly) disallowed. - Ok(specifier.clone()) - } - Operator::GreaterThanEqual => { - // Given `foo>=1.0.0`, `foo @ 1.0.0+local` is already (correctly) allowed. - Ok(specifier.clone()) - } - } - } -} - -/// Returns `true` if a provided version is compatible with the expected local version. -/// -/// The versions are compatible if they are the same including their local segment, or the -/// same except for the local segment, which is empty in the provided version. -/// -/// For example, if the expected version is `1.0.0+local` and the provided version is `1.0.0+other`, -/// this function will return `false`. -/// -/// If the expected version is `1.0.0+local` and the provided version is `1.0.0`, the function will -/// return `true`. -fn is_compatible(expected: &Version, provided: &Version) -> bool { - // The requirements should be the same, ignoring local segments. - if expected.clone().without_local() != provided.clone().without_local() { - return false; - } - - // If the provided version has a local segment, it should be the same as the expected - // version. - if provided.local().is_empty() { - true - } else { - expected.local() == provided.local() - } -} - -/// If a [`VersionSpecifier`] contains an exact equality specifier for a local version, -/// returns the local version. -pub(crate) fn from_source(source: &RequirementSource) -> Option { - match source { - // Extract all local versions from specifiers that require an exact version (e.g., - // `==1.0.0+local`). - RequirementSource::Registry { - specifier: version, .. - } => version - .iter() - .filter(|specifier| { - matches!(specifier.operator(), Operator::Equal | Operator::ExactEqual) - }) - .filter(|specifier| !specifier.version().local().is_empty()) - .map(|specifier| specifier.version().clone()) - // It's technically possible for there to be multiple local segments here. - // For example, `a==1.0+foo,==1.0+bar`. However, in that case resolution - // will fail later. - .next(), - // Exact a local version from a URL, if it includes a fully-qualified filename (e.g., - // `torch-2.2.1%2Bcu118-cp311-cp311-linux_x86_64.whl`). - RequirementSource::Url { url, .. } => url - .filename() - .ok() - .and_then(|filename| { - if let Ok(filename) = WheelFilename::from_str(&filename) { - Some(filename.version) - } else if let Ok(filename) = - SourceDistFilename::parsed_normalized_filename(&filename) - { - Some(filename.version) - } else { - None - } - }) - .filter(uv_pep440::Version::is_local), - RequirementSource::Git { .. } => None, - RequirementSource::Path { - install_path: path, .. - } => path - .file_name() - .and_then(|filename| { - let filename = filename.to_string_lossy(); - if let Ok(filename) = WheelFilename::from_str(&filename) { - Some(filename.version) - } else if let Ok(filename) = - SourceDistFilename::parsed_normalized_filename(&filename) - { - Some(filename.version) - } else { - None - } - }) - .filter(uv_pep440::Version::is_local), - RequirementSource::Directory { .. } => None, - } -} - -#[cfg(test)] -mod tests; diff --git a/crates/uv-resolver/src/resolver/locals/tests.rs b/crates/uv-resolver/src/resolver/locals/tests.rs deleted file mode 100644 index 7ff7bfe3df80..000000000000 --- a/crates/uv-resolver/src/resolver/locals/tests.rs +++ /dev/null @@ -1,130 +0,0 @@ -use std::str::FromStr; - -use anyhow::Result; -use url::Url; - -use uv_pep440::{Operator, Version, VersionSpecifier, VersionSpecifiers}; -use uv_pep508::VerbatimUrl; -use uv_pypi_types::ParsedUrl; -use uv_pypi_types::RequirementSource; - -use super::{from_source, Locals}; - -#[test] -fn extract_locals() -> Result<()> { - // Extract from a source distribution in a URL. - let url = VerbatimUrl::from_url(Url::parse("https://example.com/foo-1.0.0+local.tar.gz")?); - let source = - RequirementSource::from_parsed_url(ParsedUrl::try_from(url.to_url()).unwrap(), url); - let locals: Vec<_> = from_source(&source).into_iter().collect(); - assert_eq!(locals, vec![Version::from_str("1.0.0+local")?]); - - // Extract from a wheel in a URL. - let url = VerbatimUrl::from_url(Url::parse( - "https://example.com/foo-1.0.0+local-cp39-cp39-linux_x86_64.whl", - )?); - let source = - RequirementSource::from_parsed_url(ParsedUrl::try_from(url.to_url()).unwrap(), url); - let locals: Vec<_> = from_source(&source).into_iter().collect(); - assert_eq!(locals, vec![Version::from_str("1.0.0+local")?]); - - // Don't extract anything if the URL is opaque. - let url = VerbatimUrl::from_url(Url::parse("git+https://example.com/foo/bar")?); - let source = - RequirementSource::from_parsed_url(ParsedUrl::try_from(url.to_url()).unwrap(), url); - let locals: Vec<_> = from_source(&source).into_iter().collect(); - assert!(locals.is_empty()); - - // Extract from `==` specifiers. - let version = VersionSpecifiers::from_iter([ - VersionSpecifier::from_version(Operator::GreaterThan, Version::from_str("1.0.0")?)?, - VersionSpecifier::from_version(Operator::Equal, Version::from_str("1.0.0+local")?)?, - ]); - let source = RequirementSource::Registry { - specifier: version, - index: None, - }; - let locals: Vec<_> = from_source(&source).into_iter().collect(); - assert_eq!(locals, vec![Version::from_str("1.0.0+local")?]); - - // Ignore other specifiers. - let version = VersionSpecifiers::from_iter([VersionSpecifier::from_version( - Operator::NotEqual, - Version::from_str("1.0.0+local")?, - )?]); - let source = RequirementSource::Registry { - specifier: version, - index: None, - }; - let locals: Vec<_> = from_source(&source).into_iter().collect(); - assert!(locals.is_empty()); - - Ok(()) -} - -#[test] -fn map_version() -> Result<()> { - // Given `==1.0.0`, if the local version is `1.0.0+local`, map to `==1.0.0+local`. - let local = Version::from_str("1.0.0+local")?; - let specifier = VersionSpecifier::from_version(Operator::Equal, Version::from_str("1.0.0")?)?; - assert_eq!( - Locals::map(&local, &specifier)?, - VersionSpecifier::from_version(Operator::Equal, Version::from_str("1.0.0+local")?)? - ); - - // Given `!=1.0.0`, if the local version is `1.0.0+local`, map to `!=1.0.0+local`. - let local = Version::from_str("1.0.0+local")?; - let specifier = - VersionSpecifier::from_version(Operator::NotEqual, Version::from_str("1.0.0")?)?; - assert_eq!( - Locals::map(&local, &specifier)?, - VersionSpecifier::from_version(Operator::NotEqual, Version::from_str("1.0.0+local")?)? - ); - - // Given `<=1.0.0`, if the local version is `1.0.0+local`, map to `==1.0.0+local`. - let local = Version::from_str("1.0.0+local")?; - let specifier = - VersionSpecifier::from_version(Operator::LessThanEqual, Version::from_str("1.0.0")?)?; - assert_eq!( - Locals::map(&local, &specifier)?, - VersionSpecifier::from_version(Operator::Equal, Version::from_str("1.0.0+local")?)? - ); - - // Given `>1.0.0`, `1.0.0+local` is already (correctly) disallowed. - let local = Version::from_str("1.0.0+local")?; - let specifier = - VersionSpecifier::from_version(Operator::GreaterThan, Version::from_str("1.0.0")?)?; - assert_eq!( - Locals::map(&local, &specifier)?, - VersionSpecifier::from_version(Operator::GreaterThan, Version::from_str("1.0.0")?)? - ); - - // Given `===1.0.0`, `1.0.0+local` is already (correctly) disallowed. - let local = Version::from_str("1.0.0+local")?; - let specifier = - VersionSpecifier::from_version(Operator::ExactEqual, Version::from_str("1.0.0")?)?; - assert_eq!( - Locals::map(&local, &specifier)?, - VersionSpecifier::from_version(Operator::ExactEqual, Version::from_str("1.0.0")?)? - ); - - // Given `==1.0.0+local`, `1.0.0+local` is already (correctly) allowed. - let local = Version::from_str("1.0.0+local")?; - let specifier = - VersionSpecifier::from_version(Operator::Equal, Version::from_str("1.0.0+local")?)?; - assert_eq!( - Locals::map(&local, &specifier)?, - VersionSpecifier::from_version(Operator::Equal, Version::from_str("1.0.0+local")?)? - ); - - // Given `==1.0.0+other`, `1.0.0+local` is already (correctly) disallowed. - let local = Version::from_str("1.0.0+local")?; - let specifier = - VersionSpecifier::from_version(Operator::Equal, Version::from_str("1.0.0+other")?)?; - assert_eq!( - Locals::map(&local, &specifier)?, - VersionSpecifier::from_version(Operator::Equal, Version::from_str("1.0.0+other")?)? - ); - - Ok(()) -} diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs index 46b788b138c6..5c362342ccc6 100644 --- a/crates/uv-resolver/src/resolver/mod.rs +++ b/crates/uv-resolver/src/resolver/mod.rs @@ -24,7 +24,6 @@ use tracing::{debug, info, instrument, trace, warn, Level}; pub use environment::ResolverEnvironment; pub(crate) use fork_map::{ForkMap, ForkSet}; -use locals::Locals; pub(crate) use urls::Urls; use uv_configuration::{Constraints, Overrides}; use uv_distribution::{ArchiveMetadata, DistributionDatabase}; @@ -81,7 +80,6 @@ mod fork_map; mod groups; mod index; mod indexes; -mod locals; mod provider; mod reporter; mod urls; @@ -105,7 +103,6 @@ struct ResolverState { locations: IndexLocations, exclusions: Exclusions, urls: Urls, - locals: Locals, indexes: Indexes, dependency_mode: DependencyMode, hasher: HashStrategy, @@ -211,7 +208,6 @@ impl selector: CandidateSelector::for_resolution(options, &manifest, &env), dependency_mode: options.dependency_mode, urls: Urls::from_manifest(&manifest, &env, git, options.dependency_mode)?, - locals: Locals::from_manifest(&manifest, &env, options.dependency_mode), indexes: Indexes::from_manifest(&manifest, &env, options.dependency_mode), groups: Groups::from_manifest(&manifest, &env), project: manifest.project, @@ -535,7 +531,6 @@ impl ResolverState ResolverState, version: &Version, urls: &Urls, indexes: &Indexes, - locals: &Locals, mut dependencies: Vec, git: &GitResolver, resolution_strategy: &ResolutionStrategy, @@ -2180,8 +2173,8 @@ impl ForkState { let PubGrubDependency { package, version, - specifier, url, + .. } = dependency; let mut has_url = false; @@ -2195,31 +2188,6 @@ impl ForkState { has_url = true; }; - // If the specifier is an exact version and the user requested a local version for this - // fork that's more precise than the specifier, use the local version instead. - if let Some(specifier) = specifier { - let locals = locals.get(name, &self.env); - - // It's possible that there are multiple matching local versions requested with - // different marker expressions. All of these are potentially compatible until we - // narrow to a specific fork. - for local in locals { - let local = specifier - .iter() - .map(|specifier| { - Locals::map(local, specifier) - .map_err(ResolveError::InvalidVersion) - .map(Ranges::from) - }) - .fold_ok(Range::full(), |range, specifier| { - range.intersection(&specifier) - })?; - - // Add the local version. - *version = version.union(&local); - } - } - // If the package is pinned to an exact index, add it to the fork. for index in indexes.get(name, &self.env) { self.fork_indexes.insert(name, index, &self.env)?; diff --git a/docs/pip/compatibility.md b/docs/pip/compatibility.md index dbfdeafb45c1..fecc39cfcc92 100644 --- a/docs/pip/compatibility.md +++ b/docs/pip/compatibility.md @@ -74,35 +74,6 @@ and are instead focused on behavior for a _single_ version specifier. As such, t questions around the correct and intended behavior for pre-releases in the packaging ecosystem more broadly. -## Local version identifiers - -uv does not implement spec-compliant handling of local version identifiers (e.g., `1.2.3+local`). -This is considered a known limitation. Although local version identifiers are rare in published -packages (and, e.g., disallowed on PyPI), they're common in the PyTorch ecosystem, and uv's approach -to local versions _does_ support typical PyTorch workflows to succeed out-of-the-box. - -[PEP 440](https://peps.python.org/pep-0440/#version-specifiers) specifies that the local version -segment should typically be ignored when evaluating version specifiers, with a few exceptions. For -example, `foo==1.2.3` should accept `1.2.3+local`, but `foo==1.2.3+local` should _not_ accept -`1.2.3`. These asymmetries are hard to model in a resolution algorithm. As such, uv treats `1.2.3` -and `1.2.3+local` as entirely separate versions, but respects local versions provided as direct -dependencies throughout the resolution, such that if you provide `foo==1.2.3+local` as a direct -dependency, `1.2.3+local` _will_ be accepted for any transitive dependencies that request -`foo==1.2.3`. - -To take an example from the PyTorch ecosystem, it's common to specify `torch==2.0.0+cu118` and -`torchvision==0.15.1+cu118` as direct dependencies. `torchvision @ 0.15.1+cu118` declares a -dependency on `torch==2.0.0`. In this case, uv would recognize that `torch==2.0.0+cu118` satisfies -the specifier, since it was provided as a direct dependency. - -As compared to pip, the main differences in observed behavior are as follows: - -- In general, local versions must be provided as direct dependencies. Resolution may succeed for - transitive dependencies that request a non-local version, but this is not guaranteed. -- If _only_ local versions exist for a package `foo` at a given version (e.g., `1.2.3+local` exists, - but `1.2.3` does not), `uv pip install foo==1.2.3` will fail, while `pip install foo==1.2.3` will - resolve to an arbitrary local version. - ## Packages that exist on multiple indexes In both uv and `pip`, users can specify multiple package indexes from which to search for the From cbba56b45a9a02d8d874218642f5391b7fc6aa04 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 5 Nov 2024 22:41:57 -0500 Subject: [PATCH 13/19] Incorporate `[max]` local version into `VersionSmall` (#8843) See discussion in https://github.com/astral-sh/uv/pull/8797. --- crates/uv-cache/src/lib.rs | 12 +-- crates/uv-pep440/src/version.rs | 136 +++++++++++++++++++----------- crates/uv/tests/it/cache_clean.rs | 4 +- crates/uv/tests/it/cache_prune.rs | 4 +- 4 files changed, 100 insertions(+), 56 deletions(-) diff --git a/crates/uv-cache/src/lib.rs b/crates/uv-cache/src/lib.rs index d580c4948701..ac9fb3ec0c8f 100644 --- a/crates/uv-cache/src/lib.rs +++ b/crates/uv-cache/src/lib.rs @@ -773,16 +773,18 @@ pub enum CacheBucket { impl CacheBucket { fn to_str(self) -> &'static str { match self { - Self::SourceDistributions => "sdists-v5", - Self::FlatIndex => "flat-index-v1", + // Note that when bumping this, you'll also need to bump it + // in crates/uv/tests/cache_prune.rs. + Self::SourceDistributions => "sdists-v6", + Self::FlatIndex => "flat-index-v2", Self::Git => "git-v0", - Self::Interpreter => "interpreter-v2", + Self::Interpreter => "interpreter-v3", // Note that when bumping this, you'll also need to bump it // in crates/uv/tests/cache_clean.rs. - Self::Simple => "simple-v13", + Self::Simple => "simple-v14", // Note that when bumping this, you'll also need to bump it // in crates/uv/tests/cache_prune.rs. - Self::Wheels => "wheels-v2", + Self::Wheels => "wheels-v3", Self::Archive => "archive-v0", Self::Builds => "builds-v0", Self::Environments => "environments-v1", diff --git a/crates/uv-pep440/src/version.rs b/crates/uv-pep440/src/version.rs index 0e36d2620262..0886af258470 100644 --- a/crates/uv-pep440/src/version.rs +++ b/crates/uv-pep440/src/version.rs @@ -390,7 +390,7 @@ impl Version { #[inline] pub fn local(&self) -> LocalVersionSlice { match *self.inner { - VersionInner::Small { ref small } => small.local(), + VersionInner::Small { ref small } => small.local_slice(), VersionInner::Full { ref full } => full.local.as_slice(), } } @@ -546,6 +546,11 @@ impl Version { match value { LocalVersion::Segments(segments) => self.with_local_segments(segments), LocalVersion::Max => { + if let VersionInner::Small { ref mut small } = Arc::make_mut(&mut self.inner) { + if small.set_local(LocalVersion::Max) { + return self; + } + } self.make_full().local = value; self } @@ -559,12 +564,12 @@ impl Version { #[inline] #[must_use] pub fn without_local(mut self) -> Self { - // A "small" version is already guaranteed not to have a local - // component, so we only need to do anything if we have a "full" - // version. - if let VersionInner::Full { ref mut full } = Arc::make_mut(&mut self.inner) { - full.local.clear(); + if let VersionInner::Small { ref mut small } = Arc::make_mut(&mut self.inner) { + if small.set_local(LocalVersion::empty()) { + return self; + } } + self.make_full().local = LocalVersion::empty(); self } @@ -628,7 +633,7 @@ impl Version { pre: small.pre(), post: small.post(), dev: small.dev(), - local: LocalVersion::Segments(vec![]), + local: small.local(), }; *self = Self { inner: Arc::new(VersionInner::Full { full }), @@ -846,16 +851,16 @@ impl FromStr for Version { /// * Bytes 5, 4 and 3 correspond to the second, third and fourth release /// segments, respectively. /// * Bytes 2, 1 and 0 represent *one* of the following: -/// `min, .devN, aN, bN, rcN, , .postN, max`. +/// `min, .devN, aN, bN, rcN, , local, .postN, max`. /// Its representation is thus: -/// * The most significant 3 bits of Byte 2 corresponds to a value in -/// the range 0-7 inclusive, corresponding to min, dev, pre-a, pre-b, +/// * The most significant 4 bits of Byte 2 corresponds to a value in +/// the range 0-8 inclusive, corresponding to min, dev, pre-a, pre-b, /// pre-rc, no-suffix, post or max releases, respectively. `min` is a /// special version that does not exist in PEP 440, but is used here to /// represent the smallest possible version, preceding any `dev`, `pre`, /// `post` or releases. `max` is an analogous concept for the largest /// possible version, following any `post` or local releases. -/// * The low 5 bits combined with the bits in bytes 1 and 0 correspond +/// * The low 4 bits combined with the bits in bytes 1 and 0 correspond /// to the release number of the suffix, if one exists. If there is no /// suffix, then these bits are always 0. /// @@ -933,8 +938,9 @@ impl VersionSmall { const SUFFIX_PRE_BETA: u64 = 3; const SUFFIX_PRE_RC: u64 = 4; const SUFFIX_NONE: u64 = 5; - const SUFFIX_POST: u64 = 6; - const SUFFIX_MAX: u64 = 7; + const SUFFIX_LOCAL: u64 = 6; + const SUFFIX_POST: u64 = 7; + const SUFFIX_MAX: u64 = 8; // The mask to get only the release segment bits. // @@ -943,16 +949,16 @@ impl VersionSmall { // `Parser::parse_fast`. const SUFFIX_RELEASE_MASK: u64 = 0xFFFF_FFFF_FF00_0000; // The mask to get the version suffix. - const SUFFIX_VERSION_MASK: u64 = 0x001F_FFFF; + const SUFFIX_VERSION_MASK: u64 = 0x000F_FFFF; // The number of bits used by the version suffix. Shifting the `repr` // right by this number of bits should put the suffix kind in the least // significant bits. - const SUFFIX_VERSION_BIT_LEN: u64 = 21; + const SUFFIX_VERSION_BIT_LEN: u64 = 20; // The mask to get only the suffix kind, after shifting right by the // version bits. If you need to add a bit here, then you'll probably need // to take a bit from the suffix version. (Which requires a change to both // the mask and the bit length above.) - const SUFFIX_KIND_MASK: u64 = 0b111; + const SUFFIX_KIND_MASK: u64 = 0b1111; #[inline] fn new() -> Self { @@ -1026,11 +1032,8 @@ impl VersionSmall { #[inline] fn set_post(&mut self, value: Option) -> bool { - if self.min().is_some() - || self.pre().is_some() - || self.dev().is_some() - || self.max().is_some() - { + let suffix_kind = self.suffix_kind(); + if !(suffix_kind == Self::SUFFIX_NONE || suffix_kind == Self::SUFFIX_POST) { return value.is_none(); } match value { @@ -1073,10 +1076,11 @@ impl VersionSmall { #[inline] fn set_pre(&mut self, value: Option) -> bool { - if self.min().is_some() - || self.dev().is_some() - || self.post().is_some() - || self.max().is_some() + let suffix_kind = self.suffix_kind(); + if !(suffix_kind == Self::SUFFIX_NONE + || suffix_kind == Self::SUFFIX_PRE_ALPHA + || suffix_kind == Self::SUFFIX_PRE_BETA + || suffix_kind == Self::SUFFIX_PRE_RC) { return value.is_none(); } @@ -1116,11 +1120,8 @@ impl VersionSmall { #[inline] fn set_dev(&mut self, value: Option) -> bool { - if self.min().is_some() - || self.pre().is_some() - || self.post().is_some() - || self.max().is_some() - { + let suffix_kind = self.suffix_kind(); + if !(suffix_kind == Self::SUFFIX_NONE || suffix_kind == Self::SUFFIX_DEV) { return value.is_none(); } match value { @@ -1149,11 +1150,8 @@ impl VersionSmall { #[inline] fn set_min(&mut self, value: Option) -> bool { - if self.dev().is_some() - || self.pre().is_some() - || self.post().is_some() - || self.max().is_some() - { + let suffix_kind = self.suffix_kind(); + if !(suffix_kind == Self::SUFFIX_NONE || suffix_kind == Self::SUFFIX_MIN) { return value.is_none(); } match value { @@ -1182,11 +1180,8 @@ impl VersionSmall { #[inline] fn set_max(&mut self, value: Option) -> bool { - if self.dev().is_some() - || self.pre().is_some() - || self.post().is_some() - || self.min().is_some() - { + let suffix_kind = self.suffix_kind(); + if !(suffix_kind == Self::SUFFIX_NONE || suffix_kind == Self::SUFFIX_MAX) { return value.is_none(); } match value { @@ -1205,11 +1200,40 @@ impl VersionSmall { } #[inline] - #[allow(clippy::unused_self)] - fn local(&self) -> LocalVersionSlice { - // A "small" version is never used if the version has a non-zero number - // of local segments. - LocalVersionSlice::Segments(&[]) + fn local(&self) -> LocalVersion { + if self.suffix_kind() == Self::SUFFIX_LOCAL { + LocalVersion::Max + } else { + LocalVersion::empty() + } + } + + #[inline] + fn local_slice(&self) -> LocalVersionSlice { + if self.suffix_kind() == Self::SUFFIX_LOCAL { + LocalVersionSlice::Max + } else { + LocalVersionSlice::empty() + } + } + + #[inline] + fn set_local(&mut self, value: LocalVersion) -> bool { + let suffix_kind = self.suffix_kind(); + if !(suffix_kind == Self::SUFFIX_NONE || suffix_kind == Self::SUFFIX_LOCAL) { + return value.is_empty(); + } + match value { + LocalVersion::Max => { + self.set_suffix_kind(Self::SUFFIX_LOCAL); + true + } + LocalVersion::Segments(segments) if segments.is_empty() => { + self.set_suffix_kind(Self::SUFFIX_NONE); + true + } + LocalVersion::Segments(_) => false, + } } #[inline] @@ -1224,7 +1248,7 @@ impl VersionSmall { debug_assert!(kind <= Self::SUFFIX_MAX); self.repr &= !(Self::SUFFIX_KIND_MASK << Self::SUFFIX_VERSION_BIT_LEN); self.repr |= kind << Self::SUFFIX_VERSION_BIT_LEN; - if kind == Self::SUFFIX_NONE { + if kind == Self::SUFFIX_NONE || kind == Self::SUFFIX_LOCAL { self.set_suffix_version(0); } } @@ -1450,6 +1474,19 @@ pub enum LocalVersionSlice<'a> { } impl LocalVersion { + /// Return an empty local version. + pub fn empty() -> Self { + Self::Segments(Vec::new()) + } + + /// Returns `true` if the local version is empty. + pub fn is_empty(&self) -> bool { + match self { + Self::Segments(segments) => segments.is_empty(), + Self::Max => false, + } + } + /// Convert the local version segments into a slice. pub fn as_slice(&self) -> LocalVersionSlice<'_> { match self { @@ -1506,7 +1543,12 @@ impl Ord for LocalVersionSlice<'_> { } impl LocalVersionSlice<'_> { - /// Whether the local version is absent + /// Return an empty local version. + pub const fn empty() -> Self { + Self::Segments(&[]) + } + + /// Returns `true` if the local version is empty. pub fn is_empty(&self) -> bool { matches!(self, Self::Segments(&[])) } diff --git a/crates/uv/tests/it/cache_clean.rs b/crates/uv/tests/it/cache_clean.rs index 5536d20c0884..10a902f6e43a 100644 --- a/crates/uv/tests/it/cache_clean.rs +++ b/crates/uv/tests/it/cache_clean.rs @@ -51,7 +51,7 @@ fn clean_package_pypi() -> Result<()> { // Assert that the `.rkyv` file is created for `iniconfig`. let rkyv = context .cache_dir - .child("simple-v13") + .child("simple-v14") .child("pypi") .child("iniconfig.rkyv"); assert!( @@ -123,7 +123,7 @@ fn clean_package_index() -> Result<()> { // Assert that the `.rkyv` file is created for `iniconfig`. let rkyv = context .cache_dir - .child("simple-v13") + .child("simple-v14") .child("index") .child("e8208120cae3ba69") .child("iniconfig.rkyv"); diff --git a/crates/uv/tests/it/cache_prune.rs b/crates/uv/tests/it/cache_prune.rs index 251a3052d42a..9a26b7b8de8f 100644 --- a/crates/uv/tests/it/cache_prune.rs +++ b/crates/uv/tests/it/cache_prune.rs @@ -140,7 +140,7 @@ fn prune_stale_symlink() -> Result<()> { .success(); // Remove the wheels directory, causing the symlink to become stale. - let wheels = context.cache_dir.child("wheels-v2"); + let wheels = context.cache_dir.child("wheels-v3"); fs_err::remove_dir_all(wheels)?; let filters: Vec<_> = context @@ -328,7 +328,7 @@ fn prune_stale_revision() -> Result<()> { ----- stderr ----- DEBUG uv [VERSION] ([COMMIT] DATE) Pruning cache at: [CACHE_DIR]/ - DEBUG Removing dangling source revision: [CACHE_DIR]/sdists-v5/[ENTRY] + DEBUG Removing dangling source revision: [CACHE_DIR]/sdists-v6/[ENTRY] DEBUG Removing dangling cache archive: [CACHE_DIR]/archive-v0/[ENTRY] Removed 8 files ([SIZE]) "###); From e83bff1734c1e1c2dae25da65ac55da7ca6465e5 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 5 Nov 2024 22:51:37 -0500 Subject: [PATCH 14/19] Use a + for the visual max local (#8844) We don't actually want users to see this, but we should be stripping it anyway. Without this change, we show ranges in the debug logs that look like `>=1.0.0, <1.0.0`, which is more confusing than helpful. (We may want to post-process those debug ranges to remove these.) --- crates/uv-pep440/src/version.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/uv-pep440/src/version.rs b/crates/uv-pep440/src/version.rs index 0886af258470..b66de1a2f186 100644 --- a/crates/uv-pep440/src/version.rs +++ b/crates/uv-pep440/src/version.rs @@ -734,7 +734,7 @@ impl std::fmt::Display for Version { LocalVersionSlice::Segments(_) => { format!("+{}", self.local()) } - LocalVersionSlice::Max => String::new(), + LocalVersionSlice::Max => "+".to_string(), } }; write!(f, "{epoch}{release}{pre}{post}{dev}{local}") From 08948689334860f1a684fc635f56404f5160aab2 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 6 Nov 2024 12:22:34 -0500 Subject: [PATCH 15/19] Add dedicated tests for the max local version sentinel (#8869) --- crates/uv-pep440/src/version/tests.rs | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/crates/uv-pep440/src/version/tests.rs b/crates/uv-pep440/src/version/tests.rs index 2d10a2e668a6..78b38a0a2f78 100644 --- a/crates/uv-pep440/src/version/tests.rs +++ b/crates/uv-pep440/src/version/tests.rs @@ -1149,6 +1149,39 @@ fn ordering() { } } +#[test] +fn local_sentinel_version() { + let sentinel = Version::new([1, 0]).with_local(LocalVersion::Max); + + // Ensure that the "max local version" sentinel is less than the following versions. + let versions = &["1.0.post0", "1.1"]; + + for greater in versions { + let greater = greater.parse::().unwrap(); + assert_eq!( + sentinel.cmp(&greater), + Ordering::Less, + "less: {:?}\ngreater: {:?}", + greater.as_bloated_debug(), + sentinel.as_bloated_debug(), + ); + } + + // Ensure that the "max local version" sentinel is greater than the following versions. + let versions = &["1.0", "1.0.a0", "1.0+local"]; + + for less in versions { + let less = less.parse::().unwrap(); + assert_eq!( + sentinel.cmp(&less), + Ordering::Greater, + "less: {:?}\ngreater: {:?}", + sentinel.as_bloated_debug(), + less.as_bloated_debug() + ); + } +} + #[test] fn min_version() { // Ensure that the `.min` suffix precedes all other suffixes. From 75e4ab0f236cf5d9d077d0332c2568f8df0d07c2 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Thu, 7 Nov 2024 13:24:38 -0600 Subject: [PATCH 16/19] Only report Python pins as updated if its the same file (#8894) --- crates/uv/src/commands/python/pin.rs | 2 ++ crates/uv/tests/it/python_find.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/uv/src/commands/python/pin.rs b/crates/uv/src/commands/python/pin.rs index da43dd56cb0b..4f0f3d135aa9 100644 --- a/crates/uv/src/commands/python/pin.rs +++ b/crates/uv/src/commands/python/pin.rs @@ -132,8 +132,10 @@ pub(crate) async fn pin( new.write().await?; + // If we updated an existing version file to a new version if let Some(existing) = existing .as_ref() + .filter(|existing| existing.path() == new.path()) .and_then(PythonVersionFile::version) .filter(|version| *version != new.version().unwrap()) { diff --git a/crates/uv/tests/it/python_find.rs b/crates/uv/tests/it/python_find.rs index 2f0691f7cb8c..afa14fb17594 100644 --- a/crates/uv/tests/it/python_find.rs +++ b/crates/uv/tests/it/python_find.rs @@ -213,7 +213,7 @@ fn python_find_pin() { success: true exit_code: 0 ----- stdout ----- - Updated `.python-version` from `3.12` -> `3.11` + Pinned `.python-version` to `3.11` ----- stderr ----- "###); From e9dca7fbe71acdb3a7589defabad2086780019cc Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Thu, 7 Nov 2024 13:37:01 -0600 Subject: [PATCH 17/19] Create `.python-version` files in projects even if one exists outside of it (#8896) --- crates/uv/src/commands/project/init.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/uv/src/commands/project/init.rs b/crates/uv/src/commands/project/init.rs index d6eaf5880e4d..b7ccfe0cee3a 100644 --- a/crates/uv/src/commands/project/init.rs +++ b/crates/uv/src/commands/project/init.rs @@ -716,10 +716,12 @@ impl InitProjectKind { } fs_err::write(path.join("pyproject.toml"), pyproject)?; - // Write .python-version if it doesn't exist. + // Write .python-version if it doesn't exist in the target or is empty. if let Some(python_request) = python_request { if PythonVersionFile::discover(path, &VersionFileDiscoveryOptions::default()) .await? + .filter(|file| file.version().is_some()) + .filter(|file| file.path().parent().is_some_and(|parent| parent == path)) .is_none() { PythonVersionFile::new(path.join(".python-version")) From ef38ccf5c52647d0e85ee89f3e6f4ffa359bf91e Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Thu, 7 Nov 2024 14:06:40 -0600 Subject: [PATCH 18/19] Only write `.python-version` files during `uv init` for workspace members if the version differs (#8897) We'll read these from the workspace root anyway! --- crates/uv/src/commands/project/init.rs | 144 ++++++++++++------------- 1 file changed, 67 insertions(+), 77 deletions(-) diff --git a/crates/uv/src/commands/project/init.rs b/crates/uv/src/commands/project/init.rs index b7ccfe0cee3a..3560ad54b117 100644 --- a/crates/uv/src/commands/project/init.rs +++ b/crates/uv/src/commands/project/init.rs @@ -519,19 +519,16 @@ async fn init_project( (requires_python, python_request) }; - project_kind - .init( - name, - path, - &requires_python, - python_request.as_ref(), - vcs, - build_backend, - author_from, - no_readme, - package, - ) - .await?; + project_kind.init( + name, + path, + &requires_python, + vcs, + build_backend, + author_from, + no_readme, + package, + )?; if let Some(workspace) = workspace { if workspace.excludes(path)? { @@ -571,6 +568,40 @@ async fn init_project( workspace.install_path().simplified_display().cyan() )?; } + // Write .python-version if it doesn't exist in the workspace or if the version differs + if let Some(python_request) = python_request { + if PythonVersionFile::discover(path, &VersionFileDiscoveryOptions::default()) + .await? + .filter(|file| { + file.version() + .is_some_and(|version| *version == python_request) + && file.path().parent().is_some_and(|parent| { + parent == workspace.install_path() || parent == path + }) + }) + .is_none() + { + PythonVersionFile::new(path.join(".python-version")) + .with_versions(vec![python_request.clone()]) + .write() + .await?; + } + } + } else { + // Write .python-version if it doesn't exist in the project directory. + if let Some(python_request) = python_request { + if PythonVersionFile::discover(path, &VersionFileDiscoveryOptions::default()) + .await? + .filter(|file| file.version().is_some()) + .filter(|file| file.path().parent().is_some_and(|parent| parent == path)) + .is_none() + { + PythonVersionFile::new(path.join(".python-version")) + .with_versions(vec![python_request.clone()]) + .write() + .await?; + } + } } Ok(()) @@ -610,12 +641,11 @@ impl InitKind { impl InitProjectKind { /// Initialize this project kind at the target path. - async fn init( + fn init( self, name: &PackageName, path: &Path, requires_python: &RequiresPython, - python_request: Option<&PythonRequest>, vcs: Option, build_backend: Option, author_from: Option, @@ -623,44 +653,34 @@ impl InitProjectKind { package: bool, ) -> Result<()> { match self { - InitProjectKind::Application => { - self.init_application( - name, - path, - requires_python, - python_request, - vcs, - build_backend, - author_from, - no_readme, - package, - ) - .await - } - InitProjectKind::Library => { - self.init_library( - name, - path, - requires_python, - python_request, - vcs, - build_backend, - author_from, - no_readme, - package, - ) - .await - } + InitProjectKind::Application => InitProjectKind::init_application( + name, + path, + requires_python, + vcs, + build_backend, + author_from, + no_readme, + package, + ), + InitProjectKind::Library => InitProjectKind::init_library( + name, + path, + requires_python, + vcs, + build_backend, + author_from, + no_readme, + package, + ), } } /// Initialize a Python application at the target path. - async fn init_application( - self, + fn init_application( name: &PackageName, path: &Path, requires_python: &RequiresPython, - python_request: Option<&PythonRequest>, vcs: Option, build_backend: Option, author_from: Option, @@ -716,21 +736,6 @@ impl InitProjectKind { } fs_err::write(path.join("pyproject.toml"), pyproject)?; - // Write .python-version if it doesn't exist in the target or is empty. - if let Some(python_request) = python_request { - if PythonVersionFile::discover(path, &VersionFileDiscoveryOptions::default()) - .await? - .filter(|file| file.version().is_some()) - .filter(|file| file.path().parent().is_some_and(|parent| parent == path)) - .is_none() - { - PythonVersionFile::new(path.join(".python-version")) - .with_versions(vec![python_request.clone()]) - .write() - .await?; - } - } - // Initialize the version control system. init_vcs(path, vcs)?; @@ -738,12 +743,10 @@ impl InitProjectKind { } /// Initialize a library project at the target path. - async fn init_library( - self, + fn init_library( name: &PackageName, path: &Path, requires_python: &RequiresPython, - python_request: Option<&PythonRequest>, vcs: Option, build_backend: Option, author_from: Option, @@ -772,19 +775,6 @@ impl InitProjectKind { // Generate `src` files generate_package_scripts(name, path, build_backend, true)?; - // Write .python-version if it doesn't exist. - if let Some(python_request) = python_request { - if PythonVersionFile::discover(path, &VersionFileDiscoveryOptions::default()) - .await? - .is_none() - { - PythonVersionFile::new(path.join(".python-version")) - .with_versions(vec![python_request.clone()]) - .write() - .await?; - } - } - // Initialize the version control system. init_vcs(path, vcs)?; From df086e8349f2b72d34196656e5b86a5c551e2d3b Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Thu, 7 Nov 2024 14:10:36 -0600 Subject: [PATCH 19/19] Add docs for `.python-version` file discovery (#8898) --- docs/concepts/python-versions.md | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/docs/concepts/python-versions.md b/docs/concepts/python-versions.md index 23cedaceac21..59bfba36c0aa 100644 --- a/docs/concepts/python-versions.md +++ b/docs/concepts/python-versions.md @@ -49,6 +49,17 @@ By default, uv will automatically download Python versions if they cannot be fou This behavior can be [disabled with the `python-downloads` option](#disabling-automatic-python-downloads). +### Python version files + +The `.python-version` file can be used to create a default Python version request. uv searches for a +`.python-version` file in the working directory and each of its parents. Any of the request formats +described above can be used, though use of a version number is recommended for interopability with +other tools. + +A `.python-version` file can be created in the current directory with the `uv python pin` command. + +Discovery of `.python-version` files can be disabled with `--no-config`. + ## Installing a Python version uv bundles a list of downloadable CPython and PyPy distributions for macOS, Linux, and Windows. @@ -91,20 +102,17 @@ $ uv python install pypy All of the [Python version request](#requesting-a-version) formats are supported except those that are used for requesting local interpreters such as a file path. -## Project Python versions - By default `uv python install` will verify that a managed Python version is installed or install the -latest version. +latest version. If a `.python-version` file is present, uv will install the Python version listed in +the file. A project that requires multiple Python versions may define a `.python-versions` file. If +present, uv will install all of the Python versions listed in the file. -However, a project may include a `.python-version` file specifying a default Python version. If -present, uv will install the Python version listed in the file. - -Alternatively, a project that requires multiple Python versions may also define a `.python-versions` -file. If present, uv will install all of the Python versions listed in the file. This file takes -precedence over the `.python-version` file. +## Project Python versions -uv will also respect Python requirements defined in a `pyproject.toml` file during project command -invocations. +uv will respect Python requirements defined in `requires-python` in the `pyproject.toml` file during +project command invocations. The first Python version that is compatible with the requirement will +be used, unless a version is otherwise requested, e.g., via a `.python-version` file or the +`--python` flag. ## Viewing available Python versions