diff --git a/Cargo.lock b/Cargo.lock index 70bfe41695..615a8c7e33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -850,6 +850,12 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + [[package]] name = "gloo-timers" version = "0.2.4" @@ -1648,6 +1654,7 @@ dependencies = [ "dirs 4.0.0", "fern", "filetime", + "glob", "log", "notify", "rayon", diff --git a/Cargo.toml b/Cargo.toml index 3764093d8c..37d85d5fd0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ common-path = { version = "1.0.0" } dirs = { version = "4.0.0" } fern = { version = "0.6.1" } filetime = { version = "0.2.17" } +glob = { version = "0.3.0"} log = { version = "0.4.17" } notify = { version = "4.0.17" } rayon = { version = "1.5.3" } diff --git a/resources/test/src/bar/__init__.py b/resources/test/src/bar/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/resources/test/src/bar/migrations/__init__.py b/resources/test/src/bar/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/resources/test/src/bar/migrations/migration.py b/resources/test/src/bar/migrations/migration.py new file mode 100644 index 0000000000..c9b6c43d41 --- /dev/null +++ b/resources/test/src/bar/migrations/migration.py @@ -0,0 +1,9 @@ +a = "abc" +b = f"ghi{'jkl'}" + +c = f"def" +d = f"def" + "ghi" +e = ( + f"def" + + "ghi" +) diff --git a/resources/test/src/foo/__init__.py b/resources/test/src/foo/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/resources/test/src/foo/migrations/__init__.py b/resources/test/src/foo/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/resources/test/src/foo/migrations/migration.py b/resources/test/src/foo/migrations/migration.py new file mode 100644 index 0000000000..c9b6c43d41 --- /dev/null +++ b/resources/test/src/foo/migrations/migration.py @@ -0,0 +1,9 @@ +a = "abc" +b = f"ghi{'jkl'}" + +c = f"def" +d = f"def" + "ghi" +e = ( + f"def" + + "ghi" +) diff --git a/resources/test/src/pyproject.toml b/resources/test/src/pyproject.toml index 56f069e36f..92a284aaeb 100644 --- a/resources/test/src/pyproject.toml +++ b/resources/test/src/pyproject.toml @@ -1,6 +1,6 @@ [tool.ruff] line-length = 88 -exclude = ["excluded.py"] +exclude = ["excluded.py", "**/migrations"] select = [ "E501", "F401", diff --git a/src/fs.rs b/src/fs.rs index 1fe591ab42..ef58d40435 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -3,21 +3,33 @@ use std::io::{BufReader, Read}; use std::path::{Path, PathBuf}; use anyhow::Result; +use glob::Pattern; use walkdir::{DirEntry, WalkDir}; fn is_not_hidden(entry: &DirEntry) -> bool { entry .file_name() .to_str() - .map(|s| entry.depth() == 0 || !s.starts_with('.')) + .map(|s| (entry.depth() == 0 || !s.starts_with('.'))) .unwrap_or(false) } -pub fn iter_python_files(path: &PathBuf) -> impl Iterator { +fn is_not_excluded(entry: &DirEntry, exclude: &[Pattern]) -> bool { + entry + .path() + .to_str() + .map(|s| !exclude.iter().any(|pattern| pattern.matches(s))) + .unwrap_or(false) +} + +pub fn iter_python_files<'a>( + path: &'a PathBuf, + exclude: &'a [Pattern], +) -> impl Iterator + 'a { WalkDir::new(path) .follow_links(true) .into_iter() - .filter_entry(is_not_hidden) + .filter_entry(|entry| is_not_hidden(entry) && is_not_excluded(entry, exclude)) .filter_map(|entry| entry.ok()) .filter(|entry| entry.path().to_string_lossy().ends_with(".py")) } diff --git a/src/main.rs b/src/main.rs index 41abc0d58d..1866607857 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,19 +51,16 @@ struct Cli { fn run_once(files: &[PathBuf], settings: &Settings, cache: bool) -> Result> { // Collect all the files to check. let start = Instant::now(); - let files: Vec = files.iter().flat_map(iter_python_files).collect(); + let files: Vec = files + .iter() + .flat_map(|path| iter_python_files(path, &settings.exclude)) + .collect(); let duration = start.elapsed(); debug!("Identified files to lint in: {:?}", duration); let start = Instant::now(); let mut messages: Vec = files .par_iter() - .filter(|entry| { - !settings - .exclude - .iter() - .any(|exclusion| entry.path().starts_with(exclusion)) - }) .map(|entry| { check_path(entry.path(), settings, &cache.into()).unwrap_or_else(|e| { error!("Failed to check {}: {e:?}", entry.path().to_string_lossy()); diff --git a/src/pyproject.rs b/src/pyproject.rs index dd8bf67894..d8c59d243e 100644 --- a/src/pyproject.rs +++ b/src/pyproject.rs @@ -225,7 +225,10 @@ other-attribute = 1 config, Config { line_length: Some(88), - exclude: Some(vec![Path::new("excluded.py").to_path_buf()]), + exclude: Some(vec![ + Path::new("excluded.py").to_path_buf(), + Path::new("**/migrations").to_path_buf() + ]), select: Some(BTreeSet::from([ CheckCode::E501, CheckCode::F401, diff --git a/src/settings.rs b/src/settings.rs index f4488b6845..37ceac1da0 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,8 +1,9 @@ use std::collections::BTreeSet; use std::hash::{Hash, Hasher}; -use std::path::{Path, PathBuf}; +use std::path::Path; use anyhow::Result; +use glob::Pattern; use crate::checks::CheckCode; use crate::pyproject::load_config; @@ -10,7 +11,7 @@ use crate::pyproject::load_config; #[derive(Debug)] pub struct Settings { pub line_length: usize, - pub exclude: Vec, + pub exclude: Vec, pub select: BTreeSet, } @@ -39,6 +40,7 @@ impl Settings { path } }) + .map(|path| Pattern::new(&path.to_string_lossy()).expect("Invalid pattern.")) .collect(), select: config.select.unwrap_or_else(|| { BTreeSet::from([