Skip to content

Commit

Permalink
Add support for upgrading Python in tool environments (#7605)
Browse files Browse the repository at this point in the history
This PR adds support for upgrading the build environment of tools with
the addition of a ```--python``` argument to ```uv upgrade```, as
specified in #7471.

Some things to note:
- I added support for individual packages — I didn't think there was a
good reason for ```--python``` to only apply to all packages
- Upgrading with ```--python``` also upgrades the package itself — I
think this is fair as if a user wants to _strictly_ switch the version
of Python being used to build a tool's environment they can use ```uv
install```. This behavior can of course be modified if others don't
agree!

Closes #6297.

Closes #7471.
  • Loading branch information
tfsingh authored Sep 25, 2024
1 parent f5601e2 commit 106633a
Show file tree
Hide file tree
Showing 10 changed files with 395 additions and 54 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3396,6 +3396,20 @@ pub struct ToolUpgradeArgs {
#[arg(long, conflicts_with("name"))]
pub all: bool,

/// Upgrade a tool, and specify it to use the given Python interpreter
/// to build its environment. Use with `--all` to apply to all tools.
///
/// See `uv help python` for details on Python discovery and supported
/// request formats.
#[arg(
long,
short,
env = "UV_PYTHON",
verbatim_doc_comment,
help_heading = "Python options"
)]
pub python: Option<String>,

#[command(flatten)]
pub installer: ResolverInstallerArgs,

Expand Down
22 changes: 22 additions & 0 deletions crates/uv-python/src/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,26 @@ impl PythonEnvironment {
pub fn into_interpreter(self) -> Interpreter {
Arc::unwrap_or_clone(self.0).interpreter
}

/// Returns `true` if the [`PythonEnvironment`] uses the same underlying [`Interpreter`].
pub fn uses(&self, interpreter: &Interpreter) -> bool {
// TODO(zanieb): Consider using `sysconfig.get_path("stdlib")` instead, which
// should be generally robust.
if cfg!(windows) {
// On Windows, we can't canonicalize an interpreter based on its executable path
// because the executables are separate shim files (not links). Instead, we
// compare the `sys.base_prefix`.
let old_base_prefix = self.interpreter().sys_base_prefix();
let selected_base_prefix = interpreter.sys_base_prefix();
old_base_prefix == selected_base_prefix
} else {
// On Unix, we can see if the canonicalized executable is the same file.
self.interpreter().sys_executable() == interpreter.sys_executable()
|| same_file::is_same_file(
self.interpreter().sys_executable(),
interpreter.sys_executable(),
)
.unwrap_or(false)
}
}
}
1 change: 0 additions & 1 deletion crates/uv/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ owo-colors = { workspace = true }
rayon = { workspace = true }
regex = { workspace = true }
rustc-hash = { workspace = true }
same-file = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tempfile = { workspace = true }
Expand Down
18 changes: 1 addition & 17 deletions crates/uv/src/commands/tool/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,23 +277,7 @@ pub(crate) async fn install(
installed_tools
.get_environment(&from.name, &cache)?
.filter(|environment| {
// TODO(zanieb): Consider using `sysconfig.get_path("stdlib")` instead, which
// should be generally robust.
// TODO(zanieb): Move this into a utility on `Interpreter` since it's non-trivial.
let same_interpreter = if cfg!(windows) {
// On Windows, we can't canonicalize an interpreter based on its executable path
// because the executables are separate shim files (not links). Instead, we
// compare the `sys.base_prefix`.
let old_base_prefix = environment.interpreter().sys_base_prefix();
let selected_base_prefix = interpreter.sys_base_prefix();
old_base_prefix == selected_base_prefix
} else {
// On Unix, we can see if the canonicalized executable is the same file.
environment.interpreter().sys_executable() == interpreter.sys_executable()
|| same_file::is_same_file(environment.interpreter().sys_executable(), interpreter.sys_executable()).unwrap_or(false)
};

if same_interpreter {
if environment.uses(&interpreter) {
trace!(
"Existing interpreter matches the requested interpreter for `{}`: {}",
from.name,
Expand Down
Loading

0 comments on commit 106633a

Please sign in to comment.