mirror of https://github.com/astral-sh/uv
Disable concurrent progress bars in Jupyter Notebooks (#3890)
## Summary Resolves https://github.com/astral-sh/uv/issues/3887 by disabling the new progress output when the `JPY_SESSION_NAME` environment variable is detected.
This commit is contained in:
parent
47db418ba2
commit
038af6e658
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::env;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
|
@ -17,8 +18,18 @@ use crate::printer::Printer;
|
||||||
struct ProgressReporter {
|
struct ProgressReporter {
|
||||||
printer: Printer,
|
printer: Printer,
|
||||||
root: ProgressBar,
|
root: ProgressBar,
|
||||||
multi_progress: MultiProgress,
|
mode: ProgressMode,
|
||||||
state: Arc<Mutex<BarState>>,
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum ProgressMode {
|
||||||
|
// Reports top-level progress.
|
||||||
|
Single,
|
||||||
|
// Reports progress of all concurrent download/build/checkout processes.
|
||||||
|
Multi {
|
||||||
|
multi_progress: MultiProgress,
|
||||||
|
state: Arc<Mutex<BarState>>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
|
@ -42,11 +53,41 @@ impl BarState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProgressReporter {
|
impl ProgressReporter {
|
||||||
|
fn new(mut root: ProgressBar, printer: Printer) -> ProgressReporter {
|
||||||
|
let mode = if env::var("JPY_SESSION_NAME").is_ok() {
|
||||||
|
// Disable concurrent progress bars when running inside a Jupyter notebook
|
||||||
|
// because the Jupyter terminal does not support clearing previous lines.
|
||||||
|
// See: https://github.com/astral-sh/uv/issues/3887.
|
||||||
|
ProgressMode::Single
|
||||||
|
} else {
|
||||||
|
let multi_progress = MultiProgress::with_draw_target(printer.target());
|
||||||
|
root = multi_progress.add(root);
|
||||||
|
|
||||||
|
ProgressMode::Multi {
|
||||||
|
state: Arc::default(),
|
||||||
|
multi_progress,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ProgressReporter {
|
||||||
|
printer,
|
||||||
|
root,
|
||||||
|
mode,
|
||||||
|
}
|
||||||
|
}
|
||||||
fn on_build_start(&self, source: &BuildableSource) -> usize {
|
fn on_build_start(&self, source: &BuildableSource) -> usize {
|
||||||
let mut state = self.state.lock().unwrap();
|
let ProgressMode::Multi {
|
||||||
|
multi_progress,
|
||||||
|
state,
|
||||||
|
} = &self.mode
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut state = state.lock().unwrap();
|
||||||
let id = state.id();
|
let id = state.id();
|
||||||
|
|
||||||
let progress = self.multi_progress.insert_before(
|
let progress = multi_progress.insert_before(
|
||||||
&self.root,
|
&self.root,
|
||||||
ProgressBar::with_draw_target(None, self.printer.target()),
|
ProgressBar::with_draw_target(None, self.printer.target()),
|
||||||
);
|
);
|
||||||
|
|
@ -64,8 +105,12 @@ impl ProgressReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_build_complete(&self, source: &BuildableSource, id: usize) {
|
fn on_build_complete(&self, source: &BuildableSource, id: usize) {
|
||||||
|
let ProgressMode::Multi { state, .. } = &self.mode else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let progress = {
|
let progress = {
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = state.lock().unwrap();
|
||||||
state.headers -= 1;
|
state.headers -= 1;
|
||||||
state.bars.remove(&id).unwrap()
|
state.bars.remove(&id).unwrap()
|
||||||
};
|
};
|
||||||
|
|
@ -78,13 +123,21 @@ impl ProgressReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_download_start(&self, name: &PackageName, size: Option<u64>) -> usize {
|
fn on_download_start(&self, name: &PackageName, size: Option<u64>) -> usize {
|
||||||
let mut state = self.state.lock().unwrap();
|
let ProgressMode::Multi {
|
||||||
|
multi_progress,
|
||||||
|
state,
|
||||||
|
} = &self.mode
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut state = state.lock().unwrap();
|
||||||
|
|
||||||
// Preserve ascending order.
|
// Preserve ascending order.
|
||||||
let position = size.map_or(0, |size| state.sizes.partition_point(|&len| len < size));
|
let position = size.map_or(0, |size| state.sizes.partition_point(|&len| len < size));
|
||||||
state.sizes.insert(position, size.unwrap_or(0));
|
state.sizes.insert(position, size.unwrap_or(0));
|
||||||
|
|
||||||
let progress = self.multi_progress.insert(
|
let progress = multi_progress.insert(
|
||||||
// Make sure not to reorder the initial "Downloading..." bar, or any previous bars.
|
// Make sure not to reorder the initial "Downloading..." bar, or any previous bars.
|
||||||
position + 1 + state.headers,
|
position + 1 + state.headers,
|
||||||
ProgressBar::with_draw_target(size, self.printer.target()),
|
ProgressBar::with_draw_target(size, self.printer.target()),
|
||||||
|
|
@ -111,19 +164,35 @@ impl ProgressReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_download_progress(&self, id: usize, bytes: u64) {
|
fn on_download_progress(&self, id: usize, bytes: u64) {
|
||||||
self.state.lock().unwrap().bars[&id].inc(bytes);
|
let ProgressMode::Multi { state, .. } = &self.mode else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
state.lock().unwrap().bars[&id].inc(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_download_complete(&self, id: usize) {
|
fn on_download_complete(&self, id: usize) {
|
||||||
let progress = self.state.lock().unwrap().bars.remove(&id).unwrap();
|
let ProgressMode::Multi { state, .. } = &self.mode else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let progress = state.lock().unwrap().bars.remove(&id).unwrap();
|
||||||
progress.finish_and_clear();
|
progress.finish_and_clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_checkout_start(&self, url: &Url, rev: &str) -> usize {
|
fn on_checkout_start(&self, url: &Url, rev: &str) -> usize {
|
||||||
let mut state = self.state.lock().unwrap();
|
let ProgressMode::Multi {
|
||||||
|
multi_progress,
|
||||||
|
state,
|
||||||
|
} = &self.mode
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut state = state.lock().unwrap();
|
||||||
let id = state.id();
|
let id = state.id();
|
||||||
|
|
||||||
let progress = self.multi_progress.insert_before(
|
let progress = multi_progress.insert_before(
|
||||||
&self.root,
|
&self.root,
|
||||||
ProgressBar::with_draw_target(None, self.printer.target()),
|
ProgressBar::with_draw_target(None, self.printer.target()),
|
||||||
);
|
);
|
||||||
|
|
@ -143,8 +212,12 @@ impl ProgressReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_checkout_complete(&self, url: &Url, rev: &str, id: usize) {
|
fn on_checkout_complete(&self, url: &Url, rev: &str, id: usize) {
|
||||||
|
let ProgressMode::Multi { state, .. } = &self.mode else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let progress = {
|
let progress = {
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = state.lock().unwrap();
|
||||||
state.headers -= 1;
|
state.headers -= 1;
|
||||||
state.bars.remove(&id).unwrap()
|
state.bars.remove(&id).unwrap()
|
||||||
};
|
};
|
||||||
|
|
@ -165,24 +238,16 @@ pub(crate) struct DownloadReporter {
|
||||||
|
|
||||||
impl From<Printer> for DownloadReporter {
|
impl From<Printer> for DownloadReporter {
|
||||||
fn from(printer: Printer) -> Self {
|
fn from(printer: Printer) -> Self {
|
||||||
let multi_progress = MultiProgress::with_draw_target(printer.target());
|
let root = ProgressBar::with_draw_target(None, printer.target());
|
||||||
|
root.enable_steady_tick(Duration::from_millis(200));
|
||||||
let progress = multi_progress.add(ProgressBar::with_draw_target(None, printer.target()));
|
root.set_style(
|
||||||
progress.enable_steady_tick(Duration::from_millis(200));
|
|
||||||
progress.set_style(
|
|
||||||
ProgressStyle::with_template("{spinner:.white} {msg:.dim} ({pos}/{len})")
|
ProgressStyle::with_template("{spinner:.white} {msg:.dim} ({pos}/{len})")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]),
|
.tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]),
|
||||||
);
|
);
|
||||||
progress.set_message("Downloading packages...");
|
root.set_message("Downloading packages...");
|
||||||
|
|
||||||
let reporter = ProgressReporter {
|
|
||||||
printer,
|
|
||||||
multi_progress,
|
|
||||||
root: progress,
|
|
||||||
state: Arc::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
|
let reporter = ProgressReporter::new(root, printer);
|
||||||
Self { reporter }
|
Self { reporter }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -201,6 +266,9 @@ impl uv_installer::DownloadReporter for DownloadReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_complete(&self) {
|
fn on_complete(&self) {
|
||||||
|
// Need an extra call to `set_message` here to fully clear avoid leaving ghost output
|
||||||
|
// in Jupyter notebooks.
|
||||||
|
self.reporter.root.set_message("");
|
||||||
self.reporter.root.finish_and_clear();
|
self.reporter.root.finish_and_clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,9 +316,7 @@ impl ResolverReporter {
|
||||||
|
|
||||||
impl From<Printer> for ResolverReporter {
|
impl From<Printer> for ResolverReporter {
|
||||||
fn from(printer: Printer) -> Self {
|
fn from(printer: Printer) -> Self {
|
||||||
let multi_progress = MultiProgress::with_draw_target(printer.target());
|
let root = ProgressBar::with_draw_target(None, printer.target());
|
||||||
|
|
||||||
let root = multi_progress.add(ProgressBar::with_draw_target(None, printer.target()));
|
|
||||||
root.enable_steady_tick(Duration::from_millis(200));
|
root.enable_steady_tick(Duration::from_millis(200));
|
||||||
root.set_style(
|
root.set_style(
|
||||||
ProgressStyle::with_template("{spinner:.white} {wide_msg:.dim}")
|
ProgressStyle::with_template("{spinner:.white} {wide_msg:.dim}")
|
||||||
|
|
@ -259,14 +325,8 @@ impl From<Printer> for ResolverReporter {
|
||||||
);
|
);
|
||||||
root.set_message("Resolving dependencies...");
|
root.set_message("Resolving dependencies...");
|
||||||
|
|
||||||
let reporter = ProgressReporter {
|
let reporter = ProgressReporter::new(root, printer);
|
||||||
root,
|
Self { reporter }
|
||||||
printer,
|
|
||||||
multi_progress,
|
|
||||||
state: Arc::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
ResolverReporter { reporter }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -283,6 +343,7 @@ impl uv_resolver::ResolverReporter for ResolverReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_complete(&self) {
|
fn on_complete(&self) {
|
||||||
|
self.reporter.root.set_message("");
|
||||||
self.reporter.root.finish_and_clear();
|
self.reporter.root.finish_and_clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -376,6 +437,7 @@ impl uv_installer::InstallReporter for InstallReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_install_complete(&self) {
|
fn on_install_complete(&self) {
|
||||||
|
self.progress.set_message("");
|
||||||
self.progress.finish_and_clear();
|
self.progress.finish_and_clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue