Skip to content

Commit ad19b3f

Browse files
authored
[red-knot] Add verbosity argument to CLI (#12404)
1 parent a62e2d2 commit ad19b3f

File tree

6 files changed

+89
-36
lines changed

6 files changed

+89
-36
lines changed

crates/red_knot/src/cli/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub(crate) mod target_version;
2+
pub(crate) mod verbosity;

crates/red_knot/src/cli/verbosity.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
2+
pub(crate) enum VerbosityLevel {
3+
Info,
4+
Debug,
5+
Trace,
6+
}
7+
8+
/// Logging flags to `#[command(flatten)]` into your CLI
9+
#[derive(clap::Args, Debug, Clone, Default)]
10+
#[command(about = None, long_about = None)]
11+
pub(crate) struct Verbosity {
12+
#[arg(
13+
long,
14+
short = 'v',
15+
help = "Use verbose output (or `-vv` and `-vvv` for more verbose output)",
16+
action = clap::ArgAction::Count,
17+
global = true,
18+
)]
19+
verbose: u8,
20+
}
21+
22+
impl Verbosity {
23+
/// Returns the verbosity level based on the number of `-v` flags.
24+
///
25+
/// Returns `None` if the user did not specify any verbosity flags.
26+
pub(crate) fn level(&self) -> Option<VerbosityLevel> {
27+
match self.verbose {
28+
0 => None,
29+
1 => Some(VerbosityLevel::Info),
30+
2 => Some(VerbosityLevel::Debug),
31+
_ => Some(VerbosityLevel::Trace),
32+
}
33+
}
34+
}

crates/red_knot/src/main.rs

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ use red_knot::workspace::WorkspaceMetadata;
1717
use ruff_db::program::{ProgramSettings, SearchPathSettings};
1818
use ruff_db::system::{OsSystem, System, SystemPathBuf};
1919

20-
use self::target_version::TargetVersion;
20+
use cli::target_version::TargetVersion;
21+
use cli::verbosity::{Verbosity, VerbosityLevel};
2122

22-
mod target_version;
23+
mod cli;
2324

2425
#[derive(Debug, Parser)]
2526
#[command(
@@ -43,14 +44,19 @@ struct Args {
4344
help = "Custom directory to use for stdlib typeshed stubs"
4445
)]
4546
custom_typeshed_dir: Option<SystemPathBuf>,
47+
4648
#[arg(
4749
long,
4850
value_name = "PATH",
4951
help = "Additional path to use as a module-resolution source (can be passed multiple times)"
5052
)]
5153
extra_search_path: Vec<SystemPathBuf>,
54+
5255
#[arg(long, help = "Python version to assume when resolving types", default_value_t = TargetVersion::default(), value_name="VERSION")]
5356
target_version: TargetVersion,
57+
58+
#[clap(flatten)]
59+
verbosity: Verbosity,
5460
}
5561

5662
#[allow(
@@ -60,16 +66,18 @@ struct Args {
6066
clippy::dbg_macro
6167
)]
6268
pub fn main() -> anyhow::Result<()> {
63-
countme::enable(true);
64-
setup_tracing();
65-
6669
let Args {
6770
current_directory,
6871
custom_typeshed_dir,
6972
extra_search_path: extra_paths,
7073
target_version,
74+
verbosity,
7175
} = Args::parse_from(std::env::args().collect::<Vec<_>>());
7276

77+
let verbosity = verbosity.level();
78+
countme::enable(verbosity == Some(VerbosityLevel::Trace));
79+
setup_tracing(verbosity);
80+
7381
let cwd = if let Some(cwd) = current_directory {
7482
let canonicalized = cwd.as_utf8_path().canonicalize_utf8().unwrap();
7583
SystemPathBuf::from_utf8_path_buf(canonicalized)
@@ -97,7 +105,7 @@ pub fn main() -> anyhow::Result<()> {
97105
// cache and load the cache if it exists.
98106
let mut db = RootDatabase::new(workspace_metadata, program_settings, system);
99107

100-
let (main_loop, main_loop_cancellation_token) = MainLoop::new();
108+
let (main_loop, main_loop_cancellation_token) = MainLoop::new(verbosity);
101109

102110
// Listen to Ctrl+C and abort the watch mode.
103111
let main_loop_cancellation_token = Mutex::new(Some(main_loop_cancellation_token));
@@ -126,18 +134,19 @@ pub fn main() -> anyhow::Result<()> {
126134
}
127135

128136
struct MainLoop {
129-
orchestrator_sender: crossbeam_channel::Sender<OrchestratorMessage>,
130-
main_loop_receiver: crossbeam_channel::Receiver<MainLoopMessage>,
137+
verbosity: Option<VerbosityLevel>,
138+
orchestrator: crossbeam_channel::Sender<OrchestratorMessage>,
139+
receiver: crossbeam_channel::Receiver<MainLoopMessage>,
131140
}
132141

133142
impl MainLoop {
134-
fn new() -> (Self, MainLoopCancellationToken) {
143+
fn new(verbosity: Option<VerbosityLevel>) -> (Self, MainLoopCancellationToken) {
135144
let (orchestrator_sender, orchestrator_receiver) = crossbeam_channel::bounded(1);
136145
let (main_loop_sender, main_loop_receiver) = crossbeam_channel::bounded(1);
137146

138147
let mut orchestrator = Orchestrator {
139148
receiver: orchestrator_receiver,
140-
sender: main_loop_sender.clone(),
149+
main_loop: main_loop_sender.clone(),
141150
revision: 0,
142151
};
143152

@@ -147,8 +156,9 @@ impl MainLoop {
147156

148157
(
149158
Self {
150-
orchestrator_sender,
151-
main_loop_receiver,
159+
verbosity,
160+
orchestrator: orchestrator_sender,
161+
receiver: main_loop_receiver,
152162
},
153163
MainLoopCancellationToken {
154164
sender: main_loop_sender,
@@ -158,29 +168,27 @@ impl MainLoop {
158168

159169
fn file_changes_notifier(&self) -> FileChangesNotifier {
160170
FileChangesNotifier {
161-
sender: self.orchestrator_sender.clone(),
171+
sender: self.orchestrator.clone(),
162172
}
163173
}
164174

165175
#[allow(clippy::print_stderr)]
166176
fn run(self, db: &mut RootDatabase) {
167-
self.orchestrator_sender
168-
.send(OrchestratorMessage::Run)
169-
.unwrap();
177+
self.orchestrator.send(OrchestratorMessage::Run).unwrap();
170178

171-
for message in &self.main_loop_receiver {
179+
for message in &self.receiver {
172180
tracing::trace!("Main Loop: Tick");
173181

174182
match message {
175183
MainLoopMessage::CheckWorkspace { revision } => {
176184
let db = db.snapshot();
177-
let sender = self.orchestrator_sender.clone();
185+
let orchestrator = self.orchestrator.clone();
178186

179187
// Spawn a new task that checks the workspace. This needs to be done in a separate thread
180188
// to prevent blocking the main loop here.
181189
rayon::spawn(move || {
182190
if let Ok(result) = db.check() {
183-
sender
191+
orchestrator
184192
.send(OrchestratorMessage::CheckCompleted {
185193
diagnostics: result,
186194
revision,
@@ -195,10 +203,14 @@ impl MainLoop {
195203
}
196204
MainLoopMessage::CheckCompleted(diagnostics) => {
197205
eprintln!("{}", diagnostics.join("\n"));
198-
eprintln!("{}", countme::get_all());
206+
if self.verbosity == Some(VerbosityLevel::Trace) {
207+
eprintln!("{}", countme::get_all());
208+
}
199209
}
200210
MainLoopMessage::Exit => {
201-
eprintln!("{}", countme::get_all());
211+
if self.verbosity == Some(VerbosityLevel::Trace) {
212+
eprintln!("{}", countme::get_all());
213+
}
202214
return;
203215
}
204216
}
@@ -208,7 +220,7 @@ impl MainLoop {
208220

209221
impl Drop for MainLoop {
210222
fn drop(&mut self) {
211-
self.orchestrator_sender
223+
self.orchestrator
212224
.send(OrchestratorMessage::Shutdown)
213225
.unwrap();
214226
}
@@ -240,7 +252,7 @@ impl MainLoopCancellationToken {
240252

241253
struct Orchestrator {
242254
/// Sends messages to the main loop.
243-
sender: crossbeam_channel::Sender<MainLoopMessage>,
255+
main_loop: crossbeam_channel::Sender<MainLoopMessage>,
244256
/// Receives messages from the main loop.
245257
receiver: crossbeam_channel::Receiver<OrchestratorMessage>,
246258
revision: usize,
@@ -252,7 +264,7 @@ impl Orchestrator {
252264
while let Ok(message) = self.receiver.recv() {
253265
match message {
254266
OrchestratorMessage::Run => {
255-
self.sender
267+
self.main_loop
256268
.send(MainLoopMessage::CheckWorkspace {
257269
revision: self.revision,
258270
})
@@ -265,7 +277,7 @@ impl Orchestrator {
265277
} => {
266278
// Only take the diagnostics if they are for the latest revision.
267279
if self.revision == revision {
268-
self.sender
280+
self.main_loop
269281
.send(MainLoopMessage::CheckCompleted(diagnostics))
270282
.unwrap();
271283
} else {
@@ -313,8 +325,8 @@ impl Orchestrator {
313325
},
314326
default(std::time::Duration::from_millis(10)) => {
315327
// No more file changes after 10 ms, send the changes and schedule a new analysis
316-
self.sender.send(MainLoopMessage::ApplyChanges(changes)).unwrap();
317-
self.sender.send(MainLoopMessage::CheckWorkspace { revision: self.revision}).unwrap();
328+
self.main_loop.send(MainLoopMessage::ApplyChanges(changes)).unwrap();
329+
self.main_loop.send(MainLoopMessage::CheckWorkspace { revision: self.revision}).unwrap();
318330
return;
319331
}
320332
}
@@ -349,7 +361,14 @@ enum OrchestratorMessage {
349361
FileChanges(Vec<FileWatcherChange>),
350362
}
351363

352-
fn setup_tracing() {
364+
fn setup_tracing(verbosity: Option<VerbosityLevel>) {
365+
let trace_level = match verbosity {
366+
None => Level::WARN,
367+
Some(VerbosityLevel::Info) => Level::INFO,
368+
Some(VerbosityLevel::Debug) => Level::DEBUG,
369+
Some(VerbosityLevel::Trace) => Level::TRACE,
370+
};
371+
353372
let subscriber = Registry::default().with(
354373
tracing_tree::HierarchicalLayer::default()
355374
.with_indent_lines(true)
@@ -359,9 +378,7 @@ fn setup_tracing() {
359378
.with_targets(true)
360379
.with_writer(|| Box::new(std::io::stderr()))
361380
.with_timer(Uptime::default())
362-
.with_filter(LoggingFilter {
363-
trace_level: Level::TRACE,
364-
}),
381+
.with_filter(LoggingFilter { trace_level }),
365382
);
366383

367384
tracing::subscriber::set_global_default(subscriber).unwrap();

crates/red_knot_module_resolver/src/resolver.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,11 @@ pub(crate) fn module_resolution_settings(db: &dyn Db) -> ModuleResolutionSetting
125125
} = program.search_paths(db.upcast());
126126

127127
if let Some(custom_typeshed) = custom_typeshed {
128-
tracing::debug!("Custom typeshed directory: {custom_typeshed}");
128+
tracing::info!("Custom typeshed directory: {custom_typeshed}");
129129
}
130130

131131
if !extra_paths.is_empty() {
132-
tracing::debug!("extra search paths: {extra_paths:?}");
132+
tracing::info!("extra search paths: {extra_paths:?}");
133133
}
134134

135135
let current_directory = db.system().current_directory();
@@ -174,7 +174,7 @@ pub(crate) fn module_resolution_settings(db: &dyn Db) -> ModuleResolutionSetting
174174
// TODO vendor typeshed's third-party stubs as well as the stdlib and fallback to them as a final step
175175

176176
let target_version = program.target_version(db.upcast());
177-
tracing::debug!("Target version: {target_version}");
177+
tracing::info!("Target version: {target_version}");
178178

179179
// Filter out module resolution paths that point to the same directory on disk (the same invariant maintained by [`sys.path` at runtime]).
180180
// (Paths may, however, *overlap* -- e.g. you could have both `src/` and `src/foo`

crates/ruff_db/src/files.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ impl Files {
5858
///
5959
/// The operation always succeeds even if the path doesn't exist on disk, isn't accessible or if the path points to a directory.
6060
/// In these cases, a file with status [`FileStatus::Deleted`] is returned.
61-
#[tracing::instrument(level = "debug", skip(self, db), ret)]
61+
#[tracing::instrument(level = "trace", skip(self, db), ret)]
6262
fn system(&self, db: &dyn Db, path: &SystemPath) -> File {
6363
let absolute = SystemPath::absolute(path, db.system().current_directory());
6464
let absolute = FilePath::System(absolute);
@@ -102,7 +102,7 @@ impl Files {
102102

103103
/// Looks up a vendored file by its path. Returns `Some` if a vendored file for the given path
104104
/// exists and `None` otherwise.
105-
#[tracing::instrument(level = "debug", skip(self, db), ret)]
105+
#[tracing::instrument(level = "trace", skip(self, db), ret)]
106106
fn vendored(&self, db: &dyn Db, path: &VendoredPath) -> Option<File> {
107107
let file = match self
108108
.inner

0 commit comments

Comments
 (0)