mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-03 07:04:53 +00:00
Add support for noqa pragmas (#11)
This commit is contained in:
parent
f01707b2ae
commit
3b1b53dacf
11 changed files with 105 additions and 15 deletions
27
Cargo.lock
generated
27
Cargo.lock
generated
|
@ -13,6 +13,15 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android_system_properties"
|
name = "android_system_properties"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
|
@ -1482,6 +1491,23 @@ dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "remove_dir_all"
|
name = "remove_dir_all"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
@ -1507,6 +1533,7 @@ dependencies = [
|
||||||
"notify",
|
"notify",
|
||||||
"pyo3",
|
"pyo3",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
"regex",
|
||||||
"rustpython-parser",
|
"rustpython-parser",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
|
@ -18,9 +18,10 @@ colored = { version = "2.0.0" }
|
||||||
fern = { version = "0.6.1" }
|
fern = { version = "0.6.1" }
|
||||||
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"] }
|
||||||
rayon = { version = "1.5.3" }
|
rayon = { version = "1.5.3" }
|
||||||
|
regex = { version = "1.6.0" }
|
||||||
rustpython-parser = { git = "https://github.com/RustPython/RustPython.git", rev = "dff916d45c5d13074d21ad329a5ab68a6499426a" }
|
rustpython-parser = { git = "https://github.com/RustPython/RustPython.git", rev = "dff916d45c5d13074d21ad329a5ab68a6499426a" }
|
||||||
serde = { version = "1.0.143", features = ["derive"] }
|
serde = { version = "1.0.143", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.83" }
|
serde_json = { version = "1.0.83" }
|
||||||
walkdir = { version = "2.3.2" }
|
walkdir = { version = "2.3.2" }
|
||||||
pyo3 = { version = "0.16.5", features = ["extension-module", "abi3-py37"] }
|
|
||||||
|
|
|
@ -1 +1,6 @@
|
||||||
from if_tuple import *
|
from if_tuple import *
|
||||||
|
from if_tuple import * # noqa: E501
|
||||||
|
|
||||||
|
from if_tuple import * # noqa
|
||||||
|
from if_tuple import * # noqa: F403
|
||||||
|
from if_tuple import * # noqa: F403, E501
|
||||||
|
|
|
@ -40,10 +40,10 @@ impl Visitor for Checker {
|
||||||
fn visit_arguments(&mut self, arguments: &Arguments) {
|
fn visit_arguments(&mut self, arguments: &Arguments) {
|
||||||
// Collect all the arguments into a single vector.
|
// Collect all the arguments into a single vector.
|
||||||
let mut all_arguments: Vec<&Arg> = arguments
|
let mut all_arguments: Vec<&Arg> = arguments
|
||||||
.posonlyargs
|
.args
|
||||||
.iter()
|
.iter()
|
||||||
|
.chain(arguments.posonlyargs.iter())
|
||||||
.chain(arguments.kwonlyargs.iter())
|
.chain(arguments.kwonlyargs.iter())
|
||||||
.chain(arguments.args.iter())
|
|
||||||
.collect();
|
.collect();
|
||||||
if let Some(arg) = &arguments.vararg {
|
if let Some(arg) = &arguments.vararg {
|
||||||
all_arguments.push(arg);
|
all_arguments.push(arg);
|
||||||
|
|
|
@ -2,16 +2,17 @@ use rustpython_parser::ast::Location;
|
||||||
|
|
||||||
use crate::checks::Check;
|
use crate::checks::Check;
|
||||||
use crate::checks::CheckKind::LineTooLong;
|
use crate::checks::CheckKind::LineTooLong;
|
||||||
|
use crate::settings::MAX_LINE_LENGTH;
|
||||||
|
|
||||||
pub fn check_lines(contents: &str) -> Vec<Check> {
|
pub fn check_lines(contents: &str) -> Vec<Check> {
|
||||||
contents
|
contents
|
||||||
.lines()
|
.lines()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(row, line)| {
|
.filter_map(|(row, line)| {
|
||||||
if line.len() > 79 {
|
if line.len() > *MAX_LINE_LENGTH {
|
||||||
Some(Check {
|
Some(Check {
|
||||||
kind: LineTooLong,
|
kind: LineTooLong,
|
||||||
location: Location::new(row + 1, 79 + 1),
|
location: Location::new(row + 1, MAX_LINE_LENGTH + 1),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -26,7 +26,7 @@ impl CheckKind {
|
||||||
CheckKind::DuplicateArgumentName => "Duplicate argument name in function definition",
|
CheckKind::DuplicateArgumentName => "Duplicate argument name in function definition",
|
||||||
CheckKind::IfTuple => "If test is a tuple, which is always `True`",
|
CheckKind::IfTuple => "If test is a tuple, which is always `True`",
|
||||||
CheckKind::ImportStarUsage => "Unable to detect undefined names",
|
CheckKind::ImportStarUsage => "Unable to detect undefined names",
|
||||||
CheckKind::LineTooLong => "Line too long (> 79 characters)",
|
CheckKind::LineTooLong => "Line too long",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
15
src/fs.rs
15
src/fs.rs
|
@ -1,7 +1,8 @@
|
||||||
use anyhow::Result;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufReader, Read};
|
use std::io::{BufRead, BufReader, Read};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use walkdir::{DirEntry, WalkDir};
|
use walkdir::{DirEntry, WalkDir};
|
||||||
|
|
||||||
fn is_not_hidden(entry: &DirEntry) -> bool {
|
fn is_not_hidden(entry: &DirEntry) -> bool {
|
||||||
|
@ -21,6 +22,16 @@ pub fn iter_python_files(path: &PathBuf) -> impl Iterator<Item = DirEntry> {
|
||||||
.filter(|entry| entry.path().to_string_lossy().ends_with(".py"))
|
.filter(|entry| entry.path().to_string_lossy().ends_with(".py"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_line(path: &Path, row: &usize) -> Result<String> {
|
||||||
|
let file = File::open(path)?;
|
||||||
|
let buf_reader = BufReader::new(file);
|
||||||
|
buf_reader
|
||||||
|
.lines()
|
||||||
|
.nth(*row - 1)
|
||||||
|
.unwrap()
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_file(path: &Path) -> Result<String> {
|
pub fn read_file(path: &Path) -> Result<String> {
|
||||||
let file = File::open(path)?;
|
let file = File::open(path)?;
|
||||||
let mut buf_reader = BufReader::new(file);
|
let mut buf_reader = BufReader::new(file);
|
||||||
|
|
|
@ -6,4 +6,5 @@ pub mod fs;
|
||||||
pub mod linter;
|
pub mod linter;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
|
mod settings;
|
||||||
mod visitor;
|
mod visitor;
|
||||||
|
|
|
@ -29,6 +29,7 @@ pub fn check_path(path: &Path, mode: &cache::Mode) -> Result<Vec<Message>> {
|
||||||
location: check.location,
|
location: check.location,
|
||||||
filename: path.to_string_lossy().to_string(),
|
filename: path.to_string_lossy().to_string(),
|
||||||
})
|
})
|
||||||
|
.filter(|message| !message.is_inline_ignored())
|
||||||
.collect();
|
.collect();
|
||||||
cache::set(path, &messages, mode);
|
cache::set(path, &messages, mode);
|
||||||
|
|
||||||
|
@ -61,7 +62,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
Message {
|
Message {
|
||||||
kind: DuplicateArgumentName,
|
kind: DuplicateArgumentName,
|
||||||
location: Location::new(5, 9),
|
location: Location::new(5, 28),
|
||||||
filename: "./resources/test/src/duplicate_argument_name.py".to_string(),
|
filename: "./resources/test/src/duplicate_argument_name.py".to_string(),
|
||||||
},
|
},
|
||||||
Message {
|
Message {
|
||||||
|
@ -110,11 +111,18 @@ mod tests {
|
||||||
&Path::new("./resources/test/src/import_star_usage.py"),
|
&Path::new("./resources/test/src/import_star_usage.py"),
|
||||||
&cache::Mode::None,
|
&cache::Mode::None,
|
||||||
)?;
|
)?;
|
||||||
let expected = vec![Message {
|
let expected = vec![
|
||||||
|
Message {
|
||||||
kind: ImportStarUsage,
|
kind: ImportStarUsage,
|
||||||
location: Location::new(1, 1),
|
location: Location::new(1, 1),
|
||||||
filename: "./resources/test/src/import_star_usage.py".to_string(),
|
filename: "./resources/test/src/import_star_usage.py".to_string(),
|
||||||
}];
|
},
|
||||||
|
Message {
|
||||||
|
kind: ImportStarUsage,
|
||||||
|
location: Location::new(2, 1),
|
||||||
|
filename: "./resources/test/src/import_star_usage.py".to_string(),
|
||||||
|
},
|
||||||
|
];
|
||||||
assert_eq!(actual.len(), expected.len());
|
assert_eq!(actual.len(), expected.len());
|
||||||
for i in 1..actual.len() {
|
for i in 1..actual.len() {
|
||||||
assert_eq!(actual[i], expected[i]);
|
assert_eq!(actual[i], expected[i]);
|
||||||
|
@ -131,7 +139,7 @@ mod tests {
|
||||||
)?;
|
)?;
|
||||||
let expected = vec![Message {
|
let expected = vec![Message {
|
||||||
kind: LineTooLong,
|
kind: LineTooLong,
|
||||||
location: Location::new(3, 80),
|
location: Location::new(3, 88),
|
||||||
filename: "./resources/test/src/line_too_long.py".to_string(),
|
filename: "./resources/test/src/line_too_long.py".to_string(),
|
||||||
}];
|
}];
|
||||||
assert_eq!(actual.len(), expected.len());
|
assert_eq!(actual.len(), expected.len());
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
|
use regex::Regex;
|
||||||
use rustpython_parser::ast::Location;
|
use rustpython_parser::ast::Location;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::checks::CheckKind;
|
use crate::checks::CheckKind;
|
||||||
|
use crate::fs;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(remote = "Location")]
|
#[serde(remote = "Location")]
|
||||||
|
@ -29,6 +32,38 @@ pub struct Message {
|
||||||
pub filename: String,
|
pub filename: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Message {
|
||||||
|
pub fn is_inline_ignored(&self) -> bool {
|
||||||
|
match fs::read_line(Path::new(&self.filename), &self.location.row()) {
|
||||||
|
Ok(line) => {
|
||||||
|
// https://github.com/PyCQA/flake8/blob/799c71eeb61cf26c7c176aed43e22523e2a6d991/src/flake8/defaults.py#L26
|
||||||
|
let re = Regex::new(r"(?i)# noqa(?::\s?(?P<codes>([A-Z]+[0-9]+(?:[,\s]+)?)+))?")
|
||||||
|
.unwrap();
|
||||||
|
match re.captures(&line) {
|
||||||
|
Some(caps) => match caps.name("codes") {
|
||||||
|
Some(codes) => {
|
||||||
|
let re = Regex::new(r"[,\s]").unwrap();
|
||||||
|
for code in re
|
||||||
|
.split(codes.as_str())
|
||||||
|
.map(|code| code.trim())
|
||||||
|
.filter(|code| !code.is_empty())
|
||||||
|
{
|
||||||
|
if code == self.kind.code() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
None => true,
|
||||||
|
},
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for Message {
|
impl fmt::Display for Message {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
|
|
1
src/settings.rs
Normal file
1
src/settings.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub static MAX_LINE_LENGTH: &usize = &88;
|
Loading…
Add table
Add a link
Reference in a new issue