Compare commits

..

7 Commits

Author SHA1 Message Date
Charlie Marsh
b44d6c2c44 Bump version to 0.0.28 2022-09-06 14:20:02 -04:00
Charlie Marsh
2749660b1f Disable update-informer on linux-cross (#113) 2022-09-06 14:19:38 -04:00
Charlie Marsh
c1eeae90f1 Bump version to 0.0.27 2022-09-06 10:23:48 -04:00
Charlie Marsh
27025055ee Implement E713 and E714 (#111) 2022-09-06 10:23:20 -04:00
Charlie Marsh
e306fe0765 Implement E711 and E712 (#110) 2022-09-06 10:14:36 -04:00
Harutaka Kawamura
5ffb9c08d5 Implement E731 (#109) 2022-09-06 09:48:51 -04:00
Charlie Marsh
1a8940f015 Implement E902 (IOError) (#107) 2022-09-05 13:15:12 -04:00
19 changed files with 509 additions and 38 deletions

View File

@@ -1,6 +1,8 @@
name: Release
on:
pull_request:
branches: [main]
create:
tags:
- v*
@@ -127,7 +129,7 @@ jobs:
with:
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist
args: --no-default-features --release --out dist
maturin-version: "v0.13.0"
- uses: uraimo/run-on-arch-action@v2.0.5
if: matrix.target != 'ppc64'

View File

@@ -1,5 +1,5 @@
repos:
- repo: https://github.com/charliermarsh/ruff
rev: v0.0.26
rev: v0.0.28
hooks:
- id: lint

3
Cargo.lock generated
View File

@@ -1744,7 +1744,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.26"
version = "0.0.28"
dependencies = [
"anyhow",
"bincode",
@@ -1758,6 +1758,7 @@ dependencies = [
"fern",
"filetime",
"glob",
"itertools",
"log",
"notify",
"once_cell",

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff"
version = "0.0.26"
version = "0.0.28"
edition = "2021"
[lib]
@@ -19,6 +19,7 @@ dirs = { version = "4.0.0" }
fern = { version = "0.6.1" }
filetime = { version = "0.2.17" }
glob = { version = "0.3.0" }
itertools = "0.10.3"
log = { version = "0.4.17" }
notify = { version = "4.0.17" }
once_cell = { version = "1.13.1" }
@@ -28,9 +29,13 @@ rustpython-parser = { features = ["lalrpop"], git = "https://github.com/charlier
serde = { version = "1.0.143", features = ["derive"] }
serde_json = { version = "1.0.83" }
toml = { version = "0.5.9" }
update-informer = { version = "0.5.0", default_features = false, features = ["pypi"] }
update-informer = { version = "0.5.0", default_features = false, features = ["pypi"], optional = true }
walkdir = { version = "2.3.2" }
[features]
default = ["update-informer"]
update-informer = ["dep:update-informer"]
[profile.release]
panic = "abort"
lto = "thin"

View File

@@ -57,7 +57,7 @@ ruff also works with [Pre-Commit](https://pre-commit.com) (requires Cargo on sys
```yaml
repos:
- repo: https://github.com/charliermarsh/ruff
rev: v0.0.26
rev: v0.0.28
hooks:
- id: lint
```
@@ -86,7 +86,7 @@ ruff path/to/code/ --select F401 F403
See `ruff --help` for more:
```shell
ruff (v0.0.26)
ruff (v0.0.28)
An extremely fast Python linter.
USAGE:
@@ -122,6 +122,12 @@ lint rules that are obviated by Black (e.g., stylistic rules).
| ---- | ----- | ------- |
| E402 | ModuleImportNotAtTopOfFile | Module level import not at top of file |
| E501 | LineTooLong | Line too long |
| E711 | NoneComparison | Comparison to `None` should be `cond is None` |
| E712 | TrueFalseComparison | Comparison to `True` should be `cond is True` |
| E713 | NotInTest | Test for membership should be `not in` |
| E714 | NotIsTest | Test for object identity should be `is not` |
| E731 | DoNotAssignLambda | Do not assign a lambda expression, use a def |
| E902 | IOError | No such file or directory: `...` |
| F401 | UnusedImport | `...` imported but unused |
| F403 | ImportStarUsage | Unable to detect undefined names |
| F541 | FStringMissingPlaceholders | f-string without any placeholders |

View File

@@ -1,19 +1,25 @@
/// Generate a Markdown-compatible table of supported lint rules.
use ruff::checks::CheckKind;
use ruff::checks::{CheckKind, RejectedCmpop};
fn main() {
let mut check_kinds: Vec<CheckKind> = vec![
CheckKind::AssertTuple,
CheckKind::DefaultExceptNotLast,
CheckKind::DoNotAssignLambda,
CheckKind::DuplicateArgumentName,
CheckKind::FStringMissingPlaceholders,
CheckKind::IOError("...".to_string()),
CheckKind::IfTuple,
CheckKind::ImportStarUsage,
CheckKind::LineTooLong,
CheckKind::ModuleImportNotAtTopOfFile,
CheckKind::NoAssertEquals,
CheckKind::NoneComparison(RejectedCmpop::Eq),
CheckKind::NotInTest,
CheckKind::NotIsTest,
CheckKind::RaiseNotImplemented,
CheckKind::ReturnOutsideFunction,
CheckKind::TrueFalseComparison(true, RejectedCmpop::Eq),
CheckKind::UndefinedExport("...".to_string()),
CheckKind::UndefinedLocal("...".to_string()),
CheckKind::UndefinedName("...".to_string()),

11
resources/test/fixtures/E711.py vendored Normal file
View File

@@ -0,0 +1,11 @@
if var == None:
pass
if None != var:
pass
if var is None:
pass
if None is not var:
pass

14
resources/test/fixtures/E712.py vendored Normal file
View File

@@ -0,0 +1,14 @@
if var == True:
pass
if False != var:
pass
if var != False != True:
pass
if var is True:
pass
if False is not var:
pass

7
resources/test/fixtures/E713.py vendored Normal file
View File

@@ -0,0 +1,7 @@
my_list = [1, 2, 3]
if not num in my_list:
print(num)
my_list = [1, 2, 3]
if num not in my_list:
print(num)

5
resources/test/fixtures/E714.py vendored Normal file
View File

@@ -0,0 +1,5 @@
if not user is None:
print(user.name)
if user is not None:
print(user.name)

2
resources/test/fixtures/E731.py vendored Normal file
View File

@@ -0,0 +1,2 @@
a = lambda x: x**2
b = map(lambda x: 2 * x, range(3))

View File

@@ -4,6 +4,12 @@ exclude = ["excluded.py", "**/migrations"]
select = [
"E402",
"E501",
"E711",
"E712",
"E713",
"E714",
"E731",
"E902",
"F401",
"F403",
"F541",

View File

@@ -1,9 +1,10 @@
use std::collections::BTreeSet;
use std::path::Path;
use itertools::izip;
use rustpython_parser::ast::{
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind,
Location, Stmt, StmtKind, Suite,
Arg, Arguments, Cmpop, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind,
Location, Stmt, StmtKind, Suite, Unaryop,
};
use rustpython_parser::parser;
@@ -11,7 +12,7 @@ use crate::ast_ops::{
extract_all_names, Binding, BindingKind, Scope, ScopeKind, SourceCodeLocator,
};
use crate::builtins::{BUILTINS, MAGIC_GLOBALS};
use crate::checks::{Check, CheckCode, CheckKind, Fix};
use crate::checks::{Check, CheckCode, CheckKind, Fix, RejectedCmpop};
use crate::settings::Settings;
use crate::visitor::{walk_excepthandler, Visitor};
use crate::{autofix, fixer, visitor};
@@ -377,7 +378,20 @@ impl Visitor for Checker<'_> {
self.seen_non_import = true;
}
}
StmtKind::Delete { .. } | StmtKind::Assign { .. } | StmtKind::AnnAssign { .. } => {
StmtKind::Assign { value, .. } => {
self.seen_non_import = true;
if self
.settings
.select
.contains(CheckKind::DoNotAssignLambda.code())
{
if let ExprKind::Lambda { .. } = &value.node {
self.checks
.push(Check::new(CheckKind::DoNotAssignLambda, stmt.location));
}
}
}
StmtKind::Delete { .. } | StmtKind::AnnAssign { .. } => {
self.seen_non_import = true;
}
_ => {}
@@ -509,6 +523,127 @@ impl Visitor for Checker<'_> {
}
self.in_f_string = true;
}
ExprKind::UnaryOp { op, operand } => {
if matches!(op, Unaryop::Not) {
if let ExprKind::Compare { ops, .. } = &operand.node {
match ops[..] {
[Cmpop::In] => {
if self.settings.select.contains(CheckKind::NotInTest.code()) {
self.checks
.push(Check::new(CheckKind::NotInTest, operand.location));
}
}
[Cmpop::Is] => {
if self.settings.select.contains(CheckKind::NotIsTest.code()) {
self.checks
.push(Check::new(CheckKind::NotIsTest, operand.location));
}
}
_ => {}
}
}
}
}
ExprKind::Compare {
left,
ops,
comparators,
} => {
let op = ops.first().unwrap();
let comparator = left;
// Check `left`.
if self.settings.select.contains(&CheckCode::E711)
&& matches!(
comparator.node,
ExprKind::Constant {
value: Constant::None,
kind: None
}
)
{
if matches!(op, Cmpop::Eq) {
self.checks.push(Check::new(
CheckKind::NoneComparison(RejectedCmpop::Eq),
comparator.location,
));
}
if matches!(op, Cmpop::NotEq) {
self.checks.push(Check::new(
CheckKind::NoneComparison(RejectedCmpop::NotEq),
comparator.location,
));
}
}
if self.settings.select.contains(&CheckCode::E712) {
if let ExprKind::Constant {
value: Constant::Bool(value),
kind: None,
} = comparator.node
{
if matches!(op, Cmpop::Eq) {
self.checks.push(Check::new(
CheckKind::TrueFalseComparison(value, RejectedCmpop::Eq),
comparator.location,
));
}
if matches!(op, Cmpop::NotEq) {
self.checks.push(Check::new(
CheckKind::TrueFalseComparison(value, RejectedCmpop::NotEq),
comparator.location,
));
}
}
}
// Check each comparator in order.
for (op, comparator) in izip!(ops, comparators) {
if self.settings.select.contains(&CheckCode::E711)
&& matches!(
comparator.node,
ExprKind::Constant {
value: Constant::None,
kind: None
}
)
{
if matches!(op, Cmpop::Eq) {
self.checks.push(Check::new(
CheckKind::NoneComparison(RejectedCmpop::Eq),
comparator.location,
));
}
if matches!(op, Cmpop::NotEq) {
self.checks.push(Check::new(
CheckKind::NoneComparison(RejectedCmpop::NotEq),
comparator.location,
));
}
}
if self.settings.select.contains(&CheckCode::E712) {
if let ExprKind::Constant {
value: Constant::Bool(value),
kind: None,
} = comparator.node
{
if matches!(op, Cmpop::Eq) {
self.checks.push(Check::new(
CheckKind::TrueFalseComparison(value, RejectedCmpop::Eq),
comparator.location,
));
}
if matches!(op, Cmpop::NotEq) {
self.checks.push(Check::new(
CheckKind::TrueFalseComparison(value, RejectedCmpop::NotEq),
comparator.location,
));
}
}
}
}
}
ExprKind::Constant {
value: Constant::Str(value),
..

View File

@@ -10,6 +10,12 @@ use serde::{Deserialize, Serialize};
pub enum CheckCode {
E402,
E501,
E711,
E712,
E713,
E714,
E731,
E902,
F401,
F403,
F541,
@@ -35,6 +41,12 @@ impl FromStr for CheckCode {
match s {
"E402" => Ok(CheckCode::E402),
"E501" => Ok(CheckCode::E501),
"E711" => Ok(CheckCode::E711),
"E712" => Ok(CheckCode::E712),
"E713" => Ok(CheckCode::E713),
"E714" => Ok(CheckCode::E714),
"E731" => Ok(CheckCode::E731),
"E902" => Ok(CheckCode::E902),
"F401" => Ok(CheckCode::F401),
"F403" => Ok(CheckCode::F403),
"F541" => Ok(CheckCode::F541),
@@ -61,6 +73,12 @@ impl CheckCode {
match self {
CheckCode::E402 => "E402",
CheckCode::E501 => "E501",
CheckCode::E711 => "E711",
CheckCode::E712 => "E712",
CheckCode::E713 => "E713",
CheckCode::E714 => "E714",
CheckCode::E731 => "E731",
CheckCode::E902 => "E902",
CheckCode::F401 => "F401",
CheckCode::F403 => "F403",
CheckCode::F541 => "F541",
@@ -85,6 +103,12 @@ impl CheckCode {
match self {
CheckCode::E402 => &LintSource::AST,
CheckCode::E501 => &LintSource::Lines,
CheckCode::E711 => &LintSource::AST,
CheckCode::E712 => &LintSource::AST,
CheckCode::E713 => &LintSource::AST,
CheckCode::E714 => &LintSource::AST,
CheckCode::E731 => &LintSource::AST,
CheckCode::E902 => &LintSource::FileSystem,
CheckCode::F401 => &LintSource::AST,
CheckCode::F403 => &LintSource::AST,
CheckCode::F541 => &LintSource::AST,
@@ -109,21 +133,34 @@ impl CheckCode {
pub enum LintSource {
AST,
Lines,
FileSystem,
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum RejectedCmpop {
Eq,
NotEq,
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum CheckKind {
AssertTuple,
DefaultExceptNotLast,
DoNotAssignLambda,
DuplicateArgumentName,
FStringMissingPlaceholders,
IOError(String),
IfTuple,
ImportStarUsage,
LineTooLong,
ModuleImportNotAtTopOfFile,
NoAssertEquals,
NoneComparison(RejectedCmpop),
NotInTest,
NotIsTest,
RaiseNotImplemented,
ReturnOutsideFunction,
TrueFalseComparison(bool, RejectedCmpop),
UndefinedExport(String),
UndefinedLocal(String),
UndefinedName(String),
@@ -141,13 +178,19 @@ impl CheckKind {
CheckKind::DefaultExceptNotLast => "DefaultExceptNotLast",
CheckKind::DuplicateArgumentName => "DuplicateArgumentName",
CheckKind::FStringMissingPlaceholders => "FStringMissingPlaceholders",
CheckKind::IOError(_) => "IOError",
CheckKind::IfTuple => "IfTuple",
CheckKind::ImportStarUsage => "ImportStarUsage",
CheckKind::LineTooLong => "LineTooLong",
CheckKind::DoNotAssignLambda => "DoNotAssignLambda",
CheckKind::ModuleImportNotAtTopOfFile => "ModuleImportNotAtTopOfFile",
CheckKind::NoAssertEquals => "NoAssertEquals",
CheckKind::NoneComparison(_) => "NoneComparison",
CheckKind::NotInTest => "NotInTest",
CheckKind::NotIsTest => "NotIsTest",
CheckKind::RaiseNotImplemented => "RaiseNotImplemented",
CheckKind::ReturnOutsideFunction => "ReturnOutsideFunction",
CheckKind::TrueFalseComparison(_, _) => "TrueFalseComparison",
CheckKind::UndefinedExport(_) => "UndefinedExport",
CheckKind::UndefinedLocal(_) => "UndefinedLocal",
CheckKind::UndefinedName(_) => "UndefinedName",
@@ -165,13 +208,19 @@ impl CheckKind {
CheckKind::DefaultExceptNotLast => &CheckCode::F707,
CheckKind::DuplicateArgumentName => &CheckCode::F831,
CheckKind::FStringMissingPlaceholders => &CheckCode::F541,
CheckKind::IOError(_) => &CheckCode::E902,
CheckKind::IfTuple => &CheckCode::F634,
CheckKind::ImportStarUsage => &CheckCode::F403,
CheckKind::LineTooLong => &CheckCode::E501,
CheckKind::DoNotAssignLambda => &CheckCode::E731,
CheckKind::ModuleImportNotAtTopOfFile => &CheckCode::E402,
CheckKind::NoAssertEquals => &CheckCode::R002,
CheckKind::NoneComparison(_) => &CheckCode::E711,
CheckKind::NotInTest => &CheckCode::E713,
CheckKind::NotIsTest => &CheckCode::E714,
CheckKind::RaiseNotImplemented => &CheckCode::F901,
CheckKind::ReturnOutsideFunction => &CheckCode::F706,
CheckKind::TrueFalseComparison(_, _) => &CheckCode::E712,
CheckKind::UndefinedExport(_) => &CheckCode::F822,
CheckKind::UndefinedLocal(_) => &CheckCode::F823,
CheckKind::UndefinedName(_) => &CheckCode::F821,
@@ -197,21 +246,53 @@ impl CheckKind {
CheckKind::FStringMissingPlaceholders => {
"f-string without any placeholders".to_string()
}
CheckKind::IOError(name) => {
format!("No such file or directory: `{name}`")
}
CheckKind::IfTuple => "If test is a tuple, which is always `True`".to_string(),
CheckKind::ImportStarUsage => "Unable to detect undefined names".to_string(),
CheckKind::LineTooLong => "Line too long".to_string(),
CheckKind::DoNotAssignLambda => {
"Do not assign a lambda expression, use a def".to_string()
}
CheckKind::ModuleImportNotAtTopOfFile => {
"Module level import not at top of file".to_string()
}
CheckKind::NoAssertEquals => {
"`assertEquals` is deprecated, use `assertEqual` instead".to_string()
}
CheckKind::NoneComparison(op) => match op {
RejectedCmpop::Eq => "Comparison to `None` should be `cond is None`".to_string(),
RejectedCmpop::NotEq => {
"Comparison to `None` should be `cond is not None`".to_string()
}
},
CheckKind::NotInTest => "Test for membership should be `not in`".to_string(),
CheckKind::NotIsTest => "Test for object identity should be `is not`".to_string(),
CheckKind::RaiseNotImplemented => {
"`raise NotImplemented` should be `raise NotImplementedError`".to_string()
}
CheckKind::ReturnOutsideFunction => {
"a `return` statement outside of a function/method".to_string()
}
CheckKind::TrueFalseComparison(value, op) => match *value {
true => match op {
RejectedCmpop::Eq => {
"Comparison to `True` should be `cond is True`".to_string()
}
RejectedCmpop::NotEq => {
"Comparison to `True` should be `cond is not True`".to_string()
}
},
false => match op {
RejectedCmpop::Eq => {
"Comparison to `False` should be `cond is False`".to_string()
}
RejectedCmpop::NotEq => {
"Comparison to `False` should be `cond is not False`".to_string()
}
},
},
CheckKind::UndefinedExport(name) => {
format!("Undefined name `{name}` in `__all__`")
}
@@ -241,13 +322,19 @@ impl CheckKind {
CheckKind::DefaultExceptNotLast => false,
CheckKind::DuplicateArgumentName => false,
CheckKind::FStringMissingPlaceholders => false,
CheckKind::IOError(_) => false,
CheckKind::IfTuple => false,
CheckKind::ImportStarUsage => false,
CheckKind::DoNotAssignLambda => false,
CheckKind::LineTooLong => false,
CheckKind::ModuleImportNotAtTopOfFile => false,
CheckKind::NoAssertEquals => true,
CheckKind::NotInTest => false,
CheckKind::NotIsTest => false,
CheckKind::NoneComparison(_) => false,
CheckKind::RaiseNotImplemented => false,
CheckKind::ReturnOutsideFunction => false,
CheckKind::TrueFalseComparison(_, _) => false,
CheckKind::UndefinedExport(_) => false,
CheckKind::UndefinedLocal(_) => false,
CheckKind::UndefinedName(_) => false,

View File

@@ -1,3 +1,5 @@
extern crate core;
mod ast_ops;
mod autofix;
mod builtins;

View File

@@ -84,7 +84,7 @@ mod tests {
use anyhow::Result;
use rustpython_parser::ast::Location;
use crate::checks::{Check, CheckCode, CheckKind, Fix};
use crate::checks::{Check, CheckCode, CheckKind, Fix, RejectedCmpop};
use crate::linter::check_path;
use crate::{autofix, settings};
@@ -136,6 +136,152 @@ mod tests {
Ok(())
}
#[test]
fn e711() -> Result<()> {
let actual = check_path(
Path::new("./resources/test/fixtures/E711.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
select: BTreeSet::from([CheckCode::E711]),
},
&autofix::Mode::Generate,
)?;
let expected = vec![
Check {
kind: CheckKind::NoneComparison(RejectedCmpop::Eq),
location: Location::new(1, 11),
fix: None,
},
Check {
kind: CheckKind::NoneComparison(RejectedCmpop::NotEq),
location: Location::new(4, 4),
fix: None,
},
];
assert_eq!(actual.len(), expected.len());
for i in 0..actual.len() {
assert_eq!(actual[i], expected[i]);
}
Ok(())
}
#[test]
fn e712() -> Result<()> {
let actual = check_path(
Path::new("./resources/test/fixtures/E712.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
select: BTreeSet::from([CheckCode::E712]),
},
&autofix::Mode::Generate,
)?;
let expected = vec![
Check {
kind: CheckKind::TrueFalseComparison(true, RejectedCmpop::Eq),
location: Location::new(1, 11),
fix: None,
},
Check {
kind: CheckKind::TrueFalseComparison(false, RejectedCmpop::NotEq),
location: Location::new(4, 4),
fix: None,
},
Check {
kind: CheckKind::TrueFalseComparison(false, RejectedCmpop::NotEq),
location: Location::new(7, 11),
fix: None,
},
Check {
kind: CheckKind::TrueFalseComparison(true, RejectedCmpop::NotEq),
location: Location::new(7, 20),
fix: None,
},
];
assert_eq!(actual.len(), expected.len());
for i in 0..actual.len() {
assert_eq!(actual[i], expected[i]);
}
Ok(())
}
#[test]
fn e713() -> Result<()> {
let actual = check_path(
Path::new("./resources/test/fixtures/E713.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
select: BTreeSet::from([CheckCode::E713]),
},
&autofix::Mode::Generate,
)?;
let expected = vec![Check {
kind: CheckKind::NotInTest,
location: Location::new(2, 12),
fix: None,
}];
assert_eq!(actual.len(), expected.len());
for i in 0..actual.len() {
assert_eq!(actual[i], expected[i]);
}
Ok(())
}
#[test]
fn e714() -> Result<()> {
let actual = check_path(
Path::new("./resources/test/fixtures/E714.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
select: BTreeSet::from([CheckCode::E714]),
},
&autofix::Mode::Generate,
)?;
let expected = vec![Check {
kind: CheckKind::NotIsTest,
location: Location::new(1, 13),
fix: None,
}];
assert_eq!(actual.len(), expected.len());
for i in 0..actual.len() {
assert_eq!(actual[i], expected[i]);
}
Ok(())
}
#[test]
fn e731() -> Result<()> {
let actual = check_path(
Path::new("./resources/test/fixtures/E731.py"),
&settings::Settings {
line_length: 88,
exclude: vec![],
select: BTreeSet::from([CheckCode::E731]),
},
&autofix::Mode::Generate,
)?;
let expected = vec![Check {
kind: CheckKind::DoNotAssignLambda,
location: Location::new(1, 1),
fix: None,
}];
assert_eq!(actual.len(), expected.len());
for i in 0..actual.len() {
assert_eq!(actual[i], expected[i]);
}
Ok(())
}
#[test]
fn f401() -> Result<()> {
let actual = check_path(

View File

@@ -12,6 +12,7 @@ use rayon::prelude::*;
use walkdir::DirEntry;
use ::ruff::checks::CheckCode;
use ::ruff::checks::CheckKind;
use ::ruff::fs::iter_python_files;
use ::ruff::linter::lint_path;
use ::ruff::logging::set_up_logging;
@@ -54,6 +55,30 @@ struct Cli {
ignore: Vec<CheckCode>,
}
#[cfg(feature = "update-informer")]
fn check_for_updates() {
use update_informer::{registry, Check};
let informer = update_informer::new(registry::PyPI, CARGO_PKG_NAME, CARGO_PKG_VERSION);
if let Some(new_version) = informer.check_version().ok().flatten() {
let msg = format!(
"A new version of {pkg_name} is available: v{pkg_version} -> {new_version}",
pkg_name = CARGO_PKG_NAME.italic().cyan(),
pkg_version = CARGO_PKG_VERSION,
new_version = new_version.to_string().green()
);
let cmd = format!(
"Run to update: {cmd} {pkg_name}",
cmd = "pip3 install --upgrade".green(),
pkg_name = CARGO_PKG_NAME.green()
);
println!("\n{msg}\n{cmd}");
}
}
fn run_once(
files: &[PathBuf],
settings: &Settings,
@@ -62,7 +87,7 @@ fn run_once(
) -> Result<Vec<Message>> {
// Collect all the files to check.
let start = Instant::now();
let files: Vec<DirEntry> = files
let paths: Vec<DirEntry> = files
.iter()
.flat_map(|path| iter_python_files(path, &settings.exclude))
.collect();
@@ -70,7 +95,7 @@ fn run_once(
debug!("Identified files to lint in: {:?}", duration);
let start = Instant::now();
let mut messages: Vec<Message> = files
let mut messages: Vec<Message> = paths
.par_iter()
.map(|entry| {
lint_path(entry.path(), settings, &cache.into(), &autofix.into()).unwrap_or_else(|e| {
@@ -80,6 +105,20 @@ fn run_once(
})
.flatten()
.collect();
if settings.select.contains(&CheckCode::E902) {
for file in files {
if !file.exists() {
messages.push(Message {
kind: CheckKind::IOError(file.to_string_lossy().to_string()),
fixed: false,
location: Default::default(),
filename: file.to_string_lossy().to_string(),
})
}
}
}
messages.sort_unstable();
let duration = start.elapsed();
debug!("Checked files in: {:?}", duration);
@@ -195,6 +234,7 @@ fn inner_main() -> Result<ExitCode> {
report_once(&messages)?;
}
#[cfg(feature = "update-informer")]
check_for_updates();
if !messages.is_empty() && !cli.exit_zero {
@@ -205,29 +245,6 @@ fn inner_main() -> Result<ExitCode> {
Ok(ExitCode::SUCCESS)
}
fn check_for_updates() {
use update_informer::{registry, Check};
let informer = update_informer::new(registry::PyPI, CARGO_PKG_NAME, CARGO_PKG_VERSION);
if let Some(new_version) = informer.check_version().ok().flatten() {
let msg = format!(
"A new version of {pkg_name} is available: v{pkg_version} -> {new_version}",
pkg_name = CARGO_PKG_NAME.italic().cyan(),
pkg_version = CARGO_PKG_VERSION,
new_version = new_version.to_string().green()
);
let cmd = format!(
"Run to update: {cmd} {pkg_name}",
cmd = "pip3 install --upgrade".green(),
pkg_name = CARGO_PKG_NAME.green()
);
println!("\n{msg}\n{cmd}");
}
}
fn main() -> ExitCode {
match inner_main() {
Ok(code) => code,

View File

@@ -239,6 +239,12 @@ other-attribute = 1
select: Some(BTreeSet::from([
CheckCode::E402,
CheckCode::E501,
CheckCode::E711,
CheckCode::E712,
CheckCode::E713,
CheckCode::E714,
CheckCode::E731,
CheckCode::E902,
CheckCode::F401,
CheckCode::F403,
CheckCode::F541,

View File

@@ -46,16 +46,29 @@ impl Settings {
BTreeSet::from([
CheckCode::E402,
CheckCode::E501,
CheckCode::E711,
CheckCode::E712,
CheckCode::E713,
CheckCode::E714,
CheckCode::E731,
CheckCode::E902,
CheckCode::F401,
CheckCode::F403,
CheckCode::F541,
CheckCode::F631,
CheckCode::F634,
CheckCode::F704,
CheckCode::F706,
CheckCode::F707,
CheckCode::F821,
CheckCode::F822,
CheckCode::F823,
CheckCode::F831,
CheckCode::F841,
CheckCode::F901,
// Disable refactoring codes by default.
// CheckCode::R001,
// CheckCode::R002,
])
}),
})