mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Show messages for builds and large downloads in non-interactive mode (#11165)
When stderr is not a tty, we currently don't show any messages for build or large downloads, since indicatif is hidden. We can improve this by showing a message for: * Starting and finishing a large download (>1MB) * Starting and finishing a build Downloads are limited to 1MB or unknown size to keep the logs concise and not scroll the entire terminal away for a download that finishes almost immediately. These messages are not captured in the tests since their order is non-deterministic (downloads and builds race to finish). There are no "tick" messages for large downloads yet, we could e.g. show an update on runnning downloads every n seconds. Part of #11121 **Test Plan** ``` $ uv venv && FORCE_COLOR=1 cargo run -q pip install numpy --no-binary :all: --no-cache 2>&1 | tee a.txt Using CPython 3.13.0 Creating virtual environment at: .venv Activate with: source .venv/bin/activate Resolved 1 package in 221ms Building numpy==2.2.2 Built numpy==2.2.2 Prepared 1 package in 2m 34s Installed 1 package in 6ms + numpy==2.2.2 ```  ``` $ uv venv && FORCE_COLOR=1 cargo run -q pip install torch --no-cache 2>&1 | tee b.txt Using CPython 3.13.0 Creating virtual environment at: .venv Activate with: source .venv/bin/activate Resolved 24 packages in 648ms Downloading setuptools (1.2MiB) Downloading nvidia-cuda-cupti-cu12 (13.2MiB) Downloading torch (731.1MiB) Downloading nvidia-nvjitlink-cu12 (20.1MiB) Downloading nvidia-cufft-cu12 (201.7MiB) Downloading nvidia-cuda-nvrtc-cu12 (23.5MiB) Downloading nvidia-curand-cu12 (53.7MiB) Downloading nvidia-nccl-cu12 (179.9MiB) Downloading nvidia-cudnn-cu12 (634.0MiB) Downloading nvidia-cublas-cu12 (346.6MiB) Downloading sympy (5.9MiB) Downloading nvidia-cusparse-cu12 (197.8MiB) Downloading nvidia-cusparselt-cu12 (143.1MiB) Downloading networkx (1.6MiB) Downloading nvidia-cusolver-cu12 (122.0MiB) Downloading triton (241.4MiB) Downloaded setuptools Downloaded networkx Downloaded sympy Downloaded nvidia-cuda-cupti-cu12 Downloaded nvidia-nvjitlink-cu12 Downloaded nvidia-cuda-nvrtc-cu12 Downloaded nvidia-curand-cu12 [...] ``` 
This commit is contained in:
parent
b83e25a911
commit
59b46bc216
4 changed files with 95 additions and 21 deletions
|
@ -133,7 +133,7 @@ impl GitSource {
|
|||
// Report the checkout operation to the reporter.
|
||||
if let Some(task) = task {
|
||||
if let Some(reporter) = self.reporter.as_ref() {
|
||||
reporter.on_checkout_complete(remote.url(), short_id.as_str(), task);
|
||||
reporter.on_checkout_complete(remote.url(), actual_rev.as_str(), task);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -579,6 +579,10 @@ impl EnvVars {
|
|||
#[attr_hidden]
|
||||
pub const UV_TEST_INDEX_URL: &'static str = "UV_TEST_INDEX_URL";
|
||||
|
||||
/// Hide progress messages with non-deterministic order in tests.
|
||||
#[attr_hidden]
|
||||
pub const UV_TEST_NO_CLI_PROGRESS: &'static str = "UV_TEST_NO_CLI_PROGRESS";
|
||||
|
||||
/// `.env` files from which to load environment variables when executing `uv run` commands.
|
||||
pub const UV_ENV_FILE: &'static str = "UV_ENV_FILE";
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use std::env;
|
||||
use std::fmt::Write;
|
||||
use std::sync::LazyLock;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
|
@ -7,6 +9,8 @@ use owo_colors::OwoColorize;
|
|||
use rustc_hash::FxHashMap;
|
||||
use url::Url;
|
||||
|
||||
use crate::commands::human_readable_bytes;
|
||||
use crate::printer::Printer;
|
||||
use uv_cache::Removal;
|
||||
use uv_distribution_types::{
|
||||
BuildableSource, CachedDist, DistributionMetadata, Name, SourceDist, VersionOrUrlRef,
|
||||
|
@ -16,7 +20,10 @@ use uv_pep440::Version;
|
|||
use uv_python::PythonInstallationKey;
|
||||
use uv_static::EnvVars;
|
||||
|
||||
use crate::printer::Printer;
|
||||
/// Since downloads, fetches and builds run in parallel, their message output order is
|
||||
/// non-deterministic, so can't capture them in test output.
|
||||
static HAS_UV_TEST_NO_CLI_PROGRESS: LazyLock<bool> =
|
||||
LazyLock::new(|| env::var(EnvVars::UV_TEST_NO_CLI_PROGRESS).is_ok());
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ProgressReporter {
|
||||
|
@ -44,6 +51,8 @@ struct BarState {
|
|||
sizes: Vec<u64>,
|
||||
/// A map of progress bars, by ID.
|
||||
bars: FxHashMap<usize, ProgressBar>,
|
||||
/// The download size, if known, by ID.
|
||||
download_size: FxHashMap<usize, Option<u64>>,
|
||||
/// A monotonic counter for bar IDs.
|
||||
id: usize,
|
||||
}
|
||||
|
@ -95,11 +104,15 @@ impl ProgressReporter {
|
|||
);
|
||||
|
||||
progress.set_style(ProgressStyle::with_template("{wide_msg}").unwrap());
|
||||
progress.set_message(format!(
|
||||
"{} {}",
|
||||
let message = format!(
|
||||
" {} {}",
|
||||
"Building".bold().cyan(),
|
||||
source.to_color_string()
|
||||
));
|
||||
);
|
||||
if multi_progress.is_hidden() && !*HAS_UV_TEST_NO_CLI_PROGRESS {
|
||||
let _ = writeln!(self.printer.stderr(), "{message}");
|
||||
}
|
||||
progress.set_message(message);
|
||||
|
||||
state.headers += 1;
|
||||
state.bars.insert(id, progress);
|
||||
|
@ -107,7 +120,11 @@ impl ProgressReporter {
|
|||
}
|
||||
|
||||
fn on_build_complete(&self, source: &BuildableSource, id: usize) {
|
||||
let ProgressMode::Multi { state, .. } = &self.mode else {
|
||||
let ProgressMode::Multi {
|
||||
state,
|
||||
multi_progress,
|
||||
} = &self.mode
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -117,11 +134,15 @@ impl ProgressReporter {
|
|||
state.bars.remove(&id).unwrap()
|
||||
};
|
||||
|
||||
progress.finish_with_message(format!(
|
||||
" {} {}",
|
||||
let message = format!(
|
||||
" {} {}",
|
||||
"Built".bold().green(),
|
||||
source.to_color_string()
|
||||
));
|
||||
);
|
||||
if multi_progress.is_hidden() && !*HAS_UV_TEST_NO_CLI_PROGRESS {
|
||||
let _ = writeln!(self.printer.stderr(), "{message}");
|
||||
}
|
||||
progress.finish_with_message(message);
|
||||
}
|
||||
|
||||
fn on_download_start(&self, name: String, size: Option<u64>) -> usize {
|
||||
|
@ -145,7 +166,7 @@ impl ProgressReporter {
|
|||
ProgressBar::with_draw_target(size, self.printer.target()),
|
||||
);
|
||||
|
||||
if size.is_some() {
|
||||
if let Some(size) = size {
|
||||
// We're using binary bytes to match `human_readable_bytes`.
|
||||
progress.set_style(
|
||||
ProgressStyle::with_template(
|
||||
|
@ -154,15 +175,36 @@ impl ProgressReporter {
|
|||
.unwrap()
|
||||
.progress_chars("--"),
|
||||
);
|
||||
// If the download is larger than 1MB, show a message to indicate that this may take
|
||||
// a while keeping the log concise.
|
||||
if multi_progress.is_hidden() && !*HAS_UV_TEST_NO_CLI_PROGRESS && size > 1024 * 1024 {
|
||||
let (bytes, unit) = human_readable_bytes(size);
|
||||
let _ = writeln!(
|
||||
self.printer.stderr(),
|
||||
"{} {} {}",
|
||||
"Downloading".bold().cyan(),
|
||||
name,
|
||||
format!("({bytes:.1}{unit})").dimmed()
|
||||
);
|
||||
}
|
||||
progress.set_message(name);
|
||||
} else {
|
||||
progress.set_style(ProgressStyle::with_template("{wide_msg:.dim} ....").unwrap());
|
||||
if multi_progress.is_hidden() && !*HAS_UV_TEST_NO_CLI_PROGRESS {
|
||||
let _ = writeln!(
|
||||
self.printer.stderr(),
|
||||
"{} {}",
|
||||
"Downloading".bold().cyan(),
|
||||
name
|
||||
);
|
||||
}
|
||||
progress.set_message(name);
|
||||
progress.finish();
|
||||
}
|
||||
|
||||
let id = state.id();
|
||||
state.bars.insert(id, progress);
|
||||
state.download_size.insert(id, size);
|
||||
id
|
||||
}
|
||||
|
||||
|
@ -175,11 +217,29 @@ impl ProgressReporter {
|
|||
}
|
||||
|
||||
fn on_download_complete(&self, id: usize) {
|
||||
let ProgressMode::Multi { state, .. } = &self.mode else {
|
||||
let ProgressMode::Multi {
|
||||
state,
|
||||
multi_progress,
|
||||
} = &self.mode
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let progress = state.lock().unwrap().bars.remove(&id).unwrap();
|
||||
|
||||
let size = state.lock().unwrap().download_size[&id];
|
||||
if multi_progress.is_hidden()
|
||||
&& !*HAS_UV_TEST_NO_CLI_PROGRESS
|
||||
&& size.is_none_or(|size| size > 1024 * 1024)
|
||||
{
|
||||
let _ = writeln!(
|
||||
self.printer.stderr(),
|
||||
" {} {}",
|
||||
"Downloaded".bold().green(),
|
||||
progress.message()
|
||||
);
|
||||
}
|
||||
|
||||
progress.finish_and_clear();
|
||||
}
|
||||
|
||||
|
@ -201,12 +261,11 @@ impl ProgressReporter {
|
|||
);
|
||||
|
||||
progress.set_style(ProgressStyle::with_template("{wide_msg}").unwrap());
|
||||
progress.set_message(format!(
|
||||
"{} {} ({})",
|
||||
"Updating".bold().cyan(),
|
||||
url,
|
||||
rev.dimmed()
|
||||
));
|
||||
let message = format!(" {} {} ({})", "Updating".bold().cyan(), url, rev.dimmed());
|
||||
if multi_progress.is_hidden() && !*HAS_UV_TEST_NO_CLI_PROGRESS {
|
||||
let _ = writeln!(self.printer.stderr(), "{message}");
|
||||
}
|
||||
progress.set_message(message);
|
||||
progress.finish();
|
||||
|
||||
state.headers += 1;
|
||||
|
@ -215,7 +274,11 @@ impl ProgressReporter {
|
|||
}
|
||||
|
||||
fn on_checkout_complete(&self, url: &Url, rev: &str, id: usize) {
|
||||
let ProgressMode::Multi { state, .. } = &self.mode else {
|
||||
let ProgressMode::Multi {
|
||||
state,
|
||||
multi_progress,
|
||||
} = &self.mode
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -225,12 +288,16 @@ impl ProgressReporter {
|
|||
state.bars.remove(&id).unwrap()
|
||||
};
|
||||
|
||||
progress.finish_with_message(format!(
|
||||
" {} {} ({})",
|
||||
let message = format!(
|
||||
" {} {} ({})",
|
||||
"Updated".bold().green(),
|
||||
url,
|
||||
rev.dimmed()
|
||||
));
|
||||
);
|
||||
if multi_progress.is_hidden() && !*HAS_UV_TEST_NO_CLI_PROGRESS {
|
||||
let _ = writeln!(self.printer.stderr(), "{message}");
|
||||
}
|
||||
progress.finish_with_message(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -555,6 +555,9 @@ impl TestContext {
|
|||
.env(EnvVars::UV_PYTHON_DOWNLOADS, "never")
|
||||
.env(EnvVars::UV_TEST_PYTHON_PATH, self.python_path())
|
||||
.env(EnvVars::UV_EXCLUDE_NEWER, EXCLUDE_NEWER)
|
||||
// Since downloads, fetches and builds run in parallel, their message output order is
|
||||
// non-deterministic, so can't capture them in test output.
|
||||
.env(EnvVars::UV_TEST_NO_CLI_PROGRESS, "1")
|
||||
.env_remove(EnvVars::UV_CACHE_DIR)
|
||||
.env_remove(EnvVars::UV_TOOL_BIN_DIR)
|
||||
.current_dir(self.temp_dir.path());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue