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",
|
||||
"dirs 4.0.0",
|
||||
"fern",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"notify",
|
||||
"pyo3",
|
||||
|
|
|
@ -18,7 +18,6 @@ colored = { version = "2.0.0" }
|
|||
common-path = "1.0.0"
|
||||
dirs = "4.0.0"
|
||||
fern = { version = "0.6.1" }
|
||||
lazy_static = { version = "1.4.0" }
|
||||
log = { version = "0.4.17" }
|
||||
notify = { version = "4.0.17" }
|
||||
pyo3 = { version = "0.16.5", features = ["extension-module", "abi3-py37"] }
|
||||
|
|
61
README.md
61
README.md
|
@ -5,11 +5,10 @@ in Rust.
|
|||
|
||||
Features:
|
||||
|
||||
- Python 3.8 compatibility
|
||||
- [ESLint](https://eslint.org/docs/latest/user-guide/command-line-interface#caching)-inspired
|
||||
cache semantics
|
||||
- [TypeScript](https://www.typescriptlang.org/docs/handbook/configuring-watch.html)
|
||||
-inspired `--watch` semantics
|
||||
- Python 3.9 compatibility
|
||||
- [ESLint](https://eslint.org/docs/latest/user-guide/command-line-interface#caching)-inspired cache semantics
|
||||
- [TypeScript](https://www.typescriptlang.org/docs/handbook/configuring-watch.html)-inspired `--watch` semantics
|
||||
- `pyproject.toml` support
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```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]
|
||||
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 messages: Vec<Message> = 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());
|
||||
|
|
55
src/fs.rs
55
src/fs.rs
|
@ -1,64 +1,10 @@
|
|||
use std::collections::HashSet;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, Read};
|
||||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use lazy_static::lazy_static;
|
||||
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 {
|
||||
entry
|
||||
.file_name()
|
||||
|
@ -74,7 +20,6 @@ pub fn iter_python_files(path: &PathBuf) -> impl Iterator<Item = DirEntry> {
|
|||
.filter_entry(is_not_hidden)
|
||||
.filter_map(|entry| entry.ok())
|
||||
.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> {
|
||||
|
|
|
@ -55,7 +55,10 @@ mod tests {
|
|||
fn duplicate_argument_name() -> Result<()> {
|
||||
let actual = check_path(
|
||||
&Path::new("./resources/test/src/duplicate_argument_name.py"),
|
||||
&settings::Settings { line_length: 88 },
|
||||
&settings::Settings {
|
||||
line_length: 88,
|
||||
exclude: vec![],
|
||||
},
|
||||
&cache::Mode::None,
|
||||
)?;
|
||||
let expected = vec![
|
||||
|
@ -87,7 +90,10 @@ mod tests {
|
|||
fn f_string_missing_placeholders() -> Result<()> {
|
||||
let actual = check_path(
|
||||
&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,
|
||||
)?;
|
||||
let expected = vec![
|
||||
|
@ -119,7 +125,10 @@ mod tests {
|
|||
fn if_tuple() -> Result<()> {
|
||||
let actual = check_path(
|
||||
&Path::new("./resources/test/src/if_tuple.py"),
|
||||
&settings::Settings { line_length: 88 },
|
||||
&settings::Settings {
|
||||
line_length: 88,
|
||||
exclude: vec![],
|
||||
},
|
||||
&cache::Mode::None,
|
||||
)?;
|
||||
let expected = vec![
|
||||
|
@ -146,7 +155,10 @@ mod tests {
|
|||
fn import_star_usage() -> Result<()> {
|
||||
let actual = check_path(
|
||||
&Path::new("./resources/test/src/import_star_usage.py"),
|
||||
&settings::Settings { line_length: 88 },
|
||||
&settings::Settings {
|
||||
line_length: 88,
|
||||
exclude: vec![],
|
||||
},
|
||||
&cache::Mode::None,
|
||||
)?;
|
||||
let expected = vec![
|
||||
|
@ -173,7 +185,10 @@ mod tests {
|
|||
fn line_too_long() -> Result<()> {
|
||||
let actual = check_path(
|
||||
&Path::new("./resources/test/src/line_too_long.py"),
|
||||
&settings::Settings { line_length: 88 },
|
||||
&settings::Settings {
|
||||
line_length: 88,
|
||||
exclude: vec![],
|
||||
},
|
||||
&cache::Mode::None,
|
||||
)?;
|
||||
let expected = vec![Message {
|
||||
|
|
|
@ -7,7 +7,7 @@ use serde::Deserialize;
|
|||
|
||||
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) {
|
||||
Some(project_root) => match find_pyproject_toml(&project_root) {
|
||||
Some(path) => {
|
||||
|
@ -17,7 +17,7 @@ pub fn load_config<'a>(paths: impl IntoIterator<Item = &'a Path>) -> Result<Conf
|
|||
.tool
|
||||
.and_then(|tool| tool.linter)
|
||||
.unwrap_or_default();
|
||||
Ok(config)
|
||||
Ok((project_root, config))
|
||||
}
|
||||
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")]
|
||||
pub struct Config {
|
||||
pub line_length: Option<usize>,
|
||||
pub exclude: Option<Vec<PathBuf>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
||||
|
@ -110,7 +111,10 @@ mod tests {
|
|||
assert_eq!(
|
||||
pyproject.tool,
|
||||
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,
|
||||
Some(Tools {
|
||||
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!(
|
||||
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 crate::pyproject::{load_config, Config};
|
||||
use crate::pyproject::load_config;
|
||||
|
||||
pub struct Settings {
|
||||
pub line_length: usize,
|
||||
pub exclude: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
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 {
|
||||
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