Skip to content

Commit

Permalink
Add --show-urls and --only-downloads to uv python list
Browse files Browse the repository at this point in the history
These are useful for creating a mirror of the Python downloads for a given uv version
  • Loading branch information
zanieb committed Dec 10, 2024
1 parent 3ee2b10 commit d468cd4
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 55 deletions.
14 changes: 13 additions & 1 deletion crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4225,8 +4225,20 @@ pub struct PythonListArgs {
/// Only show installed Python versions, exclude available downloads.
///
/// By default, available downloads for the current platform are shown.
#[arg(long)]
#[arg(long, conflicts_with("only_downloads"))]
pub only_installed: bool,

/// Only show Python downloads, exclude installed distributions.
///
/// By default, available downloads for the current platform are shown.
#[arg(long, conflicts_with("only_installed"))]
pub only_downloads: bool,

/// Show the URLs of available Python downloads.
///
/// By default, these display as `<download available>`.
#[arg(long)]
pub show_urls: bool,
}

#[derive(Args)]
Expand Down
134 changes: 80 additions & 54 deletions crates/uv/src/commands/python/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::collections::BTreeSet;
use std::fmt::Write;

use anyhow::Result;
use itertools::Either;
use owo_colors::OwoColorize;
use rustc_hash::FxHashSet;
use uv_cache::Cache;
Expand Down Expand Up @@ -29,6 +30,7 @@ pub(crate) async fn list(
kinds: PythonListKinds,
all_versions: bool,
all_platforms: bool,
show_urls: bool,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
cache: &Cache,
Expand All @@ -38,6 +40,11 @@ pub(crate) async fn list(
if python_preference != PythonPreference::OnlySystem {
let download_request = match kinds {
PythonListKinds::Installed => None,
PythonListKinds::Downloads => Some(if all_platforms {
PythonDownloadRequest::default()
} else {
PythonDownloadRequest::from_env()?
}),
PythonListKinds::Default => {
if python_downloads.is_automatic() {
Some(if all_platforms {
Expand All @@ -61,48 +68,60 @@ pub(crate) async fn list(
.flatten();

for download in downloads {
output.insert((download.key().clone(), Kind::Download, None));
output.insert((
download.key().clone(),
Kind::Download,
Either::Right(download.url()),
));
}
};

let installed = find_python_installations(
&PythonRequest::Any,
EnvironmentPreference::OnlySystem,
python_preference,
cache,
)
// Raise discovery errors if critical
.filter(|result| {
result
.as_ref()
.err()
.map_or(true, DiscoveryError::is_critical)
})
.collect::<Result<Vec<Result<PythonInstallation, PythonNotFound>>, DiscoveryError>>()?
.into_iter()
// Drop any "missing" installations
.filter_map(Result::ok);

for installation in installed {
let kind = if matches!(installation.source(), PythonSource::Managed) {
Kind::Managed
} else {
Kind::System
let installed =
match kinds {
PythonListKinds::Installed | PythonListKinds::Default => {
Some(find_python_installations(
&PythonRequest::Any,
EnvironmentPreference::OnlySystem,
python_preference,
cache,
)
// Raise discovery errors if critical
.filter(|result| {
result
.as_ref()
.err()
.map_or(true, DiscoveryError::is_critical)
})
.collect::<Result<Vec<Result<PythonInstallation, PythonNotFound>>, DiscoveryError>>()?
.into_iter()
// Drop any "missing" installations
.filter_map(Result::ok))
}
PythonListKinds::Downloads => None,
};
output.insert((
installation.key(),
kind,
Some(installation.interpreter().sys_executable().to_path_buf()),
));

if let Some(installed) = installed {
for installation in installed {
let kind = if matches!(installation.source(), PythonSource::Managed) {
Kind::Managed
} else {
Kind::System
};
output.insert((
installation.key(),
kind,
Either::Left(installation.interpreter().sys_executable().to_path_buf()),
));
}
}

let mut seen_minor = FxHashSet::default();
let mut seen_patch = FxHashSet::default();
let mut seen_paths = FxHashSet::default();
let mut include = Vec::new();
for (key, kind, path) in output.iter().rev() {
for (key, kind, uri) in output.iter().rev() {
// Do not show the same path more than once
if let Some(path) = path {
if let Either::Left(path) = uri {
if !seen_paths.insert(path) {
continue;
}
Expand Down Expand Up @@ -142,38 +161,45 @@ pub(crate) async fn list(
}
}
}
include.push((key, path));
include.push((key, uri));
}

// Compute the width of the first column.
let width = include
.iter()
.fold(0usize, |acc, (key, _)| acc.max(key.to_string().len()));

for (key, path) in include {
for (key, uri) in include {
let key = key.to_string();
if let Some(path) = path {
let is_symlink = fs_err::symlink_metadata(path)?.is_symlink();
if is_symlink {
writeln!(
printer.stdout(),
"{key:width$} {} -> {}",
path.user_display().cyan(),
path.read_link()?.user_display().cyan()
)?;
} else {
writeln!(
printer.stdout(),
"{key:width$} {}",
path.user_display().cyan()
)?;
match uri {
Either::Left(path) => {
let is_symlink = fs_err::symlink_metadata(path)?.is_symlink();
if is_symlink {
writeln!(
printer.stdout(),
"{key:width$} {} -> {}",
path.user_display().cyan(),
path.read_link()?.user_display().cyan()
)?;
} else {
writeln!(
printer.stdout(),
"{key:width$} {}",
path.user_display().cyan()
)?;
}
}
Either::Right(url) => {
if show_urls {
writeln!(printer.stdout(), "{key:width$} {}", url.dimmed())?;
} else {
writeln!(
printer.stdout(),
"{key:width$} {}",
"<download available>".dimmed()
)?;
}
}
} else {
writeln!(
printer.stdout(),
"{key:width$} {}",
"<download available>".dimmed()
)?;
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
args.kinds,
args.all_versions,
args.all_platforms,
args.show_urls,
globals.python_preference,
globals.python_downloads,
&cache,
Expand Down
9 changes: 9 additions & 0 deletions crates/uv/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,9 @@ impl ToolDirSettings {
pub(crate) enum PythonListKinds {
#[default]
Default,
/// Only list version downloads.
Downloads,
/// Only list installed versions.
Installed,
}

Expand All @@ -713,6 +716,7 @@ pub(crate) struct PythonListSettings {
pub(crate) kinds: PythonListKinds,
pub(crate) all_platforms: bool,
pub(crate) all_versions: bool,
pub(crate) show_urls: bool,
}

impl PythonListSettings {
Expand All @@ -723,10 +727,14 @@ impl PythonListSettings {
all_versions,
all_platforms,
only_installed,
only_downloads,
show_urls,
} = args;

let kinds = if only_installed {
PythonListKinds::Installed
} else if only_downloads {
PythonListKinds::Downloads
} else {
PythonListKinds::default()
};
Expand All @@ -735,6 +743,7 @@ impl PythonListSettings {
kinds,
all_platforms,
all_versions,
show_urls,
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -4426,6 +4426,10 @@ uv python list [OPTIONS]

<p>When disabled, uv will only use locally cached data and locally available files.</p>

</dd><dt><code>--only-downloads</code></dt><dd><p>Only show Python downloads, exclude installed distributions.</p>

<p>By default, available downloads for the current platform are shown.</p>

</dd><dt><code>--only-installed</code></dt><dd><p>Only show installed Python versions, exclude available downloads.</p>

<p>By default, available downloads for the current platform are shown.</p>
Expand Down Expand Up @@ -4458,6 +4462,10 @@ uv python list [OPTIONS]
</ul>
</dd><dt><code>--quiet</code>, <code>-q</code></dt><dd><p>Do not print any output</p>

</dd><dt><code>--show-urls</code></dt><dd><p>Show the URLs of available Python downloads.</p>

<p>By default, these display as <code>&lt;download available&gt;</code>.</p>

</dd><dt><code>--verbose</code>, <code>-v</code></dt><dd><p>Use verbose output.</p>

<p>You can configure fine-grained logging using the <code>RUST_LOG</code> environment variable. (&lt;https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives&gt;)</p>
Expand Down

0 comments on commit d468cd4

Please sign in to comment.