Avoid re-reading + iterating over lines for ignores (#48)

This commit is contained in:
Charlie Marsh 2022-08-30 13:19:22 -04:00 committed by GitHub
parent ae27793b86
commit 7ed5b3d3a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 58 additions and 93 deletions

20
Cargo.lock generated
View file

@ -188,25 +188,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "autoincrement"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc81b6ad0c9122715c741113fb97ba283f723b1dd86e186dd7df00a2b6877bfe"
dependencies = [
"autoincrement_derive",
]
[[package]]
name = "autoincrement_derive"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1259ce9b9a586a3f5ada3d236800ad0aa446b0d4c3dbac4158084a1dd704a228"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "base64"
version = "0.10.1"
@ -1657,7 +1638,6 @@ name = "ruff"
version = "0.0.19"
dependencies = [
"anyhow",
"autoincrement",
"bincode",
"cacache",
"chrono",

View file

@ -8,7 +8,6 @@ name = "ruff"
[dependencies]
anyhow = { version = "1.0.60" }
autoincrement = "1.0.1"
bincode = { version = "1.3.3" }
cacache = { version = "10.0.1" }
chrono = { version = "0.4.21" }

View file

@ -3,24 +3,36 @@ use rustpython_parser::ast::Location;
use crate::checks::{Check, CheckKind};
use crate::settings::Settings;
pub fn check_lines(contents: &str, settings: &Settings) -> Vec<Check> {
contents
.lines()
.enumerate()
.filter_map(|(row, line)| {
if settings.select.contains(CheckKind::LineTooLong.code())
&& line.len() > settings.line_length
{
let chunks: Vec<&str> = line.split_whitespace().collect();
if !(chunks.len() == 1 || (chunks.len() == 2 && chunks[0] == "#")) {
return Some(Check {
kind: CheckKind::LineTooLong,
location: Location::new(row + 1, settings.line_length + 1),
});
pub fn check_lines(checks: &mut Vec<Check>, contents: &str, settings: &Settings) {
let enforce_line_too_ling = settings.select.contains(CheckKind::LineTooLong.code());
let mut line_checks = vec![];
let mut ignored = vec![];
for (row, line) in contents.lines().enumerate() {
// Remove any ignored checks.
// TODO(charlie): Only validate checks for the current line.
for (index, check) in checks.iter().enumerate() {
if check.location.row() == row + 1 && check.is_inline_ignored(line) {
ignored.push(index);
}
}
// Enforce line length.
if enforce_line_too_ling && line.len() > settings.line_length {
let chunks: Vec<&str> = line.split_whitespace().collect();
if !(chunks.len() == 1 || (chunks.len() == 2 && chunks[0] == "#")) {
let check = Check {
kind: CheckKind::LineTooLong,
location: Location::new(row + 1, settings.line_length + 1),
};
if !check.is_inline_ignored(line) {
line_checks.push(check);
}
}
None
})
.collect()
}
}
for index in ignored.iter().rev() {
checks.remove(*index);
}
checks.extend(line_checks);
}

View file

@ -1,6 +1,7 @@
use std::str::FromStr;
use anyhow::Result;
use regex::Regex;
use rustpython_parser::ast::Location;
use serde::{Deserialize, Serialize};
@ -135,3 +136,28 @@ pub struct Check {
pub kind: CheckKind,
pub location: Location,
}
impl Check {
pub fn is_inline_ignored(&self, line: &str) -> bool {
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().as_str() {
return true;
}
}
false
}
None => true,
},
None => false,
}
}
}

View file

@ -1,5 +1,5 @@
use std::fs::File;
use std::io::{BufRead, BufReader, Read};
use std::io::{BufReader, Read};
use std::path::{Path, PathBuf};
use anyhow::Result;
@ -22,16 +22,6 @@ pub fn iter_python_files(path: &PathBuf) -> impl Iterator<Item = DirEntry> {
.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> {
let file = File::open(path)?;
let mut buf_reader = BufReader::new(file);

View file

@ -36,13 +36,7 @@ pub fn check_path(path: &Path, settings: &Settings, mode: &cache::Mode) -> Resul
}
// Run the lines-based checks.
if settings
.select
.iter()
.any(|check_code| matches!(check_code.lint_source(), LintSource::Lines))
{
checks.extend(check_lines(&contents, settings));
}
check_lines(&mut checks, &contents, settings);
// Convert to messages.
let messages: Vec<Message> = checks
@ -52,7 +46,6 @@ pub fn check_path(path: &Path, settings: &Settings, mode: &cache::Mode) -> Resul
location: check.location,
filename: path.to_string_lossy().to_string(),
})
.filter(|message| !message.is_inline_ignored())
.collect();
cache::set(path, settings, &messages, mode);

View file

@ -1,13 +1,10 @@
use std::fmt;
use std::path::Path;
use colored::Colorize;
use regex::Regex;
use rustpython_parser::ast::Location;
use serde::{Deserialize, Serialize};
use crate::checks::CheckKind;
use crate::fs;
#[derive(Serialize, Deserialize)]
#[serde(remote = "Location")]
@ -32,38 +29,6 @@ pub struct Message {
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().as_str() {
return true;
}
}
false
}
None => true,
},
None => false,
}
}
Err(_) => false,
}
}
}
impl fmt::Display for Message {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(