Skip to content

Commit

Permalink
Add a --fix-only command-line and pyproject.toml option (#1375)
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh authored Dec 25, 2022
1 parent ec80d1c commit d9355c9
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 31 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1798,6 +1798,23 @@ fix = true

---

#### [`fix-only`](#fix-only)

Like `fix`, but disables reporting on leftover violation. Implies `fix`.

**Default value**: `false`

**Type**: `bool`

**Example usage**:

```toml
[tool.ruff]
fix-only = true
```

---

#### [`fixable`](#fixable)

A list of check code prefixes to consider autofix-able.
Expand Down
7 changes: 7 additions & 0 deletions flake8_to_ruff/src/converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ mod tests {
extend_select: None,
external: None,
fix: None,
fix_only: None,
fixable: None,
format: None,
force_exclude: None,
Expand Down Expand Up @@ -350,6 +351,7 @@ mod tests {
extend_select: None,
external: None,
fix: None,
fix_only: None,
fixable: None,
format: None,
force_exclude: None,
Expand Down Expand Up @@ -405,6 +407,7 @@ mod tests {
extend_select: None,
external: None,
fix: None,
fix_only: None,
fixable: None,
format: None,
force_exclude: None,
Expand Down Expand Up @@ -460,6 +463,7 @@ mod tests {
extend_select: None,
external: None,
fix: None,
fix_only: None,
fixable: None,
format: None,
force_exclude: None,
Expand Down Expand Up @@ -515,6 +519,7 @@ mod tests {
extend_select: None,
external: None,
fix: None,
fix_only: None,
fixable: None,
format: None,
force_exclude: None,
Expand Down Expand Up @@ -578,6 +583,7 @@ mod tests {
extend_select: None,
external: None,
fix: None,
fix_only: None,
fixable: None,
format: None,
force_exclude: None,
Expand Down Expand Up @@ -669,6 +675,7 @@ mod tests {
extend_select: None,
external: None,
fix: None,
fix_only: None,
fixable: None,
format: None,
force_exclude: None,
Expand Down
7 changes: 7 additions & 0 deletions ruff.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@
"null"
]
},
"fix-only": {
"description": "Like `fix`, but disables reporting on leftover violation. Implies `fix`.",
"type": [
"boolean",
"null"
]
},
"fixable": {
"description": "A list of check code prefixes to consider autofix-able.",
"type": [
Expand Down
8 changes: 8 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ pub struct Cli {
fix: bool,
#[clap(long, overrides_with("fix"), hide = true)]
no_fix: bool,
/// Fix any fixable lint errors, but don't report on leftover violations.
/// Implies `--fix`.
#[arg(long, overrides_with("no_fix_only"))]
fix_only: bool,
#[clap(long, overrides_with("fix_only"), hide = true)]
no_fix_only: bool,
/// Disable cache reads.
#[arg(short, long)]
pub no_cache: bool,
Expand Down Expand Up @@ -181,6 +187,7 @@ impl Cli {
unfixable: self.unfixable,
// TODO(charlie): Included in `pyproject.toml`, but not inherited.
fix: resolve_bool_arg(self.fix, self.no_fix),
fix_only: resolve_bool_arg(self.fix_only, self.no_fix_only),
format: self.format,
force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude),
cache_dir: self.cache_dir,
Expand Down Expand Up @@ -240,6 +247,7 @@ pub struct Overrides {
pub unfixable: Option<Vec<CheckCodePrefix>>,
// TODO(charlie): Captured in pyproject.toml as a default, but not part of `Settings`.
pub fix: Option<bool>,
pub fix_only: Option<bool>,
pub format: Option<SerializationFormat>,
pub force_exclude: Option<bool>,
pub cache_dir: Option<PathBuf>,
Expand Down
25 changes: 16 additions & 9 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use ::ruff::autofix::fixer;
use ::ruff::cli::{extract_log_level, Cli, Overrides};
use ::ruff::commands;
use ::ruff::logging::{set_up_logging, LogLevel};
use ::ruff::printer::Printer;
use ::ruff::printer::{Printer, Violations};
use ::ruff::resolver::{resolve_settings, FileDiscovery, PyprojectDiscovery, Relativity};
use ::ruff::settings::configuration::Configuration;
use ::ruff::settings::types::SerializationFormat;
Expand Down Expand Up @@ -112,17 +112,24 @@ fn inner_main() -> Result<ExitCode> {
PyprojectDiscovery::Hierarchical(settings) => settings.respect_gitignore,
},
};
let (fix, format) = match &pyproject_strategy {
PyprojectDiscovery::Fixed(settings) => (settings.fix, settings.format),
PyprojectDiscovery::Hierarchical(settings) => (settings.fix, settings.format),
let (fix, fix_only, format) = match &pyproject_strategy {
PyprojectDiscovery::Fixed(settings) => (settings.fix, settings.fix_only, settings.format),
PyprojectDiscovery::Hierarchical(settings) => {
(settings.fix, settings.fix_only, settings.format)
}
};
let autofix = if fix {
let autofix = if fix || fix_only {
fixer::Mode::Apply
} else if matches!(format, SerializationFormat::Json) {
fixer::Mode::Generate
} else {
fixer::Mode::None
};
let violations = if fix_only {
Violations::Hide
} else {
Violations::Show
};
let cache = !cli.no_cache;

if let Some(code) = cli.explain {
Expand All @@ -138,13 +145,13 @@ fn inner_main() -> Result<ExitCode> {
return Ok(ExitCode::SUCCESS);
}

let printer = Printer::new(&format, &log_level);
let printer = Printer::new(&format, &log_level, &autofix, &violations);
if cli.watch {
if matches!(autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
eprintln!("Warning: --fix is not enabled in watch mode.");
}
if cli.add_noqa {
eprintln!("Warning: --no-qa is not enabled in watch mode.");
eprintln!("Warning: --add-noqa is not enabled in watch mode.");
}
if cli.autoformat {
eprintln!("Warning: --autoformat is not enabled in watch mode.");
Expand Down Expand Up @@ -240,7 +247,7 @@ fn inner_main() -> Result<ExitCode> {
// unless we're writing fixes via stdin (in which case, the transformed
// source code goes to stdout).
if !(is_stdin && matches!(autofix, fixer::Mode::Apply)) {
printer.write_once(&diagnostics, autofix)?;
printer.write_once(&diagnostics)?;
}

// Check for updates if we're in a non-silent log level.
Expand All @@ -249,7 +256,7 @@ fn inner_main() -> Result<ExitCode> {
drop(updates::check_for_updates());
}

if !diagnostics.messages.is_empty() && !cli.exit_zero {
if !diagnostics.messages.is_empty() && !cli.exit_zero && !fix_only {
return Ok(ExitCode::FAILURE);
}
}
Expand Down
82 changes: 60 additions & 22 deletions src/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ use crate::message::Message;
use crate::settings::types::SerializationFormat;
use crate::tell_user;

/// Enum to control whether lint violations are shown to the user.
pub enum Violations {
Show,
Hide,
}

#[derive(Serialize)]
struct ExpandedMessage<'a> {
code: &'a CheckCode,
Expand All @@ -31,11 +37,23 @@ struct ExpandedMessage<'a> {
pub struct Printer<'a> {
format: &'a SerializationFormat,
log_level: &'a LogLevel,
autofix: &'a fixer::Mode,
violations: &'a Violations,
}

impl<'a> Printer<'a> {
pub fn new(format: &'a SerializationFormat, log_level: &'a LogLevel) -> Self {
Self { format, log_level }
pub fn new(
format: &'a SerializationFormat,
log_level: &'a LogLevel,
autofix: &'a fixer::Mode,
violations: &'a Violations,
) -> Self {
Self {
format,
log_level,
autofix,
violations,
}
}

pub fn write_to_user(&self, message: &str) {
Expand All @@ -44,35 +62,55 @@ impl<'a> Printer<'a> {
}
}

fn post_text(&self, diagnostics: &Diagnostics, autofix: fixer::Mode) {
fn post_text(&self, diagnostics: &Diagnostics) {
if self.log_level >= &LogLevel::Default {
let fixed = diagnostics.fixed;
let remaining = diagnostics.messages.len();
let total = fixed + remaining;
if fixed > 0 {
println!("Found {total} error(s) ({fixed} fixed, {remaining} remaining).");
} else if remaining > 0 {
println!("Found {remaining} error(s).");
}
match self.violations {
Violations::Show => {
let fixed = diagnostics.fixed;
let remaining = diagnostics.messages.len();
let total = fixed + remaining;
if fixed > 0 {
println!("Found {total} error(s) ({fixed} fixed, {remaining} remaining).");
} else if remaining > 0 {
println!("Found {remaining} error(s).");
}

if !matches!(autofix, fixer::Mode::Apply) {
let num_fixable = diagnostics
.messages
.iter()
.filter(|message| message.kind.fixable())
.count();
if num_fixable > 0 {
println!("{num_fixable} potentially fixable with the --fix option.");
if !matches!(self.autofix, fixer::Mode::Apply) {
let num_fixable = diagnostics
.messages
.iter()
.filter(|message| message.kind.fixable())
.count();
if num_fixable > 0 {
println!("{num_fixable} potentially fixable with the --fix option.");
}
}
}
Violations::Hide => {
let fixed = diagnostics.fixed;
if fixed > 0 {
println!("Fixed {fixed} error(s).");
}
}
}
}
}

pub fn write_once(&self, diagnostics: &Diagnostics, autofix: fixer::Mode) -> Result<()> {
pub fn write_once(&self, diagnostics: &Diagnostics) -> Result<()> {
if matches!(self.log_level, LogLevel::Silent) {
return Ok(());
}

if matches!(self.violations, Violations::Hide) {
if matches!(
self.format,
SerializationFormat::Text | SerializationFormat::Grouped
) {
self.post_text(diagnostics);
}
return Ok(());
}

match self.format {
SerializationFormat::Json => {
println!(
Expand Down Expand Up @@ -142,7 +180,7 @@ impl<'a> Printer<'a> {
print_message(message);
}

self.post_text(diagnostics, autofix);
self.post_text(diagnostics);
}
SerializationFormat::Grouped => {
// Group by filename.
Expand Down Expand Up @@ -182,7 +220,7 @@ impl<'a> Printer<'a> {
println!();
}

self.post_text(diagnostics, autofix);
self.post_text(diagnostics);
}
SerializationFormat::Github => {
// Generate error workflow command in GitHub Actions format
Expand Down
6 changes: 6 additions & 0 deletions src/settings/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub struct Configuration {
pub extend_select: Vec<Vec<CheckCodePrefix>>,
pub external: Option<Vec<String>>,
pub fix: Option<bool>,
pub fix_only: Option<bool>,
pub fixable: Option<Vec<CheckCodePrefix>>,
pub format: Option<SerializationFormat>,
pub force_exclude: Option<bool>,
Expand Down Expand Up @@ -107,6 +108,7 @@ impl Configuration {
extend_select: vec![options.extend_select.unwrap_or_default()],
external: options.external,
fix: options.fix,
fix_only: options.fix_only,
fixable: options.fixable,
format: options.format,
force_exclude: options.force_exclude,
Expand Down Expand Up @@ -179,6 +181,7 @@ impl Configuration {
.collect(),
external: self.external.or(config.external),
fix: self.fix.or(config.fix),
fix_only: self.fix_only.or(config.fix_only),
fixable: self.fixable.or(config.fixable),
format: self.format.or(config.format),
force_exclude: self.force_exclude.or(config.force_exclude),
Expand Down Expand Up @@ -226,6 +229,9 @@ impl Configuration {
if let Some(fix) = overrides.fix {
self.fix = Some(fix);
}
if let Some(fix_only) = overrides.fix_only {
self.fix_only = Some(fix_only);
}
if let Some(fixable) = overrides.fixable {
self.fixable = Some(fixable);
}
Expand Down
4 changes: 4 additions & 0 deletions src/settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub struct Settings {
pub extend_exclude: GlobSet,
pub external: FxHashSet<String>,
pub fix: bool,
pub fix_only: bool,
pub fixable: FxHashSet<CheckCode>,
pub format: SerializationFormat,
pub force_exclude: bool,
Expand Down Expand Up @@ -122,6 +123,7 @@ impl Settings {
extend_exclude: resolve_globset(config.extend_exclude)?,
external: FxHashSet::from_iter(config.external.unwrap_or_default()),
fix: config.fix.unwrap_or(false),
fix_only: config.fix_only.unwrap_or(false),
fixable: resolve_codes(
[CheckCodeSpec {
select: &config.fixable.unwrap_or_else(|| CATEGORIES.to_vec()),
Expand Down Expand Up @@ -202,6 +204,7 @@ impl Settings {
extend_exclude: GlobSet::empty(),
external: FxHashSet::default(),
fix: false,
fix_only: false,
fixable: FxHashSet::from_iter([check_code]),
format: SerializationFormat::Text,
force_exclude: false,
Expand Down Expand Up @@ -236,6 +239,7 @@ impl Settings {
extend_exclude: GlobSet::empty(),
external: FxHashSet::default(),
fix: false,
fix_only: false,
fixable: FxHashSet::from_iter(check_codes),
format: SerializationFormat::Text,
force_exclude: false,
Expand Down
Loading

0 comments on commit d9355c9

Please sign in to comment.