Make --quiet more aggressive (#566)

This commit is contained in:
Charlie Marsh 2022-11-03 10:08:15 -04:00 committed by GitHub
parent 9f9cbb5520
commit a3f7de2257
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 135 additions and 65 deletions

View file

@ -7,6 +7,7 @@ use log::warn;
use regex::Regex; use regex::Regex;
use crate::checks_gen::CheckCodePrefix; use crate::checks_gen::CheckCodePrefix;
use crate::logging::LogLevel;
use crate::printer::SerializationFormat; use crate::printer::SerializationFormat;
use crate::settings::configuration::Configuration; use crate::settings::configuration::Configuration;
use crate::settings::types::{PatternPrefixPair, PythonVersion}; use crate::settings::types::{PatternPrefixPair, PythonVersion};
@ -93,6 +94,19 @@ pub struct Cli {
pub stdin_filename: Option<String>, pub stdin_filename: Option<String>,
} }
/// Map the CLI settings to a `LogLevel`.
pub fn extract_log_level(cli: &Cli) -> LogLevel {
if cli.silent {
LogLevel::Silent
} else if cli.quiet {
LogLevel::Quiet
} else if cli.verbose {
LogLevel::Verbose
} else {
LogLevel::Default
}
}
pub enum Warnable { pub enum Warnable {
Select, Select,
ExtendSelect, ExtendSelect,

View file

@ -15,7 +15,36 @@ macro_rules! tell_user {
} }
} }
pub fn set_up_logging(verbose: bool) -> Result<()> { #[derive(Debug, PartialOrd, Ord, PartialEq, Eq)]
pub enum LogLevel {
// No output (+ `log::LevelFilter::Off`).
Silent,
// Only show lint violations, with no decorative output (+ `log::LevelFilter::Off`).
Quiet,
// All user-facing output (+ `log::LevelFilter::Info`).
Default,
// All user-facing output (+ `log::LevelFilter::Debug`).
Verbose,
}
impl LogLevel {
fn level_filter(&self) -> log::LevelFilter {
match self {
LogLevel::Default => log::LevelFilter::Info,
LogLevel::Verbose => log::LevelFilter::Debug,
LogLevel::Quiet => log::LevelFilter::Off,
LogLevel::Silent => log::LevelFilter::Off,
}
}
}
impl Default for LogLevel {
fn default() -> Self {
LogLevel::Default
}
}
pub fn set_up_logging(level: &LogLevel) -> Result<()> {
fern::Dispatch::new() fern::Dispatch::new()
.format(|out, message, record| { .format(|out, message, record| {
out.finish(format_args!( out.finish(format_args!(
@ -26,12 +55,22 @@ pub fn set_up_logging(verbose: bool) -> Result<()> {
message message
)) ))
}) })
.level(if verbose { .level(level.level_filter())
log::LevelFilter::Debug
} else {
log::LevelFilter::Info
})
.chain(std::io::stdout()) .chain(std::io::stdout())
.apply() .apply()
.map_err(|e| e.into()) .map_err(|e| e.into())
} }
#[cfg(test)]
mod tests {
use crate::logging::LogLevel;
#[test]
fn ordering() {
assert!(LogLevel::Default > LogLevel::Silent);
assert!(LogLevel::Default >= LogLevel::Default);
assert!(LogLevel::Quiet > LogLevel::Silent);
assert!(LogLevel::Verbose > LogLevel::Default);
assert!(LogLevel::Verbose > LogLevel::Silent);
}
}

View file

@ -16,17 +16,16 @@ use rayon::prelude::*;
use ruff::cache; use ruff::cache;
use ruff::checks::{CheckCode, CheckKind}; use ruff::checks::{CheckCode, CheckKind};
use ruff::checks_gen::CheckCodePrefix; use ruff::checks_gen::CheckCodePrefix;
use ruff::cli::{collect_per_file_ignores, warn_on, Cli, Warnable}; use ruff::cli::{collect_per_file_ignores, extract_log_level, warn_on, Cli, Warnable};
use ruff::fs::iter_python_files; use ruff::fs::iter_python_files;
use ruff::linter::{add_noqa_to_path, autoformat_path, lint_path, lint_stdin}; use ruff::linter::{add_noqa_to_path, autoformat_path, lint_path, lint_stdin};
use ruff::logging::set_up_logging; use ruff::logging::{set_up_logging, LogLevel};
use ruff::message::Message; use ruff::message::Message;
use ruff::printer::{Printer, SerializationFormat}; use ruff::printer::{Printer, SerializationFormat};
use ruff::settings::configuration::Configuration; use ruff::settings::configuration::Configuration;
use ruff::settings::types::FilePattern; use ruff::settings::types::FilePattern;
use ruff::settings::user::UserConfiguration; use ruff::settings::user::UserConfiguration;
use ruff::settings::{pyproject, Settings}; use ruff::settings::{pyproject, Settings};
use ruff::tell_user;
use walkdir::DirEntry; use walkdir::DirEntry;
#[cfg(feature = "update-informer")] #[cfg(feature = "update-informer")]
@ -223,10 +222,10 @@ fn autoformat(files: &[PathBuf], settings: &Settings) -> Result<usize> {
} }
fn inner_main() -> Result<ExitCode> { fn inner_main() -> Result<ExitCode> {
let mut cli = Cli::parse(); let cli = Cli::parse();
cli.quiet |= cli.silent;
set_up_logging(cli.verbose)?; let log_level = extract_log_level(&cli);
set_up_logging(&log_level)?;
// Find the project root and pyproject.toml. // Find the project root and pyproject.toml.
let project_root = pyproject::find_project_root(&cli.files); let project_root = pyproject::find_project_root(&cli.files);
@ -320,7 +319,7 @@ fn inner_main() -> Result<ExitCode> {
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
cache::init()?; cache::init()?;
let mut printer = Printer::new(cli.format, cli.verbose); let printer = Printer::new(&cli.format, &log_level);
if cli.watch { if cli.watch {
if cli.fix { if cli.fix {
eprintln!("Warning: --fix is not enabled in watch mode."); eprintln!("Warning: --fix is not enabled in watch mode.");
@ -340,12 +339,10 @@ fn inner_main() -> Result<ExitCode> {
// Perform an initial run instantly. // Perform an initial run instantly.
printer.clear_screen()?; printer.clear_screen()?;
tell_user!("Starting linter in watch mode...\n"); printer.write_to_user("Starting linter in watch mode...\n");
let messages = run_once(&cli.files, &settings, !cli.no_cache, false)?; let messages = run_once(&cli.files, &settings, !cli.no_cache, false)?;
if !cli.silent { printer.write_continuously(&messages)?;
printer.write_continuously(&messages)?;
}
// Configure the file watcher. // Configure the file watcher.
let (tx, rx) = channel(); let (tx, rx) = channel();
@ -360,12 +357,10 @@ fn inner_main() -> Result<ExitCode> {
if let Some(path) = e.path { if let Some(path) = e.path {
if path.to_string_lossy().ends_with(".py") { if path.to_string_lossy().ends_with(".py") {
printer.clear_screen()?; printer.clear_screen()?;
tell_user!("File change detected...\n"); printer.write_to_user("File change detected...\n");
let messages = run_once(&cli.files, &settings, !cli.no_cache, false)?; let messages = run_once(&cli.files, &settings, !cli.no_cache, false)?;
if !cli.silent { printer.write_continuously(&messages)?;
printer.write_continuously(&messages)?;
}
} }
} }
} }
@ -374,37 +369,36 @@ fn inner_main() -> Result<ExitCode> {
} }
} else if cli.add_noqa { } else if cli.add_noqa {
let modifications = add_noqa(&cli.files, &settings)?; let modifications = add_noqa(&cli.files, &settings)?;
if modifications > 0 { if modifications > 0 && log_level >= LogLevel::Default {
println!("Added {modifications} noqa directives."); println!("Added {modifications} noqa directives.");
} }
} else if cli.autoformat { } else if cli.autoformat {
let modifications = autoformat(&cli.files, &settings)?; let modifications = autoformat(&cli.files, &settings)?;
if modifications > 0 { if modifications > 0 && log_level >= LogLevel::Default {
println!("Formatted {modifications} files."); println!("Formatted {modifications} files.");
} }
} else { } else {
let (messages, should_print_messages, should_check_updates) = let is_stdin = cli.files == vec![PathBuf::from("-")];
if cli.files == vec![PathBuf::from("-")] {
let filename = cli.stdin_filename.unwrap_or_else(|| "-".to_string()); // Generate lint violations.
let path = Path::new(&filename); let messages = if is_stdin {
( let filename = cli.stdin_filename.unwrap_or_else(|| "-".to_string());
run_once_stdin(&settings, path, cli.fix)?, let path = Path::new(&filename);
!cli.silent && !cli.fix, run_once_stdin(&settings, path, cli.fix)?
false, } else {
) run_once(&cli.files, &settings, !cli.no_cache, cli.fix)?
} else { };
(
run_once(&cli.files, &settings, !cli.no_cache, cli.fix)?, // Always try to print violations (the printer itself may suppress output),
!cli.silent, // unless we're writing fixes via stdin (in which case, the transformed
!cli.quiet, // source code goes to stdout).
) if !(is_stdin && cli.fix) {
};
if should_print_messages {
printer.write_once(&messages)?; printer.write_once(&messages)?;
} }
// Check for updates if we're in a non-silent log level.
#[cfg(feature = "update-informer")] #[cfg(feature = "update-informer")]
if should_check_updates { if !is_stdin && log_level >= LogLevel::Default {
check_for_updates(); check_for_updates();
} }

View file

@ -5,6 +5,7 @@ use rustpython_parser::ast::Location;
use serde::Serialize; use serde::Serialize;
use crate::checks::{CheckCode, CheckKind}; use crate::checks::{CheckCode, CheckKind};
use crate::logging::LogLevel;
use crate::message::Message; use crate::message::Message;
use crate::tell_user; use crate::tell_user;
@ -25,17 +26,27 @@ struct ExpandedMessage<'a> {
filename: &'a String, filename: &'a String,
} }
pub struct Printer { pub struct Printer<'a> {
format: SerializationFormat, format: &'a SerializationFormat,
verbose: bool, log_level: &'a LogLevel,
} }
impl Printer { impl<'a> Printer<'a> {
pub fn new(format: SerializationFormat, verbose: bool) -> Self { pub fn new(format: &'a SerializationFormat, log_level: &'a LogLevel) -> Self {
Self { format, verbose } Self { format, log_level }
} }
pub fn write_once(&mut self, messages: &[Message]) -> Result<()> { pub fn write_to_user(&self, message: &str) {
if self.log_level >= &LogLevel::Default {
tell_user!("{}", message);
}
}
pub fn write_once(&self, messages: &[Message]) -> Result<()> {
if matches!(self.log_level, LogLevel::Silent) {
return Ok(());
}
let (fixed, outstanding): (Vec<&Message>, Vec<&Message>) = let (fixed, outstanding): (Vec<&Message>, Vec<&Message>) =
messages.iter().partition(|message| message.fixed); messages.iter().partition(|message| message.fixed);
let num_fixable = outstanding let num_fixable = outstanding
@ -64,22 +75,26 @@ impl Printer {
) )
} }
SerializationFormat::Text => { SerializationFormat::Text => {
if !fixed.is_empty() { if self.log_level >= &LogLevel::Default {
println!( if !fixed.is_empty() {
"Found {} error(s) ({} fixed).", println!(
outstanding.len(), "Found {} error(s) ({} fixed).",
fixed.len() outstanding.len(),
) fixed.len()
} else if !outstanding.is_empty() || self.verbose { )
println!("Found {} error(s).", outstanding.len()) } else if !outstanding.is_empty() {
println!("Found {} error(s).", outstanding.len())
}
} }
for message in outstanding { for message in outstanding {
println!("{}", message) println!("{}", message)
} }
if num_fixable > 0 { if self.log_level >= &LogLevel::Default {
println!("{num_fixable} potentially fixable with the --fix option.") if num_fixable > 0 {
println!("{num_fixable} potentially fixable with the --fix option.")
}
} }
} }
} }
@ -87,14 +102,22 @@ impl Printer {
Ok(()) Ok(())
} }
pub fn write_continuously(&mut self, messages: &[Message]) -> Result<()> { pub fn write_continuously(&self, messages: &[Message]) -> Result<()> {
tell_user!( if matches!(self.log_level, LogLevel::Silent) {
"Found {} error(s). Watching for file changes.", return Ok(());
messages.len(), }
);
if self.log_level >= &LogLevel::Default {
tell_user!(
"Found {} error(s). Watching for file changes.",
messages.len(),
);
}
if !messages.is_empty() { if !messages.is_empty() {
println!(); if self.log_level >= &LogLevel::Default {
println!();
}
for message in messages { for message in messages {
println!("{}", message) println!("{}", message)
} }
@ -103,7 +126,7 @@ impl Printer {
Ok(()) Ok(())
} }
pub fn clear_screen(&mut self) -> Result<()> { pub fn clear_screen(&self) -> Result<()> {
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
clearscreen::clear()?; clearscreen::clear()?;
Ok(()) Ok(())