Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions crates/uv/src/commands/python/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,16 @@ pub(crate) async fn install(
) -> Result<ExitStatus> {
let start = std::time::Instant::now();

// TODO(zanieb): We should consider marking the Python installation as the default when
// `--default` is used. It's not clear how this overlaps with a global Python pin, but I'd be
// surprised if `uv python find` returned the "newest" Python version rather than the one I just
// installed with the `--default` flag.
if default && !preview.is_enabled() {
warn_user!(
"The `--default` option is experimental and may change without warning. Pass `--preview` to disable this warning"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this is supposed to end in a period since it's multiple sentences, but not sure if we do that consistently for these preview warnings.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we do. I can do a pass for that everywhere separately.

);
}

if upgrade && preview.is_disabled() {
warn_user!(
"`uv python upgrade` is experimental and may change without warning. Pass `--preview` to disable this warning"
Expand Down Expand Up @@ -214,6 +224,8 @@ pub(crate) async fn install(
.map(PythonVersionFile::into_versions)
.unwrap_or_else(|| {
// If no version file is found and no requests were made
// TODO(zanieb): We should consider differentiating between a global Python version
// file here, allowing a request from there to enable `is_default_install`.
is_default_install = true;
vec![if reinstall {
// On bare `--reinstall`, reinstall all Python versions
Expand Down Expand Up @@ -443,10 +455,10 @@ pub(crate) async fn install(
}
}

let bin_dir = if !matches!(bin, Some(false)) {
Some(python_executable_dir()?)
} else {
let bin_dir = if matches!(bin, Some(false)) {
None
} else {
Some(python_executable_dir()?)
};

let installations: Vec<_> = downloaded.iter().chain(satisfied.iter().copied()).collect();
Expand Down Expand Up @@ -727,7 +739,10 @@ fn create_bin_links(
errors: &mut Vec<(InstallErrorKind, PythonInstallationKey, Error)>,
preview: PreviewMode,
) {
let targets = if (default || (is_default_install && !reinstall))
// TODO(zanieb): We want more feedback on the `is_default_install` behavior before stabilizing
// it. In particular, it may be confusing because it does not apply when versions are loaded
// from a `.python-version` file.
let targets = if (default || (is_default_install && preview.is_enabled()))
Comment on lines +742 to +745
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could stabilize the is_default_install behavior (i.e., uv python install gets you python / python3 / python3.13 while uv python install 3.13 only gets you python3.13) separately from the --default flag. The default install behavior is really helpful for beginners, who expect uv python install to result in an available python executable, but I'm not sure how common it is for people to run it without a version request.

&& first_request.matches_installation(installation)
{
vec![
Expand Down
24 changes: 14 additions & 10 deletions crates/uv/tests/it/python_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn python_install() {

----- stderr -----
Installed Python 3.13.5 in [TIME]
+ cpython-3.13.5-[PLATFORM] (python, python3, python3.13)
+ cpython-3.13.5-[PLATFORM] (python3.13)
");

let bin_python = context
Expand Down Expand Up @@ -130,7 +130,7 @@ fn python_install() {
----- stderr -----
Searching for Python versions matching: Python 3.13
Uninstalled Python 3.13.5 in [TIME]
- cpython-3.13.5-[PLATFORM] (python, python3, python3.13)
- cpython-3.13.5-[PLATFORM] (python3.13)
");

// The executable should be removed
Expand Down Expand Up @@ -383,7 +383,7 @@ fn python_install_force() {

----- stderr -----
Installed Python 3.13.5 in [TIME]
+ cpython-3.13.5-[PLATFORM] (python, python3, python3.13)
+ cpython-3.13.5-[PLATFORM] (python3.13)
");

let bin_python = context
Expand All @@ -398,7 +398,7 @@ fn python_install_force() {

----- stderr -----
Installed Python 3.13.5 in [TIME]
+ cpython-3.13.5-[PLATFORM] (python, python3, python3.13)
+ cpython-3.13.5-[PLATFORM] (python3.13)
");

// The executable should still be present in the bin directory
Expand Down Expand Up @@ -647,7 +647,7 @@ fn python_install_preview() {

----- stderr -----
Installed Python 3.13.5 in [TIME]
~ cpython-3.13.5-[PLATFORM] (python3.13)
~ cpython-3.13.5-[PLATFORM] (python, python3, python3.13)
");

// The executable should still be present in the bin directory
Expand Down Expand Up @@ -1185,6 +1185,7 @@ fn python_install_default() {
----- stdout -----

----- stderr -----
warning: The `--default` option is experimental and may change without warning. Pass `--preview` to disable this warning
Installed Python 3.13.5 in [TIME]
+ cpython-3.13.5-[PLATFORM] (python, python3)
");
Expand Down Expand Up @@ -1218,6 +1219,7 @@ fn python_install_default() {
----- stdout -----

----- stderr -----
warning: The `--default` option is experimental and may change without warning. Pass `--preview` to disable this warning
Installed Python 3.13.5 in [TIME]
+ cpython-3.13.5-[PLATFORM] (python, python3, python3.13)
");
Expand Down Expand Up @@ -1296,14 +1298,15 @@ fn python_install_default() {
bin_python_default.assert(predicate::path::missing());

// Install multiple versions, with the `--default` flag
uv_snapshot!(context.filters(), context.python_install().arg("3.12").arg("3.13").arg("--default"), @r###"
uv_snapshot!(context.filters(), context.python_install().arg("3.12").arg("3.13").arg("--default"), @r"
success: false
exit_code: 2
----- stdout -----

----- stderr -----
warning: The `--default` option is experimental and may change without warning. Pass `--preview` to disable this warning
error: The `--default` flag cannot be used with multiple targets
"###);
");

// Install 3.12 as a new default
uv_snapshot!(context.filters(), context.python_install().arg("3.12").arg("--default"), @r"
Expand All @@ -1312,6 +1315,7 @@ fn python_install_default() {
----- stdout -----

----- stderr -----
warning: The `--default` option is experimental and may change without warning. Pass `--preview` to disable this warning
Installed Python 3.12.11 in [TIME]
+ cpython-3.12.11-[PLATFORM] (python, python3, python3.12)
");
Expand Down Expand Up @@ -2075,7 +2079,7 @@ fn python_install_cached() {

----- stderr -----
Installed Python 3.13.5 in [TIME]
+ cpython-3.13.5-[PLATFORM] (python, python3, python3.13)
+ cpython-3.13.5-[PLATFORM] (python3.13)
");

let bin_python = context
Expand Down Expand Up @@ -2105,7 +2109,7 @@ fn python_install_cached() {
----- stderr -----
Searching for Python versions matching: Python 3.13
Uninstalled Python 3.13.5 in [TIME]
- cpython-3.13.5-[PLATFORM] (python, python3, python3.13)
- cpython-3.13.5-[PLATFORM] (python3.13)
");

// The cached archive can be installed offline
Expand All @@ -2119,7 +2123,7 @@ fn python_install_cached() {

----- stderr -----
Installed Python 3.13.5 in [TIME]
+ cpython-3.13.5-[PLATFORM] (python, python3, python3.13)
+ cpython-3.13.5-[PLATFORM] (python3.13)
");

// 3.12 isn't cached, so it can't be installed
Expand Down
Loading