mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 13:25:00 +00:00
Deduplicate when install or uninstall python (#4841)
When specifying the same argument multiple times, the same version will be downloaded multiple times: ```sh $ cargo run -- python install --preview --force 3.12.3 cpython-3.12 3.12.3 3.12 Looking for installation Python 3.12.3 (any-3.12.3-any-any-any) Looking for installation cpython-3.12-any-any-any (cpython-3.12-any-any-any) Looking for installation Python 3.12.3 (any-3.12.3-any-any-any) Looking for installation Python 3.12 (any-3.12-any-any-any) Found 4/4 versions requiring installation Downloading cpython-3.12.3-windows-x86_64-none Downloading cpython-3.12.3-windows-x86_64-none Downloading cpython-3.12.3-windows-x86_64-none Downloading cpython-3.12.3-windows-x86_64-none ``` This PR deduplicates the `ManagedPythonDownload` before `install` or `uninstall`: ```sh $ cargo run -q -- python install --preview --force 3.12.3 cpython-3.12 3.12.3 3.12 Looking for installation Python 3.12 (any-3.12-any-any-any) Looking for installation Python 3.12.3 (any-3.12.3-any-any-any) Looking for installation cpython-3.12-any-any-any (cpython-3.12-any-any-any) Downloading cpython-3.12.3-windows-x86_64-none Installed Python 3.12.3 to C:\Users\nigel\AppData\Roaming\uv\data\python\cpython-3.12.3-windows-x86_64-none Installed 1 version in 6s $ cargo run -q -- python uninstall --preview 3.12.3 cpython-3.12 3.12.3 3.12 Looking for Python installations matching Python 3.12 (any-3.12-any-any-any) Found installation `cpython-3.12.3-windows-x86_64-none` that matches Python 3.12 Looking for Python installations matching Python 3.12.3 (any-3.12.3-any-any-any) Looking for Python installations matching cpython-3.12-any-any-any (cpython-3.12-any-any-any) Uninstalled `cpython-3.12.3-windows-x86_64-none` Removed 1 Python installation ```
This commit is contained in:
parent
1bd73a7346
commit
bcb2568f47
6 changed files with 24 additions and 18 deletions
|
@ -10,14 +10,14 @@ pub enum Error {
|
|||
UnknownImplementation(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, Default, PartialOrd, Ord)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, Default, PartialOrd, Ord, Hash)]
|
||||
pub enum ImplementationName {
|
||||
#[default]
|
||||
CPython,
|
||||
PyPy,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
|
||||
pub enum LenientImplementationName {
|
||||
Known(ImplementationName),
|
||||
Unknown(String),
|
||||
|
|
|
@ -208,7 +208,7 @@ pub enum PythonInstallationKeyError {
|
|||
ParseError(String, String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct PythonInstallationKey {
|
||||
pub(crate) implementation: LenientImplementationName,
|
||||
pub(crate) major: u8,
|
||||
|
|
|
@ -229,7 +229,7 @@ Error=This Python installation is managed by uv and should not be modified.
|
|||
";
|
||||
|
||||
/// A uv-managed Python installation on the current system..
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct ManagedPythonInstallation {
|
||||
/// The path to the top-level directory of the installed Python.
|
||||
path: PathBuf,
|
||||
|
|
|
@ -13,13 +13,13 @@ pub enum Error {
|
|||
UnknownLibc(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
|
||||
pub struct Arch(pub(crate) target_lexicon::Architecture);
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
|
||||
pub struct Os(pub(crate) target_lexicon::OperatingSystem);
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
|
||||
pub enum Libc {
|
||||
Some(target_lexicon::Environment),
|
||||
None,
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use std::collections::BTreeSet;
|
||||
use std::fmt::Write;
|
||||
|
||||
use anyhow::Result;
|
||||
use fs_err as fs;
|
||||
use futures::StreamExt;
|
||||
|
||||
use itertools::Itertools;
|
||||
use uv_cache::Cache;
|
||||
use uv_client::Connectivity;
|
||||
use uv_configuration::PreviewMode;
|
||||
|
@ -36,6 +37,7 @@ pub(crate) async fn install(
|
|||
let installations_dir = installations.root();
|
||||
let _lock = installations.acquire_lock()?;
|
||||
|
||||
let targets = targets.into_iter().collect::<BTreeSet<_>>();
|
||||
let requests: Vec<_> = if targets.is_empty() {
|
||||
if let Some(requests) = requests_from_version_file().await? {
|
||||
requests
|
||||
|
@ -101,15 +103,6 @@ pub(crate) async fn install(
|
|||
return Ok(ExitStatus::Success);
|
||||
}
|
||||
|
||||
if unfilled_requests.len() > 1 {
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"Found {}/{} versions requiring installation",
|
||||
unfilled_requests.len(),
|
||||
requests.len()
|
||||
)?;
|
||||
}
|
||||
|
||||
let downloads = unfilled_requests
|
||||
.into_iter()
|
||||
// Populate the download requests with defaults
|
||||
|
@ -117,6 +110,18 @@ pub(crate) async fn install(
|
|||
.map(|request| ManagedPythonDownload::from_request(&request))
|
||||
.collect::<Result<Vec<_>, uv_python::downloads::Error>>()?;
|
||||
|
||||
// Ensure we only download each version once
|
||||
let downloads = downloads
|
||||
.into_iter()
|
||||
.unique_by(|download| download.key())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"Found {} versions requiring installation",
|
||||
downloads.len()
|
||||
)?;
|
||||
|
||||
// Construct a client
|
||||
let client = uv_client::BaseClientBuilder::new()
|
||||
.connectivity(connectivity)
|
||||
|
|
|
@ -27,6 +27,7 @@ pub(crate) async fn uninstall(
|
|||
let installations = ManagedPythonInstallations::from_settings()?.init()?;
|
||||
let _lock = installations.acquire_lock()?;
|
||||
|
||||
let targets = targets.into_iter().collect::<BTreeSet<_>>();
|
||||
let requests = targets
|
||||
.iter()
|
||||
.map(|target| PythonRequest::parse(target.as_str()))
|
||||
|
@ -83,7 +84,7 @@ pub(crate) async fn uninstall(
|
|||
return Ok(ExitStatus::Failure);
|
||||
}
|
||||
|
||||
let tasks = futures::stream::iter(matching_installations.iter())
|
||||
let tasks = futures::stream::iter(matching_installations.iter().unique())
|
||||
.map(|installation| async {
|
||||
(
|
||||
installation.key(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue