From 3f272b6cf83cf4e2eb0a6f8d9d907bf44e220fc3 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 14 Dec 2022 16:54:23 -0500 Subject: [PATCH] Enable opt-out of `.gitignore` checks via `respect-gitignore` flag (#1242) --- BREAKING_CHANGES.md | 8 +++-- README.md | 36 +++++++++++++++++-- flake8_to_ruff/src/converter.rs | 7 ++++ src/cli.rs | 13 ++++++- src/commands.rs | 64 +++++++++++++++++++++++---------- src/main.rs | 55 ++++++++++++++++++---------- src/resolver.rs | 26 +++++++++----- src/settings/configuration.rs | 6 ++++ src/settings/mod.rs | 5 +++ src/settings/options.rs | 12 +++++++ src/settings/pyproject.rs | 8 ++++- 11 files changed, 187 insertions(+), 53 deletions(-) diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index db8293ac66..6d36a69718 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -4,9 +4,11 @@ ### Files excluded by `.gitignore` are now ignored ([#1234](https://github.com/charliermarsh/ruff/pull/1234)) -Ruff will now avoid checking files that are excluded by a `.gitignore`. This behavior is powered by -the [`ignore`](https://docs.rs/ignore/latest/ignore/struct.WalkBuilder.html#ignore-rules), and is -applied in addition to Ruff's built-in `exclude` system. +Ruff will now avoid checking files that are excluded by `.ignore`, `.gitignore`, +`.git/info/exclude`, and global `gitignore` files. This behavior is powered by the [`ignore`](https://docs.rs/ignore/latest/ignore/struct.WalkBuilder.html#ignore-rules) +crate, and is applied in addition to Ruff's built-in `exclude` system. + +To disable this behavior, set `respect-gitignore = false` in your `pyproject.toml` file. Note that hidden files (i.e., files and directories prefixed with a `.`) are _not_ ignored by default. diff --git a/README.md b/README.md index 6705b6b2b6..4a2a9f2f5e 100644 --- a/README.md +++ b/README.md @@ -304,13 +304,15 @@ Options: --per-file-ignores List of mappings from file pattern to code to exclude --format - Output serialization format for error messages [default: text] [possible values: text, json, junit, grouped] + Output serialization format for error messages [possible values: text, json, junit, grouped, github] --show-source Show violations with source code + --respect-gitignore + Respect file exclusions via `.gitignore` and other standard ignore files --show-files See the files Ruff will be run against with the current settings --show-settings - See Ruff's settings + See the settings Ruff will use to check a given Python file --add-noqa Enable automatic additions of noqa directives to failing lines --dummy-variable-rgx @@ -365,6 +367,18 @@ extend = "../pyproject.toml" line-length = 100 ``` +### Python file discovery + +When passed a path on the command-line, Ruff will automatically discover all Python files in that +path, taking into account the [`exclude`](#exclude) and [`extend-exclude`](#extend-exclude) settings +in each directory's `pyproject.toml` file. + +By default, Ruff will also skip any files that are omitted via `.ignore`, `.gitignore`, +`.git/info/exclude`, and global `gitignore` files (see: [`respect-gitignore`](#respect-gitignore)). + +Files that are passed to `ruff` directly are always checked, regardless of the above criteria. +For example, `ruff /path/to/excluded/file.py` will always check `file.py`. + ### Ignoring errors To omit a lint check entirely, add it to the "ignore" list via [`ignore`](#ignore) or @@ -1707,6 +1721,24 @@ any matching files. --- +#### [`respect-gitignore`](#respect-gitignore) + +Whether to automatically exclude files that are ignored by `.ignore`, `.gitignore`, +`.git/info/exclude`, and global `gitignore` files. Enabled by default. + +**Default value**: `true` + +**Type**: `bool` + +**Example usage**: + +```toml +[tool.ruff] +respect_gitignore = false +``` + +--- + #### [`select`](#select) A list of check code prefixes to enable. Prefixes can specify exact checks (like diff --git a/flake8_to_ruff/src/converter.rs b/flake8_to_ruff/src/converter.rs index cd5d967b38..d482453c2e 100644 --- a/flake8_to_ruff/src/converter.rs +++ b/flake8_to_ruff/src/converter.rs @@ -258,6 +258,7 @@ mod tests { ignore_init_module_imports: None, line_length: None, per_file_ignores: None, + respect_gitignore: None, select: Some(vec![ CheckCodePrefix::E, CheckCodePrefix::F, @@ -304,6 +305,7 @@ mod tests { ignore_init_module_imports: None, line_length: Some(100), per_file_ignores: None, + respect_gitignore: None, select: Some(vec![ CheckCodePrefix::E, CheckCodePrefix::F, @@ -350,6 +352,7 @@ mod tests { ignore_init_module_imports: None, line_length: Some(100), per_file_ignores: None, + respect_gitignore: None, select: Some(vec![ CheckCodePrefix::E, CheckCodePrefix::F, @@ -396,6 +399,7 @@ mod tests { ignore_init_module_imports: None, line_length: None, per_file_ignores: None, + respect_gitignore: None, select: Some(vec![ CheckCodePrefix::E, CheckCodePrefix::F, @@ -442,6 +446,7 @@ mod tests { ignore_init_module_imports: None, line_length: None, per_file_ignores: None, + respect_gitignore: None, select: Some(vec![ CheckCodePrefix::E, CheckCodePrefix::F, @@ -496,6 +501,7 @@ mod tests { ignore_init_module_imports: None, line_length: None, per_file_ignores: None, + respect_gitignore: None, select: Some(vec![ CheckCodePrefix::D100, CheckCodePrefix::D101, @@ -578,6 +584,7 @@ mod tests { ignore_init_module_imports: None, line_length: None, per_file_ignores: None, + respect_gitignore: None, select: Some(vec![ CheckCodePrefix::E, CheckCodePrefix::F, diff --git a/src/cli.rs b/src/cli.rs index 1ff094d2ba..9d69d87bfb 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -86,10 +86,16 @@ pub struct Cli { show_source: bool, #[clap(long, overrides_with("show_source"), hide = true)] no_show_source: bool, + /// Respect file exclusions via `.gitignore` and other standard ignore + /// files. + #[arg(long, overrides_with("no_respect_gitignore"))] + respect_gitignore: bool, + #[clap(long, overrides_with("respect_gitignore"), hide = true)] + no_respect_gitignore: bool, /// See the files Ruff will be run against with the current settings. #[arg(long)] pub show_files: bool, - /// See the settings Ruff used for the first matching file. + /// See the settings Ruff will use to check a given Python file. #[arg(long)] pub show_settings: bool, /// Enable automatic additions of noqa directives to failing lines. @@ -156,6 +162,10 @@ impl Cli { line_length: self.line_length, max_complexity: self.max_complexity, per_file_ignores: self.per_file_ignores, + respect_gitignore: resolve_bool_arg( + self.respect_gitignore, + self.no_respect_gitignore, + ), select: self.select, show_source: resolve_bool_arg(self.show_source, self.no_show_source), target_version: self.target_version, @@ -212,6 +222,7 @@ pub struct Overrides { pub line_length: Option, pub max_complexity: Option, pub per_file_ignores: Option>, + pub respect_gitignore: Option, pub select: Option>, pub show_source: Option, pub target_version: Option, diff --git a/src/commands.rs b/src/commands.rs index f7ab914577..513d88fa25 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -18,20 +18,22 @@ use crate::iterators::par_iter; use crate::linter::{add_noqa_to_path, autoformat_path, lint_path, lint_stdin, Diagnostics}; use crate::message::Message; use crate::resolver; -use crate::resolver::Strategy; +use crate::resolver::{FileDiscovery, PyprojectDiscovery}; use crate::settings::types::SerializationFormat; /// Run the linter over a collection of files. pub fn run( files: &[PathBuf], - strategy: &Strategy, + pyproject_strategy: &PyprojectDiscovery, + file_strategy: &FileDiscovery, overrides: &Overrides, cache: bool, autofix: &fixer::Mode, ) -> Result { // Collect all the files to check. let start = Instant::now(); - let (paths, resolver) = resolver::python_files_in_path(files, strategy, overrides)?; + let (paths, resolver) = + resolver::python_files_in_path(files, pyproject_strategy, file_strategy, overrides)?; let duration = start.elapsed(); debug!("Identified files to lint in: {:?}", duration); @@ -41,7 +43,7 @@ pub fn run( match entry { Ok(entry) => { let path = entry.path(); - let settings = resolver.resolve(path, strategy); + let settings = resolver.resolve(path, pyproject_strategy); lint_path(path, settings, &cache.into(), autofix) .map_err(|e| (Some(path.to_owned()), e.to_string())) } @@ -57,7 +59,7 @@ pub fn run( } .unwrap_or_else(|(path, message)| { if let Some(path) = &path { - let settings = resolver.resolve(path, strategy); + let settings = resolver.resolve(path, pyproject_strategy); if settings.enabled.contains(&CheckCode::E902) { Diagnostics::new(vec![Message { kind: CheckKind::IOError(message), @@ -98,14 +100,14 @@ fn read_from_stdin() -> Result { /// Run the linter over a single file, read from `stdin`. pub fn run_stdin( - strategy: &Strategy, + strategy: &PyprojectDiscovery, filename: &Path, autofix: &fixer::Mode, ) -> Result { let stdin = read_from_stdin()?; let settings = match strategy { - Strategy::Fixed(settings) => settings, - Strategy::Hierarchical(settings) => settings, + PyprojectDiscovery::Fixed(settings) => settings, + PyprojectDiscovery::Hierarchical(settings) => settings, }; let mut diagnostics = lint_stdin(filename, &stdin, settings, autofix)?; diagnostics.messages.sort_unstable(); @@ -113,10 +115,16 @@ pub fn run_stdin( } /// Add `noqa` directives to a collection of files. -pub fn add_noqa(files: &[PathBuf], strategy: &Strategy, overrides: &Overrides) -> Result { +pub fn add_noqa( + files: &[PathBuf], + pyproject_strategy: &PyprojectDiscovery, + file_strategy: &FileDiscovery, + overrides: &Overrides, +) -> Result { // Collect all the files to check. let start = Instant::now(); - let (paths, resolver) = resolver::python_files_in_path(files, strategy, overrides)?; + let (paths, resolver) = + resolver::python_files_in_path(files, pyproject_strategy, file_strategy, overrides)?; let duration = start.elapsed(); debug!("Identified files to lint in: {:?}", duration); @@ -125,7 +133,7 @@ pub fn add_noqa(files: &[PathBuf], strategy: &Strategy, overrides: &Overrides) - .flatten() .filter_map(|entry| { let path = entry.path(); - let settings = resolver.resolve(path, strategy); + let settings = resolver.resolve(path, pyproject_strategy); match add_noqa_to_path(path, settings) { Ok(count) => Some(count), Err(e) => { @@ -143,10 +151,16 @@ pub fn add_noqa(files: &[PathBuf], strategy: &Strategy, overrides: &Overrides) - } /// Automatically format a collection of files. -pub fn autoformat(files: &[PathBuf], strategy: &Strategy, overrides: &Overrides) -> Result { +pub fn autoformat( + files: &[PathBuf], + pyproject_strategy: &PyprojectDiscovery, + file_strategy: &FileDiscovery, + overrides: &Overrides, +) -> Result { // Collect all the files to format. let start = Instant::now(); - let (paths, resolver) = resolver::python_files_in_path(files, strategy, overrides)?; + let (paths, resolver) = + resolver::python_files_in_path(files, pyproject_strategy, file_strategy, overrides)?; let duration = start.elapsed(); debug!("Identified files to lint in: {:?}", duration); @@ -155,7 +169,7 @@ pub fn autoformat(files: &[PathBuf], strategy: &Strategy, overrides: &Overrides) .flatten() .filter_map(|entry| { let path = entry.path(); - let settings = resolver.resolve(path, strategy); + let settings = resolver.resolve(path, pyproject_strategy); match autoformat_path(path, settings) { Ok(()) => Some(()), Err(e) => { @@ -173,9 +187,15 @@ pub fn autoformat(files: &[PathBuf], strategy: &Strategy, overrides: &Overrides) } /// Print the user-facing configuration settings. -pub fn show_settings(files: &[PathBuf], strategy: &Strategy, overrides: &Overrides) -> Result<()> { +pub fn show_settings( + files: &[PathBuf], + pyproject_strategy: &PyprojectDiscovery, + file_strategy: &FileDiscovery, + overrides: &Overrides, +) -> Result<()> { // Collect all files in the hierarchy. - let (paths, resolver) = resolver::python_files_in_path(files, strategy, overrides)?; + let (paths, resolver) = + resolver::python_files_in_path(files, pyproject_strategy, file_strategy, overrides)?; // Print the list of files. let Some(entry) = paths @@ -185,7 +205,7 @@ pub fn show_settings(files: &[PathBuf], strategy: &Strategy, overrides: &Overrid bail!("No files found under the given path"); }; let path = entry.path(); - let settings = resolver.resolve(path, strategy); + let settings = resolver.resolve(path, pyproject_strategy); println!("Resolved settings for: {path:?}"); println!("{settings:#?}"); @@ -193,9 +213,15 @@ pub fn show_settings(files: &[PathBuf], strategy: &Strategy, overrides: &Overrid } /// Show the list of files to be checked based on current settings. -pub fn show_files(files: &[PathBuf], strategy: &Strategy, overrides: &Overrides) -> Result<()> { +pub fn show_files( + files: &[PathBuf], + pyproject_strategy: &PyprojectDiscovery, + file_strategy: &FileDiscovery, + overrides: &Overrides, +) -> Result<()> { // Collect all files in the hierarchy. - let (paths, _resolver) = resolver::python_files_in_path(files, strategy, overrides)?; + let (paths, _resolver) = + resolver::python_files_in_path(files, pyproject_strategy, file_strategy, overrides)?; // Print the list of files. for entry in paths diff --git a/src/main.rs b/src/main.rs index 4546f81d63..51e45fa83d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,7 +20,7 @@ use ::ruff::autofix::fixer; use ::ruff::cli::{extract_log_level, Cli}; use ::ruff::logging::{set_up_logging, LogLevel}; use ::ruff::printer::Printer; -use ::ruff::resolver::Strategy; +use ::ruff::resolver::PyprojectDiscovery; use ::ruff::settings::configuration::Configuration; use ::ruff::settings::types::SerializationFormat; use ::ruff::settings::{pyproject, Settings}; @@ -33,31 +33,31 @@ use colored::Colorize; use notify::{recommended_watcher, RecursiveMode, Watcher}; use path_absolutize::path_dedot; use ruff::cli::Overrides; -use ruff::resolver::{resolve_settings, Relativity}; +use ruff::resolver::{resolve_settings, FileDiscovery, Relativity}; /// Resolve the relevant settings strategy and defaults for the current /// invocation. -fn resolve(config: Option, overrides: &Overrides) -> Result { +fn resolve(config: Option, overrides: &Overrides) -> Result { if let Some(pyproject) = config { // First priority: the user specified a `pyproject.toml` file. Use that // `pyproject.toml` for _all_ configuration, and resolve paths relative to the // current working directory. (This matches ESLint's behavior.) let settings = resolve_settings(&pyproject, &Relativity::Cwd, Some(overrides))?; - Ok(Strategy::Fixed(settings)) + Ok(PyprojectDiscovery::Fixed(settings)) } else if let Some(pyproject) = pyproject::find_pyproject_toml(path_dedot::CWD.as_path()) { // Second priority: find a `pyproject.toml` file in the current working path, // and resolve all paths relative to that directory. (With // `Strategy::Hierarchical`, we'll end up finding the "closest" `pyproject.toml` // file for every Python file later on, so these act as the "default" settings.) let settings = resolve_settings(&pyproject, &Relativity::Parent, Some(overrides))?; - Ok(Strategy::Hierarchical(settings)) + Ok(PyprojectDiscovery::Hierarchical(settings)) } else if let Some(pyproject) = pyproject::find_user_pyproject_toml() { // Third priority: find a user-specific `pyproject.toml`, but resolve all paths // relative the current working directory. (With `Strategy::Hierarchical`, we'll // end up the "closest" `pyproject.toml` file for every Python file later on, so // these act as the "default" settings.) let settings = resolve_settings(&pyproject, &Relativity::Cwd, Some(overrides))?; - Ok(Strategy::Hierarchical(settings)) + Ok(PyprojectDiscovery::Hierarchical(settings)) } else { // Fallback: load Ruff's default settings, and resolve all paths relative to the // current working directory. (With `Strategy::Hierarchical`, we'll end up the @@ -67,7 +67,7 @@ fn resolve(config: Option, overrides: &Overrides) -> Result { // Apply command-line options that override defaults. config.apply(overrides.clone()); let settings = Settings::from_configuration(config, &path_dedot::CWD)?; - Ok(Strategy::Hierarchical(settings)) + Ok(PyprojectDiscovery::Hierarchical(settings)) } } @@ -87,13 +87,19 @@ fn inner_main() -> Result { // Construct the "default" settings. These are used when no `pyproject.toml` // files are present, or files are injected from outside of the hierarchy. - let strategy = resolve(cli.config, &overrides)?; + let pyproject_strategy = resolve(cli.config, &overrides)?; // Extract options that are included in `Settings`, but only apply at the top // level. - let (fix, format) = match &strategy { - Strategy::Fixed(settings) => (settings.fix, settings.format), - Strategy::Hierarchical(settings) => (settings.fix, settings.format), + let file_strategy = FileDiscovery { + respect_gitignore: match &pyproject_strategy { + PyprojectDiscovery::Fixed(settings) => settings.respect_gitignore, + 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 autofix = if fix { fixer::Mode::Apply @@ -108,11 +114,11 @@ fn inner_main() -> Result { return Ok(ExitCode::SUCCESS); } if cli.show_settings { - commands::show_settings(&cli.files, &strategy, &overrides)?; + commands::show_settings(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?; return Ok(ExitCode::SUCCESS); } if cli.show_files { - commands::show_files(&cli.files, &strategy, &overrides)?; + commands::show_files(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?; return Ok(ExitCode::SUCCESS); } @@ -144,7 +150,8 @@ fn inner_main() -> Result { let messages = commands::run( &cli.files, - &strategy, + &pyproject_strategy, + &file_strategy, &overrides, cache_enabled, &fixer::Mode::None, @@ -173,7 +180,8 @@ fn inner_main() -> Result { let messages = commands::run( &cli.files, - &strategy, + &pyproject_strategy, + &file_strategy, &overrides, cache_enabled, &fixer::Mode::None, @@ -185,12 +193,14 @@ fn inner_main() -> Result { } } } else if cli.add_noqa { - let modifications = commands::add_noqa(&cli.files, &strategy, &overrides)?; + let modifications = + commands::add_noqa(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?; if modifications > 0 && log_level >= LogLevel::Default { println!("Added {modifications} noqa directives."); } } else if cli.autoformat { - let modifications = commands::autoformat(&cli.files, &strategy, &overrides)?; + let modifications = + commands::autoformat(&cli.files, &pyproject_strategy, &file_strategy, &overrides)?; if modifications > 0 && log_level >= LogLevel::Default { println!("Formatted {modifications} files."); } @@ -201,9 +211,16 @@ fn inner_main() -> Result { let diagnostics = if is_stdin { let filename = cli.stdin_filename.unwrap_or_else(|| "-".to_string()); let path = Path::new(&filename); - commands::run_stdin(&strategy, path, &autofix)? + commands::run_stdin(&pyproject_strategy, path, &autofix)? } else { - commands::run(&cli.files, &strategy, &overrides, cache_enabled, &autofix)? + commands::run( + &cli.files, + &pyproject_strategy, + &file_strategy, + &overrides, + cache_enabled, + &autofix, + )? }; // Always try to print violations (the printer itself may suppress output), diff --git a/src/resolver.rs b/src/resolver.rs index 7c1eba9d12..bbb45183d7 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -16,9 +16,16 @@ use crate::fs; use crate::settings::configuration::Configuration; use crate::settings::{pyproject, Settings}; -/// The strategy for discovering a `pyproject.toml` file for each Python file. +/// The strategy used to discover Python files in the filesystem.. #[derive(Debug)] -pub enum Strategy { +pub struct FileDiscovery { + pub respect_gitignore: bool, +} + +/// The strategy used to discover the relevant `pyproject.toml` file for each +/// Python file. +#[derive(Debug)] +pub enum PyprojectDiscovery { /// Use a fixed `pyproject.toml` file for all Python files (i.e., one /// provided on the command-line). Fixed(Settings), @@ -65,10 +72,10 @@ impl Resolver { } /// Return the appropriate `Settings` for a given `Path`. - pub fn resolve<'a>(&'a self, path: &Path, strategy: &'a Strategy) -> &'a Settings { + pub fn resolve<'a>(&'a self, path: &Path, strategy: &'a PyprojectDiscovery) -> &'a Settings { match strategy { - Strategy::Fixed(settings) => settings, - Strategy::Hierarchical(default) => self + PyprojectDiscovery::Fixed(settings) => settings, + PyprojectDiscovery::Hierarchical(default) => self .settings .iter() .rev() @@ -181,7 +188,8 @@ fn is_python_entry(entry: &DirEntry) -> bool { /// Find all Python (`.py` and `.pyi` files) in a set of paths. pub fn python_files_in_path( paths: &[PathBuf], - strategy: &Strategy, + pyproject_strategy: &PyprojectDiscovery, + file_strategy: &FileDiscovery, overrides: &Overrides, ) -> Result<(Vec>, Resolver)> { // Normalize every path (e.g., convert from relative to absolute). @@ -209,7 +217,9 @@ pub fn python_files_in_path( for path in &paths[1..] { builder.add(path); } - let walker = builder.hidden(false).build_parallel(); + builder.standard_filters(file_strategy.respect_gitignore); + builder.hidden(false); + let walker = builder.build_parallel(); // Run the `WalkParallel` to collect all Python files. let error: std::sync::Mutex> = std::sync::Mutex::new(Ok(())); @@ -247,7 +257,7 @@ pub fn python_files_in_path( if entry.depth() > 0 { let path = entry.path(); let resolver = resolver.read().unwrap(); - let settings = resolver.resolve(path, strategy); + let settings = resolver.resolve(path, pyproject_strategy); match fs::extract_path_names(path) { Ok((file_path, file_basename)) => { if !settings.exclude.is_empty() diff --git a/src/settings/configuration.rs b/src/settings/configuration.rs index c32959c8c7..bc05b7b1dc 100644 --- a/src/settings/configuration.rs +++ b/src/settings/configuration.rs @@ -35,6 +35,7 @@ pub struct Configuration { pub ignore_init_module_imports: Option, pub line_length: Option, pub per_file_ignores: Option>, + pub respect_gitignore: Option, pub select: Option>, pub show_source: Option, pub src: Option>, @@ -109,6 +110,7 @@ impl Configuration { }) .collect() }), + respect_gitignore: options.respect_gitignore, show_source: options.show_source, // Plugins flake8_annotations: options.flake8_annotations, @@ -129,6 +131,7 @@ impl Configuration { allowed_confusables: self.allowed_confusables.or(config.allowed_confusables), dummy_variable_rgx: self.dummy_variable_rgx.or(config.dummy_variable_rgx), exclude: self.exclude.or(config.exclude), + respect_gitignore: self.respect_gitignore.or(config.respect_gitignore), extend: self.extend.or(config.extend), extend_exclude: self.extend_exclude.or(config.extend_exclude), extend_ignore: self.extend_ignore.or(config.extend_ignore), @@ -202,6 +205,9 @@ impl Configuration { if let Some(per_file_ignores) = overrides.per_file_ignores { self.per_file_ignores = Some(collect_per_file_ignores(per_file_ignores)); } + if let Some(respect_gitignore) = overrides.respect_gitignore { + self.respect_gitignore = Some(respect_gitignore); + } if let Some(select) = overrides.select { self.select = Some(select); } diff --git a/src/settings/mod.rs b/src/settings/mod.rs index dd227bfb13..3cf70fe825 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -29,6 +29,7 @@ pub mod pyproject; pub mod types; #[derive(Debug)] +#[allow(clippy::struct_excessive_bools)] pub struct Settings { pub allowed_confusables: FxHashSet, pub dummy_variable_rgx: Regex, @@ -42,6 +43,7 @@ pub struct Settings { pub ignore_init_module_imports: bool, pub line_length: usize, pub per_file_ignores: Vec<(GlobMatcher, GlobMatcher, FxHashSet)>, + pub respect_gitignore: bool, pub show_source: bool, pub src: Vec, pub target_version: PythonVersion, @@ -122,6 +124,7 @@ impl Settings { per_file_ignores: resolve_per_file_ignores( config.per_file_ignores.unwrap_or_default(), )?, + respect_gitignore: config.respect_gitignore.unwrap_or(true), src: config .src .unwrap_or_else(|| vec![project_root.to_path_buf()]), @@ -183,6 +186,7 @@ impl Settings { ignore_init_module_imports: false, line_length: 88, per_file_ignores: vec![], + respect_gitignore: true, show_source: false, src: vec![path_dedot::CWD.clone()], target_version: PythonVersion::Py310, @@ -212,6 +216,7 @@ impl Settings { ignore_init_module_imports: false, line_length: 88, per_file_ignores: vec![], + respect_gitignore: true, show_source: false, src: vec![path_dedot::CWD.clone()], target_version: PythonVersion::Py310, diff --git a/src/settings/options.rs b/src/settings/options.rs index 8e1bdd25a8..99845e732f 100644 --- a/src/settings/options.rs +++ b/src/settings/options.rs @@ -205,6 +205,18 @@ pub struct Options { "# )] pub line_length: Option, + #[option( + doc = r#" + Whether to automatically exclude files that are ignored by `.ignore`, `.gitignore`, + `.git/info/exclude`, and global `gitignore` files. Enabled by default. + "#, + default = "true", + value_type = "bool", + example = r#" + respect_gitignore = false + "# + )] + pub respect_gitignore: Option, #[option( doc = r#" A list of check code prefixes to enable. Prefixes can specify exact checks (like diff --git a/src/settings/pyproject.rs b/src/settings/pyproject.rs index b74c02aafc..ed5c748685 100644 --- a/src/settings/pyproject.rs +++ b/src/settings/pyproject.rs @@ -119,6 +119,7 @@ mod tests { ignore_init_module_imports: None, line_length: None, per_file_ignores: None, + respect_gitignore: None, select: None, show_source: None, src: None, @@ -163,6 +164,7 @@ line-length = 79 ignore_init_module_imports: None, line_length: Some(79), per_file_ignores: None, + respect_gitignore: None, select: None, show_source: None, src: None, @@ -209,6 +211,7 @@ exclude = ["foo.py"] format: None, unfixable: None, per_file_ignores: None, + respect_gitignore: None, dummy_variable_rgx: None, src: None, target_version: None, @@ -251,6 +254,7 @@ select = ["E501"] ignore_init_module_imports: None, line_length: None, per_file_ignores: None, + respect_gitignore: None, select: Some(vec![CheckCodePrefix::E501]), show_source: None, src: None, @@ -296,6 +300,7 @@ ignore = ["E501"] ignore_init_module_imports: None, line_length: None, per_file_ignores: None, + respect_gitignore: None, select: None, show_source: None, src: None, @@ -383,8 +388,9 @@ other-attribute = 1 per_file_ignores: Some(FxHashMap::from_iter([( "__init__.py".to_string(), vec![CheckCodePrefix::F401] - ),])), + )])), dummy_variable_rgx: None, + respect_gitignore: None, src: None, target_version: None, show_source: None,