Skip to content

Commit a91d08d

Browse files
arthaudmeta-codesync[bot]
authored andcommitted
Add --progress-bar option with interactive/simple/no modes
Summary: When pyrefly is run by another tool such as pysa, the interactive progress bar produces garbled log output. This adds a `--progress-bar` option with three modes: `interactive` (default, the existing fancy progress bar), `simple` (log-style "Processed X of Y modules" lines suitable for non-interactive use), and `no` (disabled). The existing `--no-progress-bar` (in check) and `--show-progress-bar` (in buck-check) are kept for backward compatibility but hidden from help output. Reviewed By: stroxler Differential Revision: D102639648 fbshipit-source-id: 8a6959837a31584b1560494c47d86f756f2314f5
1 parent 75b7a58 commit a91d08d

3 files changed

Lines changed: 116 additions & 26 deletions

File tree

pyrefly/lib/commands/buck_check.rs

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use crate::error::legacy::LegacyErrors;
3737
use crate::report;
3838
use crate::state::require::Require;
3939
use crate::state::state::State;
40-
use crate::state::subscriber::ProgressBarSubscriber;
40+
use crate::state::subscriber::ProgressBarStyle;
4141

4242
/// Arguments for Buck-powered type checking.
4343
#[deny(clippy::missing_docs_in_private_items)]
@@ -63,9 +63,16 @@ pub struct BuckCheckArgs {
6363
#[arg(long, value_enum, default_value_t = report::pysa::PysaFormat::Capnp)]
6464
report_pysa_format: report::pysa::PysaFormat,
6565

66-
/// Show a progress bar during type checking.
67-
#[arg(long)]
66+
/// Show a progress bar during type checking. Deprecated: use `--progress-bar=interactive` instead.
67+
#[arg(long, hide = true)]
6868
show_progress_bar: bool,
69+
70+
/// Set the progress bar style.
71+
/// `interactive` shows a visual progress bar.
72+
/// `simple` prints periodic log-style progress messages (suitable for piping or non-interactive use).
73+
/// `no` (default) disables progress reporting entirely.
74+
#[arg(long, value_enum)]
75+
progress_bar: Option<ProgressBarStyle>,
6976
}
7077

7178
#[derive(Debug, Deserialize, PartialEq, Eq)]
@@ -90,7 +97,7 @@ fn compute_errors(
9097
thread_count: ThreadCount,
9198
report_pysa: Option<&Path>,
9299
report_pysa_format: report::pysa::PysaFormat,
93-
show_progress_bar: bool,
100+
progress_bar_style: ProgressBarStyle,
94101
) -> anyhow::Result<Vec<Error>> {
95102
let modules_to_check = sourcedb.modules_to_check().into_iter().collect::<Vec<_>>();
96103

@@ -134,19 +141,15 @@ fn compute_errors(
134141
transaction.as_mut().set_pysa_reporter(Some(reporter));
135142
}
136143

137-
if show_progress_bar {
138-
transaction
139-
.as_mut()
140-
.set_subscriber(Some(Box::new(ProgressBarSubscriber::new())));
141-
}
144+
transaction
145+
.as_mut()
146+
.set_subscriber(progress_bar_style.make_subscriber());
142147

143148
transaction
144149
.as_mut()
145150
.run(&modules_to_check, Require::Errors, None);
146151

147-
if show_progress_bar {
148-
transaction.as_mut().set_subscriber(None);
149-
}
152+
transaction.as_mut().set_subscriber(None);
150153

151154
let errors = transaction.as_ref().get_errors(&modules_to_check);
152155

@@ -199,6 +202,17 @@ fn write_output(errors: &[Error], path: Option<&Path>) -> anyhow::Result<()> {
199202
}
200203

201204
impl BuckCheckArgs {
205+
fn progress_bar_style(&self) -> ProgressBarStyle {
206+
if let Some(style) = &self.progress_bar {
207+
return style.clone();
208+
}
209+
if self.show_progress_bar {
210+
ProgressBarStyle::Interactive
211+
} else {
212+
ProgressBarStyle::No
213+
}
214+
}
215+
202216
pub fn run(self, thread_count: ThreadCount) -> anyhow::Result<CommandExitStatus> {
203217
let input_file = read_input_file(self.input_path.as_path())?;
204218
let python_version = PythonVersion::from_str(&input_file.py_version)?;
@@ -216,7 +230,7 @@ impl BuckCheckArgs {
216230
thread_count,
217231
self.report_pysa.as_deref(),
218232
self.report_pysa_format,
219-
self.show_progress_bar,
233+
self.progress_bar_style(),
220234
)?;
221235
let min_severity = self.min_severity.unwrap_or(Severity::Error);
222236
let displayed_errors: Vec<Error> = type_errors

pyrefly/lib/commands/check.rs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ use crate::state::require::Require;
7575
use crate::state::require::RequireLevels;
7676
use crate::state::state::State;
7777
use crate::state::state::Transaction;
78-
use crate::state::subscriber::ProgressBarSubscriber;
78+
use crate::state::subscriber::ProgressBarStyle;
7979

8080
/// Result data from a non-watch check run, used for telemetry logging.
8181
pub struct CheckResult {
@@ -319,10 +319,17 @@ struct OutputArgs {
319319
)]
320320
summary: Summary,
321321

322-
/// Suppress the progress bar during type checking.
323-
#[arg(long)]
322+
/// Suppress the progress bar during type checking. Deprecated: use `--progress-bar=no` instead.
323+
#[arg(long, hide = true)]
324324
no_progress_bar: bool,
325325

326+
/// Set the progress bar style.
327+
/// `interactive` (default) shows a visual progress bar.
328+
/// `simple` prints periodic log-style progress messages (suitable for piping or non-interactive use).
329+
/// `no` disables progress reporting entirely.
330+
#[arg(long, value_enum)]
331+
progress_bar: Option<ProgressBarStyle>,
332+
326333
/// When specified, strip this prefix from any paths in the output.
327334
/// Pass "" to show absolute paths. When omitted, we will use the current working directory.
328335
#[arg(long)]
@@ -358,6 +365,18 @@ impl OutputArgs {
358365
fn output_format(&self) -> OutputFormat {
359366
self.output_format.unwrap_or_default()
360367
}
368+
369+
/// Resolve the effective progress bar style, taking deprecated flags into account.
370+
fn progress_bar_style(&self) -> ProgressBarStyle {
371+
if let Some(style) = &self.progress_bar {
372+
return style.clone();
373+
}
374+
if self.no_progress_bar || self.summary == Summary::None {
375+
ProgressBarStyle::No
376+
} else {
377+
ProgressBarStyle::Interactive
378+
}
379+
}
361380
}
362381

363382
#[derive(Clone, Debug, ValueEnum, Default, PartialEq, Eq)]
@@ -873,7 +892,7 @@ impl CheckArgs {
873892
let events = get_watcher_events(&mut watcher).await?;
874893
transaction = state.new_committable_transaction(
875894
require_levels.default,
876-
Some(Box::new(ProgressBarSubscriber::new())),
895+
self.output.progress_bar_style().make_subscriber(),
877896
);
878897
let new_transaction_mut = transaction.as_mut();
879898
new_transaction_mut.invalidate_events(&events);
@@ -947,15 +966,9 @@ impl CheckArgs {
947966
}
948967

949968
let type_check_start = Instant::now();
950-
let show_progress_bar =
951-
self.output.summary != Summary::None && !self.output.no_progress_bar;
952-
if show_progress_bar {
953-
transaction.set_subscriber(Some(Box::new(ProgressBarSubscriber::new())));
954-
}
969+
transaction.set_subscriber(self.output.progress_bar_style().make_subscriber());
955970
transaction.run(handles, require, None);
956-
if show_progress_bar {
957-
transaction.set_subscriber(None);
958-
}
971+
transaction.set_subscriber(None);
959972

960973
let loads = if self.behavior.check_all {
961974
transaction.get_all_errors()
@@ -1159,7 +1172,7 @@ impl CheckArgs {
11591172
}
11601173
if let Some(output_path) = &self.output.report_timings {
11611174
eprintln!("Computing timing information");
1162-
transaction.set_subscriber(Some(Box::new(ProgressBarSubscriber::new())));
1175+
transaction.set_subscriber(self.output.progress_bar_style().make_subscriber());
11631176
transaction.report_timings(output_path)?;
11641177
transaction.set_subscriber(None);
11651178
}

pyrefly/lib/state/subscriber.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use std::mem;
99
use std::sync::Arc;
1010

11+
use clap::ValueEnum;
1112
use dupe::Dupe;
1213
use indicatif::ProgressBar;
1314
use indicatif::ProgressStyle;
@@ -20,6 +21,29 @@ use starlark_map::small_map::SmallMap;
2021
use crate::state::load::Load;
2122
use crate::state::state::Transaction;
2223

24+
/// Controls which progress bar style to use during type checking.
25+
#[derive(Clone, Debug, ValueEnum, Default, PartialEq, Eq)]
26+
pub enum ProgressBarStyle {
27+
/// Show an interactive progress bar (default).
28+
#[default]
29+
Interactive,
30+
/// Show simple log-style progress messages, suitable for non-interactive use.
31+
Simple,
32+
/// Disable progress reporting entirely.
33+
No,
34+
}
35+
36+
impl ProgressBarStyle {
37+
/// Create a subscriber matching this style, or `None` if progress reporting is disabled.
38+
pub fn make_subscriber(&self) -> Option<Box<dyn Subscriber>> {
39+
match self {
40+
ProgressBarStyle::Interactive => Some(Box::new(ProgressBarSubscriber::new())),
41+
ProgressBarStyle::Simple => Some(Box::new(SimpleProgressBarSubscriber::new())),
42+
ProgressBarStyle::No => None,
43+
}
44+
}
45+
}
46+
2347
/// Trait to capture which handles are executed by `State`.
2448
/// Calls to `start_work` and `finish_work` will be paired.
2549
/// It may be the case that a single `Handle` is computed multiple times within a single call to `run`.
@@ -176,6 +200,45 @@ impl ProgressBarSubscriber {
176200
}
177201
}
178202

203+
/// A simple progress bar subscriber that prints log-style progress messages.
204+
/// Suitable for non-interactive environments (e.g., when output is piped to a file
205+
/// or when pyrefly is invoked by another tool like pysa).
206+
pub struct SimpleProgressBarSubscriber {
207+
state: Mutex<ProgressBarState>,
208+
}
209+
210+
impl SimpleProgressBarSubscriber {
211+
pub fn new() -> Self {
212+
Self {
213+
state: Mutex::new(ProgressBarState {
214+
last_progress: 0,
215+
started: 0,
216+
finished: 0,
217+
}),
218+
}
219+
}
220+
}
221+
222+
impl Subscriber for SimpleProgressBarSubscriber {
223+
fn start_work(&self, _: &Handle) {
224+
self.state.lock().started += 1;
225+
}
226+
227+
fn finish_work(&self, _: &Transaction<'_>, _: &Handle, _: &Arc<Load>, _: bool) {
228+
let (finished, started) = {
229+
let mut state = self.state.lock();
230+
state.finished += 1;
231+
(state.finished, state.started)
232+
};
233+
// Log at regular intervals to avoid flooding the output.
234+
// Print on the first completion, then at every 1% of progress, and at the end.
235+
let interval = (started / 100).max(1);
236+
if finished == 1 || finished == started || finished % interval == 0 {
237+
eprintln!("Processed {} of {} modules", finished, started);
238+
}
239+
}
240+
}
241+
179242
pub struct PublishDiagnosticsSubscriber<F>
180243
where
181244
F: Fn(&Transaction<'_>, &Handle, bool) + Send + Sync,

0 commit comments

Comments
 (0)