diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 70d5322d9..2efb30724 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -4954,6 +4954,17 @@ pub struct PythonInstallArgs { #[arg(long, overrides_with("bin"), conflicts_with("default"))] pub no_bin: bool, + /// Register the Python installation in the Windows registry. + /// + /// This is the default behavior on Windows. If this flag is provided explicitly, uv will error if the + /// registry entry cannot be created. + #[arg(long, overrides_with("no_registry"), hide = true)] + pub registry: bool, + + /// Do not register the Python installation in the Windows registry. + #[arg(long, overrides_with("registry"))] + pub no_registry: bool, + /// The Python version(s) to install. /// /// If not provided, the requested Python version(s) will be read from the `UV_PYTHON` diff --git a/crates/uv/src/commands/python/install.rs b/crates/uv/src/commands/python/install.rs index b22d6010e..bbab7cbb1 100644 --- a/crates/uv/src/commands/python/install.rs +++ b/crates/uv/src/commands/python/install.rs @@ -152,6 +152,7 @@ pub(crate) async fn install( reinstall: bool, upgrade: bool, bin: Option, + registry: Option, force: bool, python_install_mirror: Option, pypy_install_mirror: Option, @@ -500,7 +501,7 @@ pub(crate) async fn install( ); } - if preview.is_enabled() { + if preview.is_enabled() && !matches!(registry, Some(false)) { #[cfg(windows)] { match uv_python::windows_registry::create_registry_entry(installation) { @@ -670,11 +671,14 @@ pub(crate) async fn install( } if !errors.is_empty() { - // If there are only bin install errors and the user didn't opt-in, we're only going to warn - let fatal = errors - .iter() - .all(|(kind, _, _)| matches!(kind, InstallErrorKind::Bin)) - && bin.is_none(); + // If there are only side-effect install errors and the user didn't opt-in, we're only going + // to warn + let fatal = !errors.iter().all(|(kind, _, _)| match kind { + InstallErrorKind::Bin => bin.is_none(), + #[cfg(windows)] + InstallErrorKind::Registry => registry.is_none(), + InstallErrorKind::DownloadUnpack => false, + }); for (kind, key, err) in errors .into_iter() @@ -691,10 +695,14 @@ pub(crate) async fn install( (level, "install executable for") } #[cfg(windows)] - InstallErrorKind::Registry => ( - "error".red().bold().to_string(), - "install registry entry for", - ), + InstallErrorKind::Registry => { + let level = match registry { + None => "warning".yellow().bold().to_string(), + Some(false) => continue, + Some(true) => "error".red().bold().to_string(), + }; + (level, "install registry entry for") + } }; writeln!( @@ -714,10 +722,8 @@ pub(crate) async fn install( } if fatal { - return Ok(ExitStatus::Success); + return Ok(ExitStatus::Failure); } - - return Ok(ExitStatus::Failure); } Ok(ExitStatus::Success) diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index 3a700b965..e6fea035f 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -1403,6 +1403,7 @@ async fn run(mut cli: Cli) -> Result { args.reinstall, upgrade, args.bin, + args.registry, args.force, args.python_install_mirror, args.pypy_install_mirror, @@ -1432,6 +1433,7 @@ async fn run(mut cli: Cli) -> Result { reinstall, upgrade, args.bin, + args.registry, args.force, args.python_install_mirror, args.pypy_install_mirror, diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index d373250ac..b221f0f5d 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -934,6 +934,7 @@ pub(crate) struct PythonInstallSettings { pub(crate) reinstall: bool, pub(crate) force: bool, pub(crate) bin: Option, + pub(crate) registry: Option, pub(crate) python_install_mirror: Option, pub(crate) pypy_install_mirror: Option, pub(crate) python_downloads_json_url: Option, @@ -964,6 +965,8 @@ impl PythonInstallSettings { reinstall, bin, no_bin, + registry, + no_registry, force, mirror: _, pypy_mirror: _, @@ -977,6 +980,7 @@ impl PythonInstallSettings { reinstall, force, bin: flag(bin, no_bin, "bin"), + registry: flag(registry, no_registry, "registry"), python_install_mirror: python_mirror, pypy_install_mirror: pypy_mirror, python_downloads_json_url, @@ -992,6 +996,7 @@ pub(crate) struct PythonUpgradeSettings { pub(crate) install_dir: Option, pub(crate) targets: Vec, pub(crate) force: bool, + pub(crate) registry: Option, pub(crate) python_install_mirror: Option, pub(crate) pypy_install_mirror: Option, pub(crate) python_downloads_json_url: Option, @@ -1019,6 +1024,7 @@ impl PythonUpgradeSettings { let force = false; let default = false; let bin = None; + let registry = None; let PythonUpgradeArgs { install_dir, @@ -1032,6 +1038,7 @@ impl PythonUpgradeSettings { install_dir, targets, force, + registry, python_install_mirror: python_mirror, pypy_install_mirror: pypy_mirror, python_downloads_json_url, diff --git a/crates/uv/tests/it/help.rs b/crates/uv/tests/it/help.rs index a6230108c..a557b0eff 100644 --- a/crates/uv/tests/it/help.rs +++ b/crates/uv/tests/it/help.rs @@ -507,6 +507,9 @@ fn help_subsubcommand() { --no-bin Do not install a Python executable into the `bin` directory + --no-registry + Do not register the Python installation in the Windows registry + --mirror Set the URL to use as the source for downloading Python installations. @@ -795,6 +798,8 @@ fn help_flag_subsubcommand() { The directory to store the Python installation in [env: UV_PYTHON_INSTALL_DIR=] --no-bin Do not install a Python executable into the `bin` directory + --no-registry + Do not register the Python installation in the Windows registry --mirror Set the URL to use as the source for downloading Python installations [env: UV_PYTHON_INSTALL_MIRROR=] diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 93d928518..f6bc028df 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -2804,6 +2804,7 @@ uv python install [OPTIONS] [TARGETS]...

May also be set with the UV_NO_MANAGED_PYTHON environment variable.

--no-progress

Hide all progress outputs.

For example, spinners or progress bars.

May also be set with the UV_NO_PROGRESS environment variable.

--no-python-downloads

Disable automatic downloads of Python.

+
--no-registry

Do not register the Python installation in the Windows registry

--offline

Disable network access.

When disabled, uv will only use locally cached data and locally available files.

May also be set with the UV_OFFLINE environment variable.

--project project

Run the command within the given project directory.