mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:25:17 +00:00
Enable excludes (#18)
This commit is contained in:
parent
7359e862c1
commit
b11a7eefa3
10 changed files with 140 additions and 83 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1546,7 +1546,6 @@ dependencies = [
|
||||||
"common-path",
|
"common-path",
|
||||||
"dirs 4.0.0",
|
"dirs 4.0.0",
|
||||||
"fern",
|
"fern",
|
||||||
"lazy_static",
|
|
||||||
"log",
|
"log",
|
||||||
"notify",
|
"notify",
|
||||||
"pyo3",
|
"pyo3",
|
||||||
|
|
|
@ -18,7 +18,6 @@ colored = { version = "2.0.0" }
|
||||||
common-path = "1.0.0"
|
common-path = "1.0.0"
|
||||||
dirs = "4.0.0"
|
dirs = "4.0.0"
|
||||||
fern = { version = "0.6.1" }
|
fern = { version = "0.6.1" }
|
||||||
lazy_static = { version = "1.4.0" }
|
|
||||||
log = { version = "0.4.17" }
|
log = { version = "0.4.17" }
|
||||||
notify = { version = "4.0.17" }
|
notify = { version = "4.0.17" }
|
||||||
pyo3 = { version = "0.16.5", features = ["extension-module", "abi3-py37"] }
|
pyo3 = { version = "0.16.5", features = ["extension-module", "abi3-py37"] }
|
||||||
|
|
61
README.md
61
README.md
|
@ -5,11 +5,10 @@ in Rust.
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
|
|
||||||
- Python 3.8 compatibility
|
- Python 3.9 compatibility
|
||||||
- [ESLint](https://eslint.org/docs/latest/user-guide/command-line-interface#caching)-inspired
|
- [ESLint](https://eslint.org/docs/latest/user-guide/command-line-interface#caching)-inspired cache semantics
|
||||||
cache semantics
|
- [TypeScript](https://www.typescriptlang.org/docs/handbook/configuring-watch.html)-inspired `--watch` semantics
|
||||||
- [TypeScript](https://www.typescriptlang.org/docs/handbook/configuring-watch.html)
|
- `pyproject.toml` support
|
||||||
-inspired `--watch` semantics
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
@ -66,6 +65,58 @@ support pattern matching, which was introduced in v3.10.
|
||||||
git clone --branch 3.9 https://github.com/python/cpython.git resources/test/cpython
|
git clone --branch 3.9 https://github.com/python/cpython.git resources/test/cpython
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Add this `pyproject.toml` to the directory:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[tool.linter]
|
||||||
|
line-length = 88
|
||||||
|
exclude = [
|
||||||
|
"Lib/ctypes/test/test_numbers.py",
|
||||||
|
"Lib/dataclasses.py",
|
||||||
|
"Lib/lib2to3/tests/data/bom.py",
|
||||||
|
"Lib/lib2to3/tests/data/crlf.py",
|
||||||
|
"Lib/lib2to3/tests/data/different_encoding.py",
|
||||||
|
"Lib/lib2to3/tests/data/false_encoding.py",
|
||||||
|
"Lib/lib2to3/tests/data/py2_test_grammar.py",
|
||||||
|
"Lib/sqlite3/test/factory.py",
|
||||||
|
"Lib/sqlite3/test/hooks.py",
|
||||||
|
"Lib/sqlite3/test/regression.py",
|
||||||
|
"Lib/sqlite3/test/transactions.py",
|
||||||
|
"Lib/sqlite3/test/types.py",
|
||||||
|
"Lib/test/bad_coding2.py",
|
||||||
|
"Lib/test/badsyntax_3131.py",
|
||||||
|
"Lib/test/badsyntax_pep3120.py",
|
||||||
|
"Lib/test/encoded_modules/module_iso_8859_1.py",
|
||||||
|
"Lib/test/encoded_modules/module_koi8_r.py",
|
||||||
|
"Lib/test/sortperf.py",
|
||||||
|
"Lib/test/test_email/torture_test.py",
|
||||||
|
"Lib/test/test_fstring.py",
|
||||||
|
"Lib/test/test_genericpath.py",
|
||||||
|
"Lib/test/test_getopt.py",
|
||||||
|
"Lib/test/test_htmlparser.py",
|
||||||
|
"Lib/test/test_importlib/stubs.py",
|
||||||
|
"Lib/test/test_importlib/test_files.py",
|
||||||
|
"Lib/test/test_importlib/test_metadata_api.py",
|
||||||
|
"Lib/test/test_importlib/test_open.py",
|
||||||
|
"Lib/test/test_importlib/test_util.py",
|
||||||
|
"Lib/test/test_named_expressions.py",
|
||||||
|
"Lib/test/test_peg_generator/__main__.py",
|
||||||
|
"Lib/test/test_pipes.py",
|
||||||
|
"Lib/test/test_source_encoding.py",
|
||||||
|
"Lib/test/test_weakref.py",
|
||||||
|
"Lib/test/test_webbrowser.py",
|
||||||
|
"Lib/tkinter/__main__.py",
|
||||||
|
"Lib/tkinter/test/test_tkinter/test_variables.py",
|
||||||
|
"Modules/_decimal/libmpdec/literature/fnt.py",
|
||||||
|
"Modules/_decimal/tests/deccheck.py",
|
||||||
|
"Tools/i18n/pygettext.py",
|
||||||
|
"Tools/test2to3/maintest.py",
|
||||||
|
"Tools/test2to3/setup.py",
|
||||||
|
"Tools/test2to3/test/test_foo.py",
|
||||||
|
"Tools/test2to3/test2to3/hello.py",
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
Next, to benchmark the release build:
|
Next, to benchmark the release build:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
|
9
resources/test/src/excluded.py
Normal file
9
resources/test/src/excluded.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
a = "abc"
|
||||||
|
b = f"ghi{'jkl'}"
|
||||||
|
|
||||||
|
c = f"def"
|
||||||
|
d = f"def" + "ghi"
|
||||||
|
e = (
|
||||||
|
f"def" +
|
||||||
|
"ghi"
|
||||||
|
)
|
|
@ -1,2 +1,3 @@
|
||||||
[tool.linter]
|
[tool.linter]
|
||||||
line-length = 88
|
line-length = 88
|
||||||
|
exclude = ["excluded.py"]
|
||||||
|
|
|
@ -41,6 +41,12 @@ fn run_once(files: &[PathBuf], settings: &Settings, cache: bool) -> Result<Vec<M
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let messages: Vec<Message> = files
|
let messages: Vec<Message> = files
|
||||||
.par_iter()
|
.par_iter()
|
||||||
|
.filter(|entry| {
|
||||||
|
!settings
|
||||||
|
.exclude
|
||||||
|
.iter()
|
||||||
|
.any(|exclusion| entry.path().starts_with(exclusion))
|
||||||
|
})
|
||||||
.map(|entry| {
|
.map(|entry| {
|
||||||
check_path(entry.path(), settings, &cache.into()).unwrap_or_else(|e| {
|
check_path(entry.path(), settings, &cache.into()).unwrap_or_else(|e| {
|
||||||
error!("Failed to check {}: {e:?}", entry.path().to_string_lossy());
|
error!("Failed to check {}: {e:?}", entry.path().to_string_lossy());
|
||||||
|
|
55
src/fs.rs
55
src/fs.rs
|
@ -1,64 +1,10 @@
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufRead, BufReader, Read};
|
use std::io::{BufRead, BufReader, Read};
|
||||||
use std::ops::Deref;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use walkdir::{DirEntry, WalkDir};
|
use walkdir::{DirEntry, WalkDir};
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
// TODO(charlie): Make these configurable.
|
|
||||||
static ref EXCLUDES: HashSet<&'static str> = vec![
|
|
||||||
"resources/test/cpython/Lib/ctypes/test/test_numbers.py",
|
|
||||||
"resources/test/cpython/Lib/dataclasses.py",
|
|
||||||
"resources/test/cpython/Lib/lib2to3/tests/data/bom.py",
|
|
||||||
"resources/test/cpython/Lib/lib2to3/tests/data/crlf.py",
|
|
||||||
"resources/test/cpython/Lib/lib2to3/tests/data/different_encoding.py",
|
|
||||||
"resources/test/cpython/Lib/lib2to3/tests/data/false_encoding.py",
|
|
||||||
"resources/test/cpython/Lib/lib2to3/tests/data/py2_test_grammar.py",
|
|
||||||
"resources/test/cpython/Lib/sqlite3/test/factory.py",
|
|
||||||
"resources/test/cpython/Lib/sqlite3/test/hooks.py",
|
|
||||||
"resources/test/cpython/Lib/sqlite3/test/regression.py",
|
|
||||||
"resources/test/cpython/Lib/sqlite3/test/transactions.py",
|
|
||||||
"resources/test/cpython/Lib/sqlite3/test/types.py",
|
|
||||||
"resources/test/cpython/Lib/test/bad_coding2.py",
|
|
||||||
"resources/test/cpython/Lib/test/badsyntax_3131.py",
|
|
||||||
"resources/test/cpython/Lib/test/badsyntax_pep3120.py",
|
|
||||||
"resources/test/cpython/Lib/test/encoded_modules/module_iso_8859_1.py",
|
|
||||||
"resources/test/cpython/Lib/test/encoded_modules/module_koi8_r.py",
|
|
||||||
"resources/test/cpython/Lib/test/sortperf.py",
|
|
||||||
"resources/test/cpython/Lib/test/test_email/torture_test.py",
|
|
||||||
"resources/test/cpython/Lib/test/test_fstring.py",
|
|
||||||
"resources/test/cpython/Lib/test/test_genericpath.py",
|
|
||||||
"resources/test/cpython/Lib/test/test_getopt.py",
|
|
||||||
"resources/test/cpython/Lib/test/test_htmlparser.py",
|
|
||||||
"resources/test/cpython/Lib/test/test_importlib/stubs.py",
|
|
||||||
"resources/test/cpython/Lib/test/test_importlib/test_files.py",
|
|
||||||
"resources/test/cpython/Lib/test/test_importlib/test_metadata_api.py",
|
|
||||||
"resources/test/cpython/Lib/test/test_importlib/test_open.py",
|
|
||||||
"resources/test/cpython/Lib/test/test_importlib/test_util.py",
|
|
||||||
"resources/test/cpython/Lib/test/test_named_expressions.py",
|
|
||||||
"resources/test/cpython/Lib/test/test_peg_generator/__main__.py",
|
|
||||||
"resources/test/cpython/Lib/test/test_pipes.py",
|
|
||||||
"resources/test/cpython/Lib/test/test_source_encoding.py",
|
|
||||||
"resources/test/cpython/Lib/test/test_weakref.py",
|
|
||||||
"resources/test/cpython/Lib/test/test_webbrowser.py",
|
|
||||||
"resources/test/cpython/Lib/tkinter/__main__.py",
|
|
||||||
"resources/test/cpython/Lib/tkinter/test/test_tkinter/test_variables.py",
|
|
||||||
"resources/test/cpython/Modules/_decimal/libmpdec/literature/fnt.py",
|
|
||||||
"resources/test/cpython/Modules/_decimal/tests/deccheck.py",
|
|
||||||
"resources/test/cpython/Tools/i18n/pygettext.py",
|
|
||||||
"resources/test/cpython/Tools/test2to3/maintest.py",
|
|
||||||
"resources/test/cpython/Tools/test2to3/setup.py",
|
|
||||||
"resources/test/cpython/Tools/test2to3/test/test_foo.py",
|
|
||||||
"resources/test/cpython/Tools/test2to3/test2to3/hello.py",
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_not_hidden(entry: &DirEntry) -> bool {
|
fn is_not_hidden(entry: &DirEntry) -> bool {
|
||||||
entry
|
entry
|
||||||
.file_name()
|
.file_name()
|
||||||
|
@ -74,7 +20,6 @@ pub fn iter_python_files(path: &PathBuf) -> impl Iterator<Item = DirEntry> {
|
||||||
.filter_entry(is_not_hidden)
|
.filter_entry(is_not_hidden)
|
||||||
.filter_map(|entry| entry.ok())
|
.filter_map(|entry| entry.ok())
|
||||||
.filter(|entry| entry.path().to_string_lossy().ends_with(".py"))
|
.filter(|entry| entry.path().to_string_lossy().ends_with(".py"))
|
||||||
.filter(|entry| !EXCLUDES.contains(entry.path().to_string_lossy().deref()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_line(path: &Path, row: &usize) -> Result<String> {
|
pub fn read_line(path: &Path, row: &usize) -> Result<String> {
|
||||||
|
|
|
@ -55,7 +55,10 @@ mod tests {
|
||||||
fn duplicate_argument_name() -> Result<()> {
|
fn duplicate_argument_name() -> Result<()> {
|
||||||
let actual = check_path(
|
let actual = check_path(
|
||||||
&Path::new("./resources/test/src/duplicate_argument_name.py"),
|
&Path::new("./resources/test/src/duplicate_argument_name.py"),
|
||||||
&settings::Settings { line_length: 88 },
|
&settings::Settings {
|
||||||
|
line_length: 88,
|
||||||
|
exclude: vec![],
|
||||||
|
},
|
||||||
&cache::Mode::None,
|
&cache::Mode::None,
|
||||||
)?;
|
)?;
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
|
@ -87,7 +90,10 @@ mod tests {
|
||||||
fn f_string_missing_placeholders() -> Result<()> {
|
fn f_string_missing_placeholders() -> Result<()> {
|
||||||
let actual = check_path(
|
let actual = check_path(
|
||||||
&Path::new("./resources/test/src/f_string_missing_placeholders.py"),
|
&Path::new("./resources/test/src/f_string_missing_placeholders.py"),
|
||||||
&settings::Settings { line_length: 88 },
|
&settings::Settings {
|
||||||
|
line_length: 88,
|
||||||
|
exclude: vec![],
|
||||||
|
},
|
||||||
&cache::Mode::None,
|
&cache::Mode::None,
|
||||||
)?;
|
)?;
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
|
@ -119,7 +125,10 @@ mod tests {
|
||||||
fn if_tuple() -> Result<()> {
|
fn if_tuple() -> Result<()> {
|
||||||
let actual = check_path(
|
let actual = check_path(
|
||||||
&Path::new("./resources/test/src/if_tuple.py"),
|
&Path::new("./resources/test/src/if_tuple.py"),
|
||||||
&settings::Settings { line_length: 88 },
|
&settings::Settings {
|
||||||
|
line_length: 88,
|
||||||
|
exclude: vec![],
|
||||||
|
},
|
||||||
&cache::Mode::None,
|
&cache::Mode::None,
|
||||||
)?;
|
)?;
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
|
@ -146,7 +155,10 @@ mod tests {
|
||||||
fn import_star_usage() -> Result<()> {
|
fn import_star_usage() -> Result<()> {
|
||||||
let actual = check_path(
|
let actual = check_path(
|
||||||
&Path::new("./resources/test/src/import_star_usage.py"),
|
&Path::new("./resources/test/src/import_star_usage.py"),
|
||||||
&settings::Settings { line_length: 88 },
|
&settings::Settings {
|
||||||
|
line_length: 88,
|
||||||
|
exclude: vec![],
|
||||||
|
},
|
||||||
&cache::Mode::None,
|
&cache::Mode::None,
|
||||||
)?;
|
)?;
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
|
@ -173,7 +185,10 @@ mod tests {
|
||||||
fn line_too_long() -> Result<()> {
|
fn line_too_long() -> Result<()> {
|
||||||
let actual = check_path(
|
let actual = check_path(
|
||||||
&Path::new("./resources/test/src/line_too_long.py"),
|
&Path::new("./resources/test/src/line_too_long.py"),
|
||||||
&settings::Settings { line_length: 88 },
|
&settings::Settings {
|
||||||
|
line_length: 88,
|
||||||
|
exclude: vec![],
|
||||||
|
},
|
||||||
&cache::Mode::None,
|
&cache::Mode::None,
|
||||||
)?;
|
)?;
|
||||||
let expected = vec![Message {
|
let expected = vec![Message {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use serde::Deserialize;
|
||||||
|
|
||||||
use crate::fs;
|
use crate::fs;
|
||||||
|
|
||||||
pub fn load_config<'a>(paths: impl IntoIterator<Item = &'a Path>) -> Result<Config> {
|
pub fn load_config<'a>(paths: impl IntoIterator<Item = &'a Path>) -> Result<(PathBuf, Config)> {
|
||||||
match find_project_root(paths) {
|
match find_project_root(paths) {
|
||||||
Some(project_root) => match find_pyproject_toml(&project_root) {
|
Some(project_root) => match find_pyproject_toml(&project_root) {
|
||||||
Some(path) => {
|
Some(path) => {
|
||||||
|
@ -17,7 +17,7 @@ pub fn load_config<'a>(paths: impl IntoIterator<Item = &'a Path>) -> Result<Conf
|
||||||
.tool
|
.tool
|
||||||
.and_then(|tool| tool.linter)
|
.and_then(|tool| tool.linter)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
Ok(config)
|
Ok((project_root, config))
|
||||||
}
|
}
|
||||||
None => Ok(Default::default()),
|
None => Ok(Default::default()),
|
||||||
},
|
},
|
||||||
|
@ -29,6 +29,7 @@ pub fn load_config<'a>(paths: impl IntoIterator<Item = &'a Path>) -> Result<Conf
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub line_length: Option<usize>,
|
pub line_length: Option<usize>,
|
||||||
|
pub exclude: Option<Vec<PathBuf>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
||||||
|
@ -110,7 +111,10 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pyproject.tool,
|
pyproject.tool,
|
||||||
Some(Tools {
|
Some(Tools {
|
||||||
linter: Some(Config { line_length: None })
|
linter: Some(Config {
|
||||||
|
line_length: None,
|
||||||
|
exclude: None
|
||||||
|
})
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -125,7 +129,25 @@ line-length = 79
|
||||||
pyproject.tool,
|
pyproject.tool,
|
||||||
Some(Tools {
|
Some(Tools {
|
||||||
linter: Some(Config {
|
linter: Some(Config {
|
||||||
line_length: Some(79)
|
line_length: Some(79),
|
||||||
|
exclude: None
|
||||||
|
})
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let pyproject: PyProject = toml::from_str(
|
||||||
|
r#"
|
||||||
|
[tool.black]
|
||||||
|
[tool.linter]
|
||||||
|
exclude = ["foo.py"]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
assert_eq!(
|
||||||
|
pyproject.tool,
|
||||||
|
Some(Tools {
|
||||||
|
linter: Some(Config {
|
||||||
|
line_length: None,
|
||||||
|
exclude: Some(vec![Path::new("foo.py").to_path_buf()])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -170,7 +192,8 @@ other-attribute = 1
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config,
|
config,
|
||||||
Config {
|
Config {
|
||||||
line_length: Some(88)
|
line_length: Some(88),
|
||||||
|
exclude: Some(vec![Path::new("excluded.py").to_path_buf()])
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,34 @@
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::pyproject::{load_config, Config};
|
use crate::pyproject::load_config;
|
||||||
|
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub line_length: usize,
|
pub line_length: usize,
|
||||||
|
pub exclude: Vec<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEFAULT_MAX_LINE_LENGTH: usize = 88;
|
static DEFAULT_MAX_LINE_LENGTH: usize = 88;
|
||||||
|
|
||||||
impl From<Config> for Settings {
|
|
||||||
fn from(config: Config) -> Settings {
|
|
||||||
Settings {
|
|
||||||
line_length: config.line_length.unwrap_or(DEFAULT_MAX_LINE_LENGTH),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Settings {
|
impl Settings {
|
||||||
pub fn from_paths<'a>(paths: impl IntoIterator<Item = &'a Path>) -> Result<Self> {
|
pub fn from_paths<'a>(paths: impl IntoIterator<Item = &'a Path>) -> Result<Self> {
|
||||||
load_config(paths).map(|config| config.into())
|
let (project_root, config) = load_config(paths)?;
|
||||||
|
|
||||||
|
Ok(Settings {
|
||||||
|
line_length: config.line_length.unwrap_or(DEFAULT_MAX_LINE_LENGTH),
|
||||||
|
exclude: config
|
||||||
|
.exclude
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.map(|path| {
|
||||||
|
if path.is_relative() {
|
||||||
|
project_root.join(path)
|
||||||
|
} else {
|
||||||
|
path
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue