Add fix option to pyproject.toml (#639)

This commit is contained in:
Charlie Marsh 2022-11-07 09:46:21 -05:00 committed by GitHub
parent 7773e9728b
commit 07c9fc55f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 49 additions and 13 deletions

View file

@ -38,8 +38,10 @@ pub struct Cli {
#[arg(short, long)] #[arg(short, long)]
pub watch: bool, pub watch: bool,
/// Attempt to automatically fix lint errors. /// Attempt to automatically fix lint errors.
#[arg(short, long)] #[arg(long, overrides_with("no_fix"))]
pub fix: bool, fix: bool,
#[clap(long, overrides_with("fix"), hide = true)]
no_fix: bool,
/// Disable cache reads. /// Disable cache reads.
#[arg(short, long)] #[arg(short, long)]
pub no_cache: bool, pub no_cache: bool,
@ -94,6 +96,22 @@ pub struct Cli {
pub stdin_filename: Option<String>, pub stdin_filename: Option<String>,
} }
impl Cli {
// See: https://github.com/clap-rs/clap/issues/3146
pub fn fix(&self) -> Option<bool> {
resolve_bool_arg(self.fix, self.no_fix)
}
}
fn resolve_bool_arg(yes: bool, no: bool) -> Option<bool> {
match (yes, no) {
(true, false) => Some(true),
(false, true) => Some(false),
(false, false) => None,
(..) => unreachable!("Clap should make this impossible"),
}
}
/// Map the CLI settings to a `LogLevel`. /// Map the CLI settings to a `LogLevel`.
pub fn extract_log_level(cli: &Cli) -> LogLevel { pub fn extract_log_level(cli: &Cli) -> LogLevel {
if cli.silent { if cli.silent {
@ -165,7 +183,7 @@ pub fn warn_on(
} }
} }
/// Collect a list of `PatternPrefixPair` structs as a `BTreeMap`. /// Convert a list of `PatternPrefixPair` structs to `PerFileIgnore`.
pub fn collect_per_file_ignores( pub fn collect_per_file_ignores(
pairs: Vec<PatternPrefixPair>, pairs: Vec<PatternPrefixPair>,
project_root: &Option<PathBuf>, project_root: &Option<PathBuf>,

View file

@ -220,7 +220,9 @@ fn autoformat(files: &[PathBuf], settings: &Settings) -> Result<usize> {
} }
fn inner_main() -> Result<ExitCode> { fn inner_main() -> Result<ExitCode> {
// Extract command-line arguments.
let cli = Cli::parse(); let cli = Cli::parse();
let fix = cli.fix();
let log_level = extract_log_level(&cli); let log_level = extract_log_level(&cli);
set_up_logging(&log_level)?; set_up_logging(&log_level)?;
@ -239,7 +241,7 @@ fn inner_main() -> Result<ExitCode> {
None => debug!("Unable to find pyproject.toml; using default settings..."), None => debug!("Unable to find pyproject.toml; using default settings..."),
}; };
// Parse the settings from the pyproject.toml and command-line arguments. // Reconcile configuration from pyproject.toml and command-line arguments.
let exclude: Vec<FilePattern> = cli let exclude: Vec<FilePattern> = cli
.exclude .exclude
.iter() .iter()
@ -296,6 +298,9 @@ fn inner_main() -> Result<ExitCode> {
if let Some(dummy_variable_rgx) = cli.dummy_variable_rgx { if let Some(dummy_variable_rgx) = cli.dummy_variable_rgx {
configuration.dummy_variable_rgx = dummy_variable_rgx; configuration.dummy_variable_rgx = dummy_variable_rgx;
} }
if let Some(fix) = fix {
configuration.fix = fix;
}
if cli.show_settings && cli.show_files { if cli.show_settings && cli.show_files {
eprintln!("Error: specify --show-settings or show-files (not both)."); eprintln!("Error: specify --show-settings or show-files (not both).");
@ -306,6 +311,8 @@ fn inner_main() -> Result<ExitCode> {
return Ok(ExitCode::SUCCESS); return Ok(ExitCode::SUCCESS);
} }
// Extract settings for internal use.
let autofix = configuration.fix;
let settings = Settings::from_configuration(configuration); let settings = Settings::from_configuration(configuration);
if cli.show_files { if cli.show_files {
@ -318,7 +325,7 @@ fn inner_main() -> Result<ExitCode> {
let printer = Printer::new(&cli.format, &log_level); let printer = Printer::new(&cli.format, &log_level);
if cli.watch { if cli.watch {
if cli.fix { if autofix {
eprintln!("Warning: --fix is not enabled in watch mode."); eprintln!("Warning: --fix is not enabled in watch mode.");
} }
@ -381,15 +388,15 @@ fn inner_main() -> Result<ExitCode> {
let messages = if is_stdin { let messages = if is_stdin {
let filename = cli.stdin_filename.unwrap_or_else(|| "-".to_string()); let filename = cli.stdin_filename.unwrap_or_else(|| "-".to_string());
let path = Path::new(&filename); let path = Path::new(&filename);
run_once_stdin(&settings, path, cli.fix)? run_once_stdin(&settings, path, autofix)?
} else { } else {
run_once(&cli.files, &settings, !cli.no_cache, cli.fix)? run_once(&cli.files, &settings, !cli.no_cache, autofix)?
}; };
// Always try to print violations (the printer itself may suppress output), // Always try to print violations (the printer itself may suppress output),
// unless we're writing fixes via stdin (in which case, the transformed // unless we're writing fixes via stdin (in which case, the transformed
// source code goes to stdout). // source code goes to stdout).
if !(is_stdin && cli.fix) { if !(is_stdin && autofix) {
printer.write_once(&messages)?; printer.write_once(&messages)?;
} }

View file

@ -20,6 +20,7 @@ pub struct Configuration {
pub extend_exclude: Vec<FilePattern>, pub extend_exclude: Vec<FilePattern>,
pub extend_ignore: Vec<CheckCodePrefix>, pub extend_ignore: Vec<CheckCodePrefix>,
pub extend_select: Vec<CheckCodePrefix>, pub extend_select: Vec<CheckCodePrefix>,
pub fix: bool,
pub ignore: Vec<CheckCodePrefix>, pub ignore: Vec<CheckCodePrefix>,
pub line_length: usize, pub line_length: usize,
pub per_file_ignores: Vec<PerFileIgnore>, pub per_file_ignores: Vec<PerFileIgnore>,
@ -91,6 +92,7 @@ impl Configuration {
.select .select
.unwrap_or_else(|| vec![CheckCodePrefix::E, CheckCodePrefix::F]), .unwrap_or_else(|| vec![CheckCodePrefix::E, CheckCodePrefix::F]),
extend_select: options.extend_select.unwrap_or_default(), extend_select: options.extend_select.unwrap_or_default(),
fix: options.fix.unwrap_or_default(),
ignore: options.ignore.unwrap_or_default(), ignore: options.ignore.unwrap_or_default(),
line_length: options.line_length.unwrap_or(88), line_length: options.line_length.unwrap_or(88),
per_file_ignores: options per_file_ignores: options

View file

@ -11,15 +11,16 @@ use crate::{flake8_annotations, flake8_quotes, pep8_naming};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)] #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")] #[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub struct Options { pub struct Options {
pub line_length: Option<usize>, pub dummy_variable_rgx: Option<String>,
pub exclude: Option<Vec<String>>, pub exclude: Option<Vec<String>>,
pub extend_exclude: Option<Vec<String>>, pub extend_exclude: Option<Vec<String>>,
pub select: Option<Vec<CheckCodePrefix>>,
pub extend_select: Option<Vec<CheckCodePrefix>>,
pub ignore: Option<Vec<CheckCodePrefix>>,
pub extend_ignore: Option<Vec<CheckCodePrefix>>, pub extend_ignore: Option<Vec<CheckCodePrefix>>,
pub extend_select: Option<Vec<CheckCodePrefix>>,
pub fix: Option<bool>,
pub ignore: Option<Vec<CheckCodePrefix>>,
pub line_length: Option<usize>,
pub per_file_ignores: Option<BTreeMap<String, Vec<CheckCodePrefix>>>, pub per_file_ignores: Option<BTreeMap<String, Vec<CheckCodePrefix>>>,
pub dummy_variable_rgx: Option<String>, pub select: Option<Vec<CheckCodePrefix>>,
pub target_version: Option<PythonVersion>, pub target_version: Option<PythonVersion>,
// Plugins // Plugins
pub flake8_annotations: Option<flake8_annotations::settings::Options>, pub flake8_annotations: Option<flake8_annotations::settings::Options>,

View file

@ -134,6 +134,7 @@ mod tests {
Some(Tools { Some(Tools {
ruff: Some(Options { ruff: Some(Options {
line_length: None, line_length: None,
fix: None,
exclude: None, exclude: None,
extend_exclude: None, extend_exclude: None,
select: None, select: None,
@ -162,6 +163,7 @@ line-length = 79
Some(Tools { Some(Tools {
ruff: Some(Options { ruff: Some(Options {
line_length: Some(79), line_length: Some(79),
fix: None,
exclude: None, exclude: None,
extend_exclude: None, extend_exclude: None,
select: None, select: None,
@ -190,6 +192,7 @@ exclude = ["foo.py"]
Some(Tools { Some(Tools {
ruff: Some(Options { ruff: Some(Options {
line_length: None, line_length: None,
fix: None,
exclude: Some(vec!["foo.py".to_string()]), exclude: Some(vec!["foo.py".to_string()]),
extend_exclude: None, extend_exclude: None,
select: None, select: None,
@ -218,6 +221,7 @@ select = ["E501"]
Some(Tools { Some(Tools {
ruff: Some(Options { ruff: Some(Options {
line_length: None, line_length: None,
fix: None,
exclude: None, exclude: None,
extend_exclude: None, extend_exclude: None,
select: Some(vec![CheckCodePrefix::E501]), select: Some(vec![CheckCodePrefix::E501]),
@ -247,6 +251,7 @@ ignore = ["E501"]
Some(Tools { Some(Tools {
ruff: Some(Options { ruff: Some(Options {
line_length: None, line_length: None,
fix: None,
exclude: None, exclude: None,
extend_exclude: None, extend_exclude: None,
select: None, select: None,
@ -315,6 +320,7 @@ other-attribute = 1
config, config,
Options { Options {
line_length: Some(88), line_length: Some(88),
fix: None,
exclude: None, exclude: None,
extend_exclude: Some(vec![ extend_exclude: Some(vec![
"excluded.py".to_string(), "excluded.py".to_string(),

View file

@ -40,6 +40,7 @@ pub struct UserConfiguration {
pub extend_exclude: Vec<Exclusion>, pub extend_exclude: Vec<Exclusion>,
pub extend_ignore: Vec<CheckCodePrefix>, pub extend_ignore: Vec<CheckCodePrefix>,
pub extend_select: Vec<CheckCodePrefix>, pub extend_select: Vec<CheckCodePrefix>,
pub fix: bool,
pub ignore: Vec<CheckCodePrefix>, pub ignore: Vec<CheckCodePrefix>,
pub line_length: usize, pub line_length: usize,
pub per_file_ignores: Vec<(Exclusion, Vec<CheckCode>)>, pub per_file_ignores: Vec<(Exclusion, Vec<CheckCode>)>,
@ -74,6 +75,7 @@ impl UserConfiguration {
.collect(), .collect(),
extend_ignore: configuration.extend_ignore, extend_ignore: configuration.extend_ignore,
extend_select: configuration.extend_select, extend_select: configuration.extend_select,
fix: configuration.fix,
ignore: configuration.ignore, ignore: configuration.ignore,
line_length: configuration.line_length, line_length: configuration.line_length,
per_file_ignores: configuration per_file_ignores: configuration