Simplify managed Python flags (#12246)

Currently, for users to specify at the command line whether to use
uv-managed or system Python interpreters, they use the
`--python-preference` parameter, which takes four possible values. This
is more complex than necessary since the normal case is to either say
"only managed" or "not managed". This PR hides the old
`--python-preference` parameter from help and documentation and adds two
new flags: `--managed-python` and `--no-managed-python` to capture the
"only managed" and "not managed" cases.

I have successfully tested this locally but currently cannot add
snapshot tests because of problems with distinguishing managed vs.
system interpreters in CI (and non-determinism when run on different
developers' machines). The `--python-preference` test in
`tool-install.rs` is currently ignored for this reason. See #5144 and
#7473.

---------

Co-authored-by: Zanie Blue <contact@zanie.dev>
This commit is contained in:
John Mumm 2025-03-18 18:13:14 +01:00 committed by GitHub
parent e9d2b6ecea
commit f66ce58a09
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 569 additions and 716 deletions

View file

@ -134,21 +134,44 @@ pub struct TopLevelArgs {
#[command(next_help_heading = "Global options", next_display_order = 1000)]
#[allow(clippy::struct_excessive_bools)]
pub struct GlobalArgs {
/// Whether to prefer uv-managed or system Python installations.
///
/// By default, uv prefers using Python versions it manages. However, it
/// will use system Python installations if a uv-managed Python is not
/// installed. This option allows prioritizing or ignoring system Python
/// installations.
#[arg(
global = true,
long,
help_heading = "Python options",
display_order = 700,
env = EnvVars::UV_PYTHON_PREFERENCE
env = EnvVars::UV_PYTHON_PREFERENCE,
hide = true
)]
pub python_preference: Option<PythonPreference>,
/// Require use of uv-managed Python versions.
///
/// By default, uv prefers using Python versions it manages. However, it
/// will use system Python versions if a uv-managed Python is not
/// installed. This option disables use of system Python versions.
#[arg(
global = true,
long,
help_heading = "Python options",
env = EnvVars::UV_MANAGED_PYTHON,
overrides_with = "no_managed_python",
conflicts_with = "python_preference"
)]
pub managed_python: bool,
/// Disable use of uv-managed Python versions.
///
/// Instead, uv will search for a suitable Python version on the system.
#[arg(
global = true,
long,
help_heading = "Python options",
env = EnvVars::UV_NO_MANAGED_PYTHON,
overrides_with = "managed_python",
conflicts_with = "python_preference"
)]
pub no_managed_python: bool,
#[allow(clippy::doc_markdown)]
/// Allow automatically downloading Python when required. [env: "UV_PYTHON_DOWNLOADS=auto"]
#[arg(global = true, long, help_heading = "Python options", hide = true)]
@ -4419,8 +4442,9 @@ pub enum PythonCommand {
/// By default, installed Python versions and the downloads for latest available patch version
/// of each supported Python major version are shown.
///
/// The displayed versions are filtered by the `--python-preference` option, i.e., if using
/// `only-system`, no managed Python versions will be shown.
/// Use `--managed-python` to view only managed Python versions.
///
/// Use `--no-managed-python` to omit managed Python versions.
///
/// Use `--all-versions` to view all available patch versions.
///

View file

@ -140,10 +140,15 @@ impl EnvVars {
/// exclude distributions published after the specified date.
pub const UV_EXCLUDE_NEWER: &'static str = "UV_EXCLUDE_NEWER";
/// Equivalent to the `--python-preference` command-line argument. Whether uv
/// should prefer system or managed Python versions.
/// Whether uv should prefer system or managed Python versions.
pub const UV_PYTHON_PREFERENCE: &'static str = "UV_PYTHON_PREFERENCE";
/// Require use of uv-managed Python versions.
pub const UV_MANAGED_PYTHON: &'static str = "UV_MANAGED_PYTHON";
/// Disable use of uv-managed Python versions.
pub const UV_NO_MANAGED_PYTHON: &'static str = "UV_NO_MANAGED_PYTHON";
/// Equivalent to the
/// [`python-downloads`](../reference/settings.md#python-downloads) setting and, when disabled, the
/// `--no-python-downloads` option. Whether uv should allow Python downloads.

View file

@ -73,6 +73,7 @@ impl GlobalSettings {
/// Resolve the [`GlobalSettings`] from the CLI and filesystem configuration.
pub(crate) fn resolve(args: &GlobalArgs, workspace: Option<&FilesystemOptions>) -> Self {
let network_settings = NetworkSettings::resolve(args, workspace);
let python_preference = resolve_python_preference(args, workspace);
Self {
required_version: workspace
.and_then(|workspace| workspace.globals.required_version.clone()),
@ -120,10 +121,7 @@ impl GlobalSettings {
.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))
.unwrap_or_default(),
python_preference,
python_downloads: flag(args.allow_python_downloads, args.no_python_downloads)
.map(PythonDownloads::from)
.combine(env(env::UV_PYTHON_DOWNLOADS))
@ -137,6 +135,21 @@ impl GlobalSettings {
}
}
fn resolve_python_preference(
args: &GlobalArgs,
workspace: Option<&FilesystemOptions>,
) -> PythonPreference {
if args.managed_python {
PythonPreference::OnlyManaged
} else if args.no_managed_python {
PythonPreference::OnlySystem
} else {
args.python_preference
.combine(workspace.and_then(|workspace| workspace.globals.python_preference))
.unwrap_or_default()
}
}
/// The resolved network settings to use for any invocation of the CLI.
#[derive(Debug, Clone)]
pub(crate) struct NetworkSettings {

View file

@ -7,7 +7,7 @@ fn help() {
let context = TestContext::new_with_versions(&[]);
// The `uv help` command should show the long help message
uv_snapshot!(context.filters(), context.help(), @r###"
uv_snapshot!(context.filters(), context.help(), @r#"
success: true
exit_code: 0
----- stdout -----
@ -42,11 +42,10 @@ fn help() {
--cache-dir [CACHE_DIR] Path to the cache directory [env: UV_CACHE_DIR=]
Python options:
--python-preference <PYTHON_PREFERENCE>
Whether to prefer uv-managed or system Python installations [env: UV_PYTHON_PREFERENCE=]
[possible values: only-managed, managed, system, only-system]
--no-python-downloads
Disable automatic downloads of Python. [env: "UV_PYTHON_DOWNLOADS=never"]
--managed-python Require use of uv-managed Python versions [env: UV_MANAGED_PYTHON=]
--no-managed-python Disable use of uv-managed Python versions [env: UV_NO_MANAGED_PYTHON=]
--no-python-downloads Disable automatic downloads of Python. [env:
"UV_PYTHON_DOWNLOADS=never"]
Global options:
-q, --quiet
@ -81,14 +80,14 @@ fn help() {
----- stderr -----
"###);
"#);
}
#[test]
fn help_flag() {
let context = TestContext::new_with_versions(&[]);
uv_snapshot!(context.filters(), context.command().arg("--help"), @r###"
uv_snapshot!(context.filters(), context.command().arg("--help"), @r#"
success: true
exit_code: 0
----- stdout -----
@ -122,11 +121,10 @@ fn help_flag() {
--cache-dir [CACHE_DIR] Path to the cache directory [env: UV_CACHE_DIR=]
Python options:
--python-preference <PYTHON_PREFERENCE>
Whether to prefer uv-managed or system Python installations [env: UV_PYTHON_PREFERENCE=]
[possible values: only-managed, managed, system, only-system]
--no-python-downloads
Disable automatic downloads of Python. [env: "UV_PYTHON_DOWNLOADS=never"]
--managed-python Require use of uv-managed Python versions [env: UV_MANAGED_PYTHON=]
--no-managed-python Disable use of uv-managed Python versions [env: UV_NO_MANAGED_PYTHON=]
--no-python-downloads Disable automatic downloads of Python. [env:
"UV_PYTHON_DOWNLOADS=never"]
Global options:
-q, --quiet
@ -160,14 +158,14 @@ fn help_flag() {
Use `uv help` for more details.
----- stderr -----
"###);
"#);
}
#[test]
fn help_short_flag() {
let context = TestContext::new_with_versions(&[]);
uv_snapshot!(context.filters(), context.command().arg("-h"), @r###"
uv_snapshot!(context.filters(), context.command().arg("-h"), @r#"
success: true
exit_code: 0
----- stdout -----
@ -201,11 +199,10 @@ fn help_short_flag() {
--cache-dir [CACHE_DIR] Path to the cache directory [env: UV_CACHE_DIR=]
Python options:
--python-preference <PYTHON_PREFERENCE>
Whether to prefer uv-managed or system Python installations [env: UV_PYTHON_PREFERENCE=]
[possible values: only-managed, managed, system, only-system]
--no-python-downloads
Disable automatic downloads of Python. [env: "UV_PYTHON_DOWNLOADS=never"]
--managed-python Require use of uv-managed Python versions [env: UV_MANAGED_PYTHON=]
--no-managed-python Disable use of uv-managed Python versions [env: UV_NO_MANAGED_PYTHON=]
--no-python-downloads Disable automatic downloads of Python. [env:
"UV_PYTHON_DOWNLOADS=never"]
Global options:
-q, --quiet
@ -239,14 +236,14 @@ fn help_short_flag() {
Use `uv help` for more details.
----- stderr -----
"###);
"#);
}
#[test]
fn help_subcommand() {
let context = TestContext::new_with_versions(&[]);
uv_snapshot!(context.filters(), context.help().arg("python"), @r###"
uv_snapshot!(context.filters(), context.help().arg("python"), @r#"
success: true
exit_code: 0
----- stdout -----
@ -318,22 +315,21 @@ fn help_subcommand() {
[env: UV_CACHE_DIR=]
Python options:
--python-preference <PYTHON_PREFERENCE>
Whether to prefer uv-managed or system Python installations.
--managed-python
Require use of uv-managed Python versions.
By default, uv prefers using Python versions it manages. However, it will use system
Python installations if a uv-managed Python is not installed. This option allows
prioritizing or ignoring system Python installations.
Python versions if a uv-managed Python is not installed. This option disables use of
system Python versions.
[env: UV_PYTHON_PREFERENCE=]
[env: UV_MANAGED_PYTHON=]
Possible values:
- only-managed: Only use managed Python installations; never use system Python
installations
- managed: Prefer managed Python installations over system Python installations
- system: Prefer system Python installations over managed Python installations
- only-system: Only use system Python installations; never use managed Python
installations
--no-managed-python
Disable use of uv-managed Python versions.
Instead, uv will search for a suitable Python version on the system.
[env: UV_NO_MANAGED_PYTHON=]
--no-python-downloads
Disable automatic downloads of Python. [env: "UV_PYTHON_DOWNLOADS=never"]
@ -447,14 +443,14 @@ fn help_subcommand() {
----- stderr -----
"###);
"#);
}
#[test]
fn help_subsubcommand() {
let context = TestContext::new_with_versions(&[]);
uv_snapshot!(context.filters(), context.help().env_remove(EnvVars::UV_PYTHON_INSTALL_DIR).arg("python").arg("install"), @r###"
uv_snapshot!(context.filters(), context.help().env_remove(EnvVars::UV_PYTHON_INSTALL_DIR).arg("python").arg("install"), @r#"
success: true
exit_code: 0
----- stdout -----
@ -567,22 +563,21 @@ fn help_subsubcommand() {
[env: UV_CACHE_DIR=]
Python options:
--python-preference <PYTHON_PREFERENCE>
Whether to prefer uv-managed or system Python installations.
--managed-python
Require use of uv-managed Python versions.
By default, uv prefers using Python versions it manages. However, it will use system
Python installations if a uv-managed Python is not installed. This option allows
prioritizing or ignoring system Python installations.
Python versions if a uv-managed Python is not installed. This option disables use of
system Python versions.
[env: UV_PYTHON_PREFERENCE=]
[env: UV_MANAGED_PYTHON=]
Possible values:
- only-managed: Only use managed Python installations; never use system Python
installations
- managed: Prefer managed Python installations over system Python installations
- system: Prefer system Python installations over managed Python installations
- only-system: Only use system Python installations; never use managed Python
installations
--no-managed-python
Disable use of uv-managed Python versions.
Instead, uv will search for a suitable Python version on the system.
[env: UV_NO_MANAGED_PYTHON=]
--no-python-downloads
Disable automatic downloads of Python. [env: "UV_PYTHON_DOWNLOADS=never"]
@ -694,14 +689,14 @@ fn help_subsubcommand() {
----- stderr -----
"###);
"#);
}
#[test]
fn help_flag_subcommand() {
let context = TestContext::new_with_versions(&[]);
uv_snapshot!(context.filters(), context.command().arg("python").arg("--help"), @r###"
uv_snapshot!(context.filters(), context.command().arg("python").arg("--help"), @r#"
success: true
exit_code: 0
----- stdout -----
@ -723,11 +718,10 @@ fn help_flag_subcommand() {
--cache-dir [CACHE_DIR] Path to the cache directory [env: UV_CACHE_DIR=]
Python options:
--python-preference <PYTHON_PREFERENCE>
Whether to prefer uv-managed or system Python installations [env: UV_PYTHON_PREFERENCE=]
[possible values: only-managed, managed, system, only-system]
--no-python-downloads
Disable automatic downloads of Python. [env: "UV_PYTHON_DOWNLOADS=never"]
--managed-python Require use of uv-managed Python versions [env: UV_MANAGED_PYTHON=]
--no-managed-python Disable use of uv-managed Python versions [env: UV_NO_MANAGED_PYTHON=]
--no-python-downloads Disable automatic downloads of Python. [env:
"UV_PYTHON_DOWNLOADS=never"]
Global options:
-q, --quiet
@ -761,14 +755,14 @@ fn help_flag_subcommand() {
Use `uv help python` for more details.
----- stderr -----
"###);
"#);
}
#[test]
fn help_flag_subsubcommand() {
let context = TestContext::new_with_versions(&[]);
uv_snapshot!(context.filters(), context.command().arg("python").arg("install").arg("--help"), @r###"
uv_snapshot!(context.filters(), context.command().arg("python").arg("install").arg("--help"), @r#"
success: true
exit_code: 0
----- stdout -----
@ -796,11 +790,10 @@ fn help_flag_subsubcommand() {
--cache-dir [CACHE_DIR] Path to the cache directory [env: UV_CACHE_DIR=]
Python options:
--python-preference <PYTHON_PREFERENCE>
Whether to prefer uv-managed or system Python installations [env: UV_PYTHON_PREFERENCE=]
[possible values: only-managed, managed, system, only-system]
--no-python-downloads
Disable automatic downloads of Python. [env: "UV_PYTHON_DOWNLOADS=never"]
--managed-python Require use of uv-managed Python versions [env: UV_MANAGED_PYTHON=]
--no-managed-python Disable use of uv-managed Python versions [env: UV_NO_MANAGED_PYTHON=]
--no-python-downloads Disable automatic downloads of Python. [env:
"UV_PYTHON_DOWNLOADS=never"]
Global options:
-q, --quiet
@ -832,7 +825,7 @@ fn help_flag_subsubcommand() {
Display the uv version
----- stderr -----
"###);
"#);
}
#[test]
@ -918,7 +911,7 @@ fn help_unknown_subsubcommand() {
fn help_with_global_option() {
let context = TestContext::new_with_versions(&[]);
uv_snapshot!(context.filters(), context.help().arg("--no-cache"), @r###"
uv_snapshot!(context.filters(), context.help().arg("--no-cache"), @r#"
success: true
exit_code: 0
----- stdout -----
@ -953,11 +946,10 @@ fn help_with_global_option() {
--cache-dir [CACHE_DIR] Path to the cache directory [env: UV_CACHE_DIR=]
Python options:
--python-preference <PYTHON_PREFERENCE>
Whether to prefer uv-managed or system Python installations [env: UV_PYTHON_PREFERENCE=]
[possible values: only-managed, managed, system, only-system]
--no-python-downloads
Disable automatic downloads of Python. [env: "UV_PYTHON_DOWNLOADS=never"]
--managed-python Require use of uv-managed Python versions [env: UV_MANAGED_PYTHON=]
--no-managed-python Disable use of uv-managed Python versions [env: UV_NO_MANAGED_PYTHON=]
--no-python-downloads Disable automatic downloads of Python. [env:
"UV_PYTHON_DOWNLOADS=never"]
Global options:
-q, --quiet
@ -992,7 +984,7 @@ fn help_with_global_option() {
----- stderr -----
"###);
"#);
}
#[test]
@ -1034,7 +1026,7 @@ fn help_with_no_pager() {
// We can't really test whether the --no-pager option works with a snapshot test.
// It's still nice to have a test for the option to confirm the option exists.
uv_snapshot!(context.filters(), context.help().arg("--no-pager"), @r###"
uv_snapshot!(context.filters(), context.help().arg("--no-pager"), @r#"
success: true
exit_code: 0
----- stdout -----
@ -1069,11 +1061,10 @@ fn help_with_no_pager() {
--cache-dir [CACHE_DIR] Path to the cache directory [env: UV_CACHE_DIR=]
Python options:
--python-preference <PYTHON_PREFERENCE>
Whether to prefer uv-managed or system Python installations [env: UV_PYTHON_PREFERENCE=]
[possible values: only-managed, managed, system, only-system]
--no-python-downloads
Disable automatic downloads of Python. [env: "UV_PYTHON_DOWNLOADS=never"]
--managed-python Require use of uv-managed Python versions [env: UV_MANAGED_PYTHON=]
--no-managed-python Disable use of uv-managed Python versions [env: UV_NO_MANAGED_PYTHON=]
--no-python-downloads Disable automatic downloads of Python. [env:
"UV_PYTHON_DOWNLOADS=never"]
Global options:
-q, --quiet
@ -1108,5 +1099,5 @@ fn help_with_no_pager() {
----- stderr -----
"###);
"#);
}

View file

@ -275,24 +275,43 @@ during `uv python install`.
[persistent configuration file](../configuration/files.md) to change the default behavior, or
the `--no-python-downloads` flag can be passed to any uv command.
## Adjusting Python version preferences
## Requiring or disabling managed Python versions
By default, uv will attempt to use Python versions found on the system and only download managed
interpreters when necessary.
Python versions when necessary. To ignore system Python versions, and only use managed Python
versions, use the `--managed-python` flag:
The [`python-preference`](../reference/settings.md#python-preference) option can be used to adjust
this behavior. By default, it is set to `managed` which prefers managed Python installations over
system Python installations. However, system Python installations are still preferred over
```console
$ uv python list --managed-python
```
Similarly, to ignore managed Python versions and only use system Python versions, use the
`--no-managed-python` flag:
```console
$ uv python list --no-managed-python
```
To change uv's default behavior in a configuration file, use the
[`python-preference` setting](#adjusting-python-version-preferences).
## Adjusting Python version preferences
The [`python-preference`](../reference/settings.md#python-preference) setting determines whether to
prefer using Python installations that are already present on the system, or those that are
downloaded and installed by uv.
By default, the `python-preference` is set to `managed` which prefers managed Python installations
over system Python installations. However, system Python installations are still preferred over
downloading a managed Python version.
The following alternative options are available:
- `only-managed`: Only use managed Python installations; never use system Python installations
- `system`: Prefer system Python installations over managed Python installations
- `only-system`: Only use system Python installations; never use managed Python installations
These options allow disabling uv's managed Python versions entirely or always using them and
ignoring any existing system installations.
- `only-managed`: Only use managed Python installations; never use system Python installations.
Equivalent to `--managed-python`.
- `system`: Prefer system Python installations over managed Python installations.
- `only-system`: Only use system Python installations; never use managed Python installations.
Equivalent to `--no-managed-python`.
!!! note

View file

@ -179,6 +179,10 @@ Add additional context and structure to log messages.
If logging is not enabled, e.g., with `RUST_LOG` or `-v`, this has no effect.
### `UV_MANAGED_PYTHON`
Require use of uv-managed Python versions.
### `UV_NATIVE_TLS`
Equivalent to the `--native-tls` command-line argument. If set to `true`, uv will
@ -229,6 +233,10 @@ Ignore `.env` files when executing `uv run` commands.
Skip writing `uv` installer metadata files (e.g., `INSTALLER`, `REQUESTED`, and `direct_url.json`) to site-packages `.dist-info` directories.
### `UV_NO_MANAGED_PYTHON`
Disable use of uv-managed Python versions.
### `UV_NO_PROGRESS`
Equivalent to the `--no-progress` command-line argument. Disables all progress output. For
@ -343,8 +351,7 @@ Distributions can be read from a local directory by using the `file://` URL sche
### `UV_PYTHON_PREFERENCE`
Equivalent to the `--python-preference` command-line argument. Whether uv
should prefer system or managed Python versions.
Whether uv should prefer system or managed Python versions.
### `UV_REQUEST_TIMEOUT`

View file

@ -116,8 +116,8 @@ command invocation. See the
[Python discovery](../concepts/python-versions.md#discovery-of-python-versions) documentation for
details.
To force uv to use the system Python, provide the `--python-preference only-system` option. See the
[Python version preference](../concepts/python-versions.md#adjusting-python-version-preferences)
To force uv to use the system Python, provide the `--no-managed-python` flag. See the
[Python version preference](../concepts/python-versions.md#requiring-or-disabling-managed-python-versions)
documentation for more details.
## Next steps

File diff suppressed because it is too large Load diff