Compare commits

...

6 Commits

Author SHA1 Message Date
Charlie Marsh
dca994d05f Bump version to 0.0.145 2022-11-28 20:57:58 -05:00
Charlie Marsh
9944246f98 Rewrite type annotations on Python 3.7 when __future__ enabled (#953) 2022-11-28 20:57:38 -05:00
Charlie Marsh
82b0b7941a Implement eradicate (#947) 2022-11-28 20:54:33 -05:00
Charlie Marsh
72453695d6 Bump version to 0.0.144 2022-11-28 20:11:08 -05:00
Charlie Marsh
1617d715f2 Allow long lines that consist of only a URL (#952) 2022-11-28 20:10:21 -05:00
pwoolvett
c4a7344791 fix(flake8_boolean_trap): add allowlist for dict methods (#943) 2022-11-28 16:17:01 -05:00
33 changed files with 1025 additions and 265 deletions

View File

@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.143
rev: v0.0.145
hooks:
- id: ruff

6
Cargo.lock generated
View File

@@ -700,7 +700,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8-to-ruff"
version = "0.0.143-dev.0"
version = "0.0.145-dev.0"
dependencies = [
"anyhow",
"clap 4.0.22",
@@ -1805,7 +1805,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.143"
version = "0.0.145"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -1856,7 +1856,7 @@ dependencies = [
[[package]]
name = "ruff_dev"
version = "0.0.143"
version = "0.0.145"
dependencies = [
"anyhow",
"clap 4.0.22",

View File

@@ -6,7 +6,7 @@ members = [
[package]
name = "ruff"
version = "0.0.143"
version = "0.0.145"
edition = "2021"
rust-version = "1.65.0"

25
LICENSE
View File

@@ -267,6 +267,31 @@ are:
SOFTWARE.
"""
- flake8-eradicate, licensed as follows:
"""
MIT License
Copyright (c) 2018 Nikita Sobolev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- flake8-tidy-imports, licensed as follows:
"""
MIT License

View File

@@ -27,9 +27,9 @@ An extremely fast Python linter, written in Rust.
Ruff aims to be orders of magnitude faster than alternative tools while integrating more
functionality behind a single, common interface. Ruff can be used to replace Flake8 (plus a variety
of plugins), [`isort`](https://pypi.org/project/isort/), [`pydocstyle`](https://pypi.org/project/pydocstyle/),
[`yesqa`](https://github.com/asottile/yesqa), and even a subset of [`pyupgrade`](https://pypi.org/project/pyupgrade/)
and [`autoflake`](https://pypi.org/project/autoflake/) all while executing tens or hundreds of times
faster than any individual tool.
[`yesqa`](https://github.com/asottile/yesqa), [`eradicate`](https://pypi.org/project/eradicate/),
and even a subset of [`pyupgrade`](https://pypi.org/project/pyupgrade/) and [`autoflake`](https://pypi.org/project/autoflake/)
all while executing tens or hundreds of times faster than any individual tool.
Ruff is extremely actively developed and used in major open-source projects like:
@@ -73,6 +73,7 @@ of [Conda](https://docs.conda.io/en/latest/):
1. [pydocstyle (D)](#pydocstyle)
1. [pyupgrade (U)](#pyupgrade)
1. [pep8-naming (N)](#pep8-naming)
1. [eradicate (ERA)](#eradicate)
1. [flake8-bandit (S)](#flake8-bandit)
1. [flake8-comprehensions (C)](#flake8-comprehensions)
1. [flake8-boolean-trap (FBT)](#flake8-boolean-trap)
@@ -134,7 +135,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
```yaml
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.143
rev: v0.0.145
hooks:
- id: ruff
```
@@ -533,6 +534,14 @@ For more, see [pep8-naming](https://pypi.org/project/pep8-naming/0.13.2/) on PyP
| N817 | CamelcaseImportedAsAcronym | Camelcase `...` imported as acronym `...` | |
| N818 | ErrorSuffixOnExceptionName | Exception name `...` should be named with an Error suffix | |
### eradicate
For more, see [eradicate](https://pypi.org/project/eradicate/2.1.0/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| ERA001 | CommentedCode | Found commented-out code | 🛠 |
### flake8-bandit
For more, see [flake8-bandit](https://pypi.org/project/flake8-bandit/4.1.1/) on PyPI.
@@ -854,27 +863,29 @@ plugins, (2) alongside Black, and (3) on Python 3 code.
Under those conditions, Ruff implements every rule in Flake8, with the exception of `F811`.
Ruff also re-implements some of the most popular Flake8 plugins and related code quality tools
natively, including:
natively, including:
- [`isort`](https://pypi.org/project/isort/)
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
- [`yesqa`](https://github.com/asottile/yesqa)
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/)
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
- [`flake8-debugger`](https://pypi.org/project/flake8-debugger/)
- [`flake8-super`](https://pypi.org/project/flake8-super/)
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
- [`flake8-print`](https://pypi.org/project/flake8-print/)
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) (6/40)
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
- [`flake8-blind-except`](https://pypi.org/project/flake8-blind-except/)
- [`flake8-boolean-trap`](https://pypi.org/project/flake8-boolean-trap/)
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/)
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
- [`flake8-debugger`](https://pypi.org/project/flake8-debugger/)
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
- [`flake8-eradicate`](https://pypi.org/project/flake8-eradicate/)
- [`flake8-print`](https://pypi.org/project/flake8-print/)
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
- [`flake8-super`](https://pypi.org/project/flake8-super/)
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
- [`mccabe`](https://pypi.org/project/mccabe/)
- [`isort`](https://pypi.org/project/isort/)
- [`yesqa`](https://github.com/asottile/yesqa)
- [`eradicate`](https://pypi.org/project/eradicate/)
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (16/33)
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
@@ -891,20 +902,21 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/)
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
- [`flake8-debugger`](https://pypi.org/project/flake8-debugger/)
- [`flake8-super`](https://pypi.org/project/flake8-super/)
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
- [`flake8-print`](https://pypi.org/project/flake8-print/)
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) (6/40)
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
- [`flake8-blind-except`](https://pypi.org/project/flake8-blind-except/)
- [`flake8-boolean-trap`](https://pypi.org/project/flake8-boolean-trap/)
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/)
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
- [`flake8-debugger`](https://pypi.org/project/flake8-debugger/)
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
- [`flake8-eradicate`](https://pypi.org/project/flake8-eradicate/)
- [`flake8-print`](https://pypi.org/project/flake8-print/)
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
- [`flake8-super`](https://pypi.org/project/flake8-super/)
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
- [`mccabe`](https://pypi.org/project/mccabe/)
Ruff can also replace [`isort`](https://pypi.org/project/isort/), [`yesqa`](https://github.com/asottile/yesqa),

View File

@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8_to_ruff"
version = "0.0.143"
version = "0.0.145"
dependencies = [
"anyhow",
"clap",
@@ -1975,7 +1975,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.143"
version = "0.0.145"
dependencies = [
"anyhow",
"bincode",

View File

@@ -1,6 +1,6 @@
[package]
name = "flake8-to-ruff"
version = "0.0.143-dev.0"
version = "0.0.145-dev.0"
edition = "2021"
[lib]

View File

@@ -6,18 +6,19 @@ use ruff::checks_gen::CheckCodePrefix;
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Plugin {
Flake8Annotations,
Flake8Bandit,
Flake8BlindExcept,
Flake8Bugbear,
Flake8Builtins,
Flake8Comprehensions,
Flake8Debugger,
Flake8Docstrings,
Flake8TidyImports,
Flake8Eradicate,
Flake8Print,
Flake8Quotes,
Flake8Annotations,
Flake8TidyImports,
McCabe,
Flake8BlindExcept,
PEP8Naming,
Pyupgrade,
}
@@ -27,17 +28,18 @@ impl FromStr for Plugin {
fn from_str(string: &str) -> Result<Self, Self::Err> {
match string {
"flake8-annotations" => Ok(Plugin::Flake8Annotations),
"flake8-bandit" => Ok(Plugin::Flake8Bandit),
"flake8-blind-except" => Ok(Plugin::Flake8BlindExcept),
"flake8-bugbear" => Ok(Plugin::Flake8Bugbear),
"flake8-builtins" => Ok(Plugin::Flake8Builtins),
"flake8-comprehensions" => Ok(Plugin::Flake8Comprehensions),
"flake8-debugger" => Ok(Plugin::Flake8Debugger),
"flake8-docstrings" => Ok(Plugin::Flake8Docstrings),
"flake8-tidy-imports" => Ok(Plugin::Flake8TidyImports),
"flake8-eradicate" => Ok(Plugin::Flake8BlindExcept),
"flake8-print" => Ok(Plugin::Flake8Print),
"flake8-quotes" => Ok(Plugin::Flake8Quotes),
"flake8-annotations" => Ok(Plugin::Flake8Annotations),
"flake8-blind-except" => Ok(Plugin::Flake8BlindExcept),
"flake8-tidy-imports" => Ok(Plugin::Flake8TidyImports),
"mccabe" => Ok(Plugin::McCabe),
"pep8-naming" => Ok(Plugin::PEP8Naming),
"pyupgrade" => Ok(Plugin::Pyupgrade),
@@ -49,17 +51,18 @@ impl FromStr for Plugin {
impl Plugin {
pub fn default(&self) -> CheckCodePrefix {
match self {
Plugin::Flake8Annotations => CheckCodePrefix::ANN,
Plugin::Flake8Bandit => CheckCodePrefix::S,
Plugin::Flake8BlindExcept => CheckCodePrefix::BLE,
Plugin::Flake8Bugbear => CheckCodePrefix::B,
Plugin::Flake8Builtins => CheckCodePrefix::A,
Plugin::Flake8Comprehensions => CheckCodePrefix::C4,
Plugin::Flake8Debugger => CheckCodePrefix::T1,
Plugin::Flake8Docstrings => CheckCodePrefix::D,
Plugin::Flake8TidyImports => CheckCodePrefix::I25,
Plugin::Flake8Eradicate => CheckCodePrefix::ERA,
Plugin::Flake8Print => CheckCodePrefix::T2,
Plugin::Flake8Quotes => CheckCodePrefix::Q,
Plugin::Flake8Annotations => CheckCodePrefix::ANN,
Plugin::Flake8BlindExcept => CheckCodePrefix::BLE,
Plugin::Flake8TidyImports => CheckCodePrefix::I25,
Plugin::McCabe => CheckCodePrefix::C9,
Plugin::PEP8Naming => CheckCodePrefix::N,
Plugin::Pyupgrade => CheckCodePrefix::U,
@@ -68,7 +71,9 @@ impl Plugin {
pub fn select(&self, flake8: &HashMap<String, Option<String>>) -> Vec<CheckCodePrefix> {
match self {
Plugin::Flake8Annotations => vec![CheckCodePrefix::ANN],
Plugin::Flake8Bandit => vec![CheckCodePrefix::S],
Plugin::Flake8BlindExcept => vec![CheckCodePrefix::BLE],
Plugin::Flake8Bugbear => vec![CheckCodePrefix::B],
Plugin::Flake8Builtins => vec![CheckCodePrefix::A],
Plugin::Flake8Comprehensions => vec![CheckCodePrefix::C4],
@@ -89,11 +94,10 @@ impl Plugin {
// Default to PEP8.
DocstringConvention::PEP8.select()
}
Plugin::Flake8TidyImports => vec![CheckCodePrefix::I25],
Plugin::Flake8Eradicate => vec![CheckCodePrefix::ERA],
Plugin::Flake8Print => vec![CheckCodePrefix::T2],
Plugin::Flake8Quotes => vec![CheckCodePrefix::Q],
Plugin::Flake8Annotations => vec![CheckCodePrefix::ANN],
Plugin::Flake8BlindExcept => vec![CheckCodePrefix::BLE],
Plugin::Flake8TidyImports => vec![CheckCodePrefix::I25],
Plugin::McCabe => vec![CheckCodePrefix::C9],
Plugin::PEP8Naming => vec![CheckCodePrefix::N],
Plugin::Pyupgrade => vec![CheckCodePrefix::U],
@@ -281,31 +285,6 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
let mut plugins = BTreeSet::new();
for key in flake8.keys() {
match key.as_str() {
// flake8-docstrings
"docstring-convention" | "docstring_convention" => {
plugins.insert(Plugin::Flake8Docstrings);
}
// flake8-bugbear
"extend-immutable-calls" | "extend_immutable_calls" => {
plugins.insert(Plugin::Flake8Bugbear);
}
// flake8-builtins
"builtins-ignorelist" | "builtins_ignorelist" => {
plugins.insert(Plugin::Flake8Builtins);
}
// flake8-quotes
"quotes" | "inline-quotes" | "inline_quotes" => {
plugins.insert(Plugin::Flake8Quotes);
}
"multiline-quotes" | "multiline_quotes" => {
plugins.insert(Plugin::Flake8Quotes);
}
"docstring-quotes" | "docstring_quotes" => {
plugins.insert(Plugin::Flake8Quotes);
}
"avoid-escape" | "avoid_escape" => {
plugins.insert(Plugin::Flake8Quotes);
}
// flake8-annotations
"suppress-none-returning" | "suppress_none_returning" => {
plugins.insert(Plugin::Flake8Annotations);
@@ -331,6 +310,41 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
"allow-star-arg-any" | "allow_star_arg_any" => {
plugins.insert(Plugin::Flake8Annotations);
}
// flake8-bugbear
"extend-immutable-calls" | "extend_immutable_calls" => {
plugins.insert(Plugin::Flake8Bugbear);
}
// flake8-builtins
"builtins-ignorelist" | "builtins_ignorelist" => {
plugins.insert(Plugin::Flake8Builtins);
}
// flake8-docstrings
"docstring-convention" | "docstring_convention" => {
plugins.insert(Plugin::Flake8Docstrings);
}
// flake8-eradicate
"eradicate-aggressive" | "eradicate_aggressive" => {
plugins.insert(Plugin::Flake8Eradicate);
}
"eradicate-whitelist" | "eradicate_whitelist" => {
plugins.insert(Plugin::Flake8Eradicate);
}
"eradicate-whitelist-extend" | "eradicate_whitelist_extend" => {
plugins.insert(Plugin::Flake8Eradicate);
}
// flake8-quotes
"quotes" | "inline-quotes" | "inline_quotes" => {
plugins.insert(Plugin::Flake8Quotes);
}
"multiline-quotes" | "multiline_quotes" => {
plugins.insert(Plugin::Flake8Quotes);
}
"docstring-quotes" | "docstring_quotes" => {
plugins.insert(Plugin::Flake8Quotes);
}
"avoid-escape" | "avoid_escape" => {
plugins.insert(Plugin::Flake8Quotes);
}
// flake8-tidy-imports
"ban-relative-imports" | "ban_relative_imports" => {
plugins.insert(Plugin::Flake8TidyImports);
@@ -364,17 +378,18 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
/// `flake8-annotations` is active.
pub fn infer_plugins_from_codes(codes: &BTreeSet<CheckCodePrefix>) -> Vec<Plugin> {
[
Plugin::Flake8Annotations,
Plugin::Flake8Bandit,
Plugin::Flake8BlindExcept,
Plugin::Flake8Bugbear,
Plugin::Flake8Builtins,
Plugin::Flake8Comprehensions,
Plugin::Flake8Debugger,
Plugin::Flake8Docstrings,
Plugin::Flake8TidyImports,
Plugin::Flake8Eradicate,
Plugin::Flake8Print,
Plugin::Flake8Quotes,
Plugin::Flake8Annotations,
Plugin::Flake8BlindExcept,
Plugin::Flake8TidyImports,
Plugin::PEP8Naming,
Plugin::Pyupgrade,
]

View File

@@ -52,3 +52,6 @@ sit amet consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labor
# OK
# A very long URL: https://loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong.url.com
# OK
# https://loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong.url.com

13
resources/test/fixtures/ERA001.py vendored Normal file
View File

@@ -0,0 +1,13 @@
#import os
# from foo import junk
#a = 3
a = 4
#foo(1, 2, 3)
def foo(x, y, z):
contentet = 1 # print('hello')
print(x, y, z)
# This is a real comment.
#return True
return False

View File

@@ -38,5 +38,20 @@ def function(
def used(do):
return do
used("a", True)
used(do=True)
# Avoid FBT003 for explicitly allowed methods.
"""
FBT003 Boolean positional value on dict
"""
a = {"a": "b"}
a.get("hello", False)
{}.get("hello", False)
{}.setdefault("hello", True)
{}.pop("hello", False)
{}.pop(True, False)
dict.fromkeys(("world",), True)
{}.deploy(True, False)

View File

@@ -1,6 +1,7 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import List, Optional
from models import (
Fruit,
@@ -28,3 +29,12 @@ class Foo:
@classmethod
def d(cls) -> Fruit:
return cls(x=0, y=0)
def f(x: int) -> List[int]:
y = List[int]()
y.append(x)
return y
x: Optional[int] = None

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff_dev"
version = "0.0.143"
version = "0.0.145"
edition = "2021"
[dependencies]

View File

@@ -78,6 +78,7 @@ pub struct Checker<'a> {
in_f_string: Option<Range>,
in_annotation: bool,
in_deferred_string_annotation: bool,
in_deferred_annotation: bool,
in_literal: bool,
in_subscript: bool,
in_withitem: bool,
@@ -124,6 +125,7 @@ impl<'a> Checker<'a> {
in_f_string: None,
in_annotation: false,
in_deferred_string_annotation: false,
in_deferred_annotation: false,
in_literal: false,
in_subscript: false,
in_withitem: false,
@@ -1191,10 +1193,13 @@ where
// Pre-visit.
match &expr.node {
ExprKind::Subscript { value, slice, .. } => {
// Ex) typing.List[...]
// Ex) Optional[...]
if !self.in_deferred_string_annotation
&& self.settings.enabled.contains(&CheckCode::U007)
&& self.settings.target_version >= PythonVersion::Py310
&& (self.settings.target_version >= PythonVersion::Py310
|| (self.settings.target_version >= PythonVersion::Py37
&& self.annotations_future_enabled
&& self.in_deferred_annotation))
{
pyupgrade::plugins::use_pep604_annotation(self, expr, value, slice);
}
@@ -1233,7 +1238,10 @@ where
// Ex) List[...]
if !self.in_deferred_string_annotation
&& self.settings.enabled.contains(&CheckCode::U006)
&& self.settings.target_version >= PythonVersion::Py39
&& (self.settings.target_version >= PythonVersion::Py39
|| (self.settings.target_version >= PythonVersion::Py37
&& self.annotations_future_enabled
&& self.in_deferred_annotation))
&& typing::is_pep585_builtin(
expr,
&self.from_imports,
@@ -1268,8 +1276,12 @@ where
}
ExprKind::Attribute { attr, .. } => {
// Ex) typing.List[...]
if self.settings.enabled.contains(&CheckCode::U006)
&& self.settings.target_version >= PythonVersion::Py39
if !self.in_deferred_string_annotation
&& self.settings.enabled.contains(&CheckCode::U006)
&& (self.settings.target_version >= PythonVersion::Py39
|| (self.settings.target_version >= PythonVersion::Py37
&& self.annotations_future_enabled
&& self.in_deferred_annotation))
&& typing::is_pep585_builtin(expr, &self.from_imports, &self.import_aliases)
{
pyupgrade::plugins::use_pep585_annotation(self, expr, attr);
@@ -1638,7 +1650,7 @@ where
// flake8-boolean-trap
if self.settings.enabled.contains(&CheckCode::FBT003) {
flake8_boolean_trap::plugins::check_boolean_positional_value_in_function_call(
self, args,
self, args, func,
);
}
if let ExprKind::Name { id, ctx } = &func.node {
@@ -2743,7 +2755,9 @@ impl<'a> Checker<'a> {
while let Some((expr, scopes, parents)) = self.deferred_annotations.pop() {
self.scope_stack = scopes;
self.parent_stack = parents;
self.in_deferred_annotation = true;
self.visit_expr(expr);
self.in_deferred_annotation = false;
}
}
@@ -2769,9 +2783,9 @@ impl<'a> Checker<'a> {
}
}
for (expr, (scopes, parents)) in allocator.iter().zip(stacks) {
self.in_deferred_string_annotation = true;
self.scope_stack = scopes;
self.parent_stack = parents;
self.in_deferred_string_annotation = true;
self.visit_expr(expr);
self.in_deferred_string_annotation = false;
}

View File

@@ -14,9 +14,9 @@ use crate::settings::Settings;
// Regex from PEP263
static CODING_COMMENT_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^[ \t\f]*#.*?coding[:=][ \t]*utf-?8").expect("Invalid regex"));
Lazy::new(|| Regex::new(r"^[ \t\f]*#.*?coding[:=][ \t]*utf-?8").unwrap());
static URL_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^https?://\S+$").expect("Invalid regex"));
static URL_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^https?://\S+$").unwrap());
/// Whether the given line is too long and should be reported.
fn should_enforce_line_length(line: &str, length: usize, limit: usize) -> bool {
@@ -25,7 +25,7 @@ fn should_enforce_line_length(line: &str, length: usize, limit: usize) -> bool {
if let (Some(first), Some(_)) = (chunks.next(), chunks.next()) {
// Do not enforce the line length for commented lines that end with a URL
// or contain only a single word.
!(first == "#" && chunks.last().map_or(false, |c| URL_REGEX.is_match(c)))
!(first == "#" && chunks.last().map_or(true, |c| URL_REGEX.is_match(c)))
} else {
// Single word / no printable chars - no way to make the line shorter
false

View File

@@ -6,7 +6,7 @@ use crate::checks::{Check, CheckCode};
use crate::lex::docstring_detection::StateMachine;
use crate::rules::checks::Context;
use crate::source_code_locator::SourceCodeLocator;
use crate::{flake8_quotes, pycodestyle, rules, Settings};
use crate::{eradicate, flake8_quotes, pycodestyle, rules, Settings};
pub fn check_tokens(
locator: &SourceCodeLocator,
@@ -23,6 +23,7 @@ pub fn check_tokens(
|| settings.enabled.contains(&CheckCode::Q001)
|| settings.enabled.contains(&CheckCode::Q002)
|| settings.enabled.contains(&CheckCode::Q003);
let enforce_commented_out_code = settings.enabled.contains(&CheckCode::ERA001);
let enforce_invalid_escape_sequence = settings.enabled.contains(&CheckCode::W605);
let mut state_machine = StateMachine::default();
@@ -72,6 +73,17 @@ pub fn check_tokens(
}
}
// eradicate
if enforce_commented_out_code {
if matches!(tok, Tok::Comment) {
if let Some(check) =
eradicate::checks::commented_out_code(locator, start, end, settings, autofix)
{
checks.push(check);
}
}
}
// W605
if enforce_invalid_escape_sequence {
if matches!(tok, Tok::String { .. }) {

View File

@@ -254,6 +254,8 @@ pub enum CheckCode {
N818,
// isort
I001,
// eradicate
ERA001,
// flake8-bandit
S101,
S102,
@@ -282,6 +284,7 @@ pub enum CheckCategory {
Pydocstyle,
Pyupgrade,
PEP8Naming,
Eradicate,
Flake8Bandit,
Flake8Comprehensions,
Flake8Debugger,
@@ -302,69 +305,71 @@ pub enum CheckCategory {
impl CheckCategory {
pub fn title(&self) -> &'static str {
match self {
CheckCategory::Pycodestyle => "pycodestyle",
CheckCategory::Pyflakes => "Pyflakes",
CheckCategory::Isort => "isort",
CheckCategory::Eradicate => "eradicate",
CheckCategory::Flake82020 => "flake8-2020",
CheckCategory::Flake8Annotations => "flake8-annotations",
CheckCategory::Flake8Bandit => "flake8-bandit",
CheckCategory::Flake8BlindExcept => "flake8-blind-except",
CheckCategory::Flake8BooleanTrap => "flake8-boolean-trap",
CheckCategory::Flake8Builtins => "flake8-builtins",
CheckCategory::Flake8Bugbear => "flake8-bugbear",
CheckCategory::Flake8Builtins => "flake8-builtins",
CheckCategory::Flake8Comprehensions => "flake8-comprehensions",
CheckCategory::Flake8Debugger => "flake8-debugger",
CheckCategory::Flake8TidyImports => "flake8-tidy-imports",
CheckCategory::Flake8Print => "flake8-print",
CheckCategory::Flake8Quotes => "flake8-quotes",
CheckCategory::Flake8Annotations => "flake8-annotations",
CheckCategory::Flake82020 => "flake8-2020",
CheckCategory::Flake8BlindExcept => "flake8-blind-except",
CheckCategory::Pyupgrade => "pyupgrade",
CheckCategory::Pydocstyle => "pydocstyle",
CheckCategory::PEP8Naming => "pep8-naming",
CheckCategory::Flake8TidyImports => "flake8-tidy-imports",
CheckCategory::Isort => "isort",
CheckCategory::McCabe => "mccabe",
CheckCategory::Ruff => "Ruff-specific rules",
CheckCategory::Meta => "Meta rules",
CheckCategory::PEP8Naming => "pep8-naming",
CheckCategory::Pycodestyle => "pycodestyle",
CheckCategory::Pydocstyle => "pydocstyle",
CheckCategory::Pyflakes => "Pyflakes",
CheckCategory::Pyupgrade => "pyupgrade",
CheckCategory::Ruff => "Ruff-specific rules",
}
}
pub fn url(&self) -> Option<&'static str> {
match self {
CheckCategory::Pycodestyle => Some("https://pypi.org/project/pycodestyle/2.9.1/"),
CheckCategory::Pyflakes => Some("https://pypi.org/project/pyflakes/2.5.0/"),
CheckCategory::Isort => Some("https://pypi.org/project/isort/5.10.1/"),
CheckCategory::Flake8Builtins => {
Some("https://pypi.org/project/flake8-builtins/2.0.1/")
CheckCategory::Eradicate => Some("https://pypi.org/project/eradicate/2.1.0/"),
CheckCategory::Flake82020 => Some("https://pypi.org/project/flake8-2020/1.7.0/"),
CheckCategory::Flake8Annotations => {
Some("https://pypi.org/project/flake8-annotations/2.9.1/")
}
CheckCategory::Flake8Bandit => Some("https://pypi.org/project/flake8-bandit/4.1.1/"),
CheckCategory::Flake8BlindExcept => {
Some("https://pypi.org/project/flake8-blind-except/0.2.1/")
}
CheckCategory::Flake8BooleanTrap => {
Some("https://pypi.org/project/flake8-boolean-trap/0.1.0/")
}
CheckCategory::Flake8Bugbear => {
Some("https://pypi.org/project/flake8-bugbear/22.10.27/")
}
CheckCategory::Flake8Builtins => {
Some("https://pypi.org/project/flake8-builtins/2.0.1/")
}
CheckCategory::Flake8Comprehensions => {
Some("https://pypi.org/project/flake8-comprehensions/3.10.1/")
}
CheckCategory::Flake8Debugger => {
Some("https://pypi.org/project/flake8-debugger/4.1.2/")
}
CheckCategory::Flake8Print => Some("https://pypi.org/project/flake8-print/5.0.0/"),
CheckCategory::Flake8Quotes => Some("https://pypi.org/project/flake8-quotes/3.3.1/"),
CheckCategory::Flake8TidyImports => {
Some("https://pypi.org/project/flake8-tidy-imports/4.8.0/")
}
CheckCategory::Flake8Print => Some("https://pypi.org/project/flake8-print/5.0.0/"),
CheckCategory::Flake8Quotes => Some("https://pypi.org/project/flake8-quotes/3.3.1/"),
CheckCategory::Flake8Annotations => {
Some("https://pypi.org/project/flake8-annotations/2.9.1/")
}
CheckCategory::Flake82020 => Some("https://pypi.org/project/flake8-2020/1.7.0/"),
CheckCategory::Pyupgrade => Some("https://pypi.org/project/pyupgrade/3.2.0/"),
CheckCategory::Pydocstyle => Some("https://pypi.org/project/pydocstyle/6.1.1/"),
CheckCategory::PEP8Naming => Some("https://pypi.org/project/pep8-naming/0.13.2/"),
CheckCategory::Flake8Bandit => Some("https://pypi.org/project/flake8-bandit/4.1.1/"),
CheckCategory::Flake8BlindExcept => {
Some("https://pypi.org/project/flake8-blind-except/0.2.1/")
}
CheckCategory::Isort => Some("https://pypi.org/project/isort/5.10.1/"),
CheckCategory::McCabe => Some("https://pypi.org/project/mccabe/0.7.0/"),
CheckCategory::Flake8BooleanTrap => {
Some("https://pypi.org/project/flake8-boolean-trap/0.1.0/")
}
CheckCategory::Ruff => None,
CheckCategory::Meta => None,
CheckCategory::PEP8Naming => Some("https://pypi.org/project/pep8-naming/0.13.2/"),
CheckCategory::Pycodestyle => Some("https://pypi.org/project/pycodestyle/2.9.1/"),
CheckCategory::Pydocstyle => Some("https://pypi.org/project/pydocstyle/6.1.1/"),
CheckCategory::Pyflakes => Some("https://pypi.org/project/pyflakes/2.5.0/"),
CheckCategory::Pyupgrade => Some("https://pypi.org/project/pyupgrade/3.2.0/"),
CheckCategory::Ruff => None,
}
}
}
@@ -611,6 +616,8 @@ pub enum CheckKind {
ErrorSuffixOnExceptionName(String),
// isort
UnsortedImports,
// eradicate
CommentedOutCode,
// flake8-bandit
AssertUsed,
ExecUsed,
@@ -641,7 +648,8 @@ impl CheckCode {
CheckCode::E501 | CheckCode::W292 | CheckCode::M001 | CheckCode::U009 => {
&LintSource::Lines
}
CheckCode::Q000
CheckCode::ERA001
| CheckCode::Q000
| CheckCode::Q001
| CheckCode::Q002
| CheckCode::Q003
@@ -925,6 +933,8 @@ impl CheckCode {
CheckCode::N818 => CheckKind::ErrorSuffixOnExceptionName("...".to_string()),
// isort
CheckCode::I001 => CheckKind::UnsortedImports,
// eradicate
CheckCode::ERA001 => CheckKind::CommentedOutCode,
// flake8-bandit
CheckCode::S101 => CheckKind::AssertUsed,
CheckCode::S102 => CheckKind::ExecUsed,
@@ -950,67 +960,20 @@ impl CheckCode {
pub fn category(&self) -> CheckCategory {
#[allow(clippy::match_same_arms)]
match self {
CheckCode::E402 => CheckCategory::Pycodestyle,
CheckCode::E501 => CheckCategory::Pycodestyle,
CheckCode::E711 => CheckCategory::Pycodestyle,
CheckCode::E712 => CheckCategory::Pycodestyle,
CheckCode::E713 => CheckCategory::Pycodestyle,
CheckCode::E714 => CheckCategory::Pycodestyle,
CheckCode::E721 => CheckCategory::Pycodestyle,
CheckCode::E722 => CheckCategory::Pycodestyle,
CheckCode::E731 => CheckCategory::Pycodestyle,
CheckCode::E741 => CheckCategory::Pycodestyle,
CheckCode::E742 => CheckCategory::Pycodestyle,
CheckCode::E743 => CheckCategory::Pycodestyle,
CheckCode::E902 => CheckCategory::Pycodestyle,
CheckCode::E999 => CheckCategory::Pycodestyle,
CheckCode::W292 => CheckCategory::Pycodestyle,
CheckCode::W605 => CheckCategory::Pycodestyle,
CheckCode::F401 => CheckCategory::Pyflakes,
CheckCode::F402 => CheckCategory::Pyflakes,
CheckCode::F403 => CheckCategory::Pyflakes,
CheckCode::F404 => CheckCategory::Pyflakes,
CheckCode::F405 => CheckCategory::Pyflakes,
CheckCode::F406 => CheckCategory::Pyflakes,
CheckCode::F407 => CheckCategory::Pyflakes,
CheckCode::F501 => CheckCategory::Pyflakes,
CheckCode::F502 => CheckCategory::Pyflakes,
CheckCode::F503 => CheckCategory::Pyflakes,
CheckCode::F504 => CheckCategory::Pyflakes,
CheckCode::F505 => CheckCategory::Pyflakes,
CheckCode::F506 => CheckCategory::Pyflakes,
CheckCode::F507 => CheckCategory::Pyflakes,
CheckCode::F508 => CheckCategory::Pyflakes,
CheckCode::F509 => CheckCategory::Pyflakes,
CheckCode::F521 => CheckCategory::Pyflakes,
CheckCode::F522 => CheckCategory::Pyflakes,
CheckCode::F523 => CheckCategory::Pyflakes,
CheckCode::F524 => CheckCategory::Pyflakes,
CheckCode::F525 => CheckCategory::Pyflakes,
CheckCode::F541 => CheckCategory::Pyflakes,
CheckCode::F601 => CheckCategory::Pyflakes,
CheckCode::F602 => CheckCategory::Pyflakes,
CheckCode::F621 => CheckCategory::Pyflakes,
CheckCode::F622 => CheckCategory::Pyflakes,
CheckCode::F631 => CheckCategory::Pyflakes,
CheckCode::F632 => CheckCategory::Pyflakes,
CheckCode::F633 => CheckCategory::Pyflakes,
CheckCode::F634 => CheckCategory::Pyflakes,
CheckCode::F701 => CheckCategory::Pyflakes,
CheckCode::F702 => CheckCategory::Pyflakes,
CheckCode::F704 => CheckCategory::Pyflakes,
CheckCode::F706 => CheckCategory::Pyflakes,
CheckCode::F707 => CheckCategory::Pyflakes,
CheckCode::F722 => CheckCategory::Pyflakes,
CheckCode::F821 => CheckCategory::Pyflakes,
CheckCode::F822 => CheckCategory::Pyflakes,
CheckCode::F823 => CheckCategory::Pyflakes,
CheckCode::F831 => CheckCategory::Pyflakes,
CheckCode::F841 => CheckCategory::Pyflakes,
CheckCode::F901 => CheckCategory::Pyflakes,
CheckCode::A001 => CheckCategory::Flake8Builtins,
CheckCode::A002 => CheckCategory::Flake8Builtins,
CheckCode::A003 => CheckCategory::Flake8Builtins,
CheckCode::ANN001 => CheckCategory::Flake8Annotations,
CheckCode::ANN002 => CheckCategory::Flake8Annotations,
CheckCode::ANN003 => CheckCategory::Flake8Annotations,
CheckCode::ANN101 => CheckCategory::Flake8Annotations,
CheckCode::ANN102 => CheckCategory::Flake8Annotations,
CheckCode::ANN201 => CheckCategory::Flake8Annotations,
CheckCode::ANN202 => CheckCategory::Flake8Annotations,
CheckCode::ANN204 => CheckCategory::Flake8Annotations,
CheckCode::ANN205 => CheckCategory::Flake8Annotations,
CheckCode::ANN206 => CheckCategory::Flake8Annotations,
CheckCode::ANN401 => CheckCategory::Flake8Annotations,
CheckCode::B002 => CheckCategory::Flake8Bugbear,
CheckCode::B003 => CheckCategory::Flake8Bugbear,
CheckCode::B004 => CheckCategory::Flake8Bugbear,
@@ -1055,49 +1018,7 @@ impl CheckCode {
CheckCode::C415 => CheckCategory::Flake8Comprehensions,
CheckCode::C416 => CheckCategory::Flake8Comprehensions,
CheckCode::C417 => CheckCategory::Flake8Comprehensions,
CheckCode::T100 => CheckCategory::Flake8Debugger,
CheckCode::I252 => CheckCategory::Flake8TidyImports,
CheckCode::T201 => CheckCategory::Flake8Print,
CheckCode::T203 => CheckCategory::Flake8Print,
CheckCode::Q000 => CheckCategory::Flake8Quotes,
CheckCode::Q001 => CheckCategory::Flake8Quotes,
CheckCode::Q002 => CheckCategory::Flake8Quotes,
CheckCode::Q003 => CheckCategory::Flake8Quotes,
CheckCode::ANN001 => CheckCategory::Flake8Annotations,
CheckCode::ANN002 => CheckCategory::Flake8Annotations,
CheckCode::ANN003 => CheckCategory::Flake8Annotations,
CheckCode::ANN101 => CheckCategory::Flake8Annotations,
CheckCode::ANN102 => CheckCategory::Flake8Annotations,
CheckCode::ANN201 => CheckCategory::Flake8Annotations,
CheckCode::ANN202 => CheckCategory::Flake8Annotations,
CheckCode::ANN204 => CheckCategory::Flake8Annotations,
CheckCode::ANN205 => CheckCategory::Flake8Annotations,
CheckCode::ANN206 => CheckCategory::Flake8Annotations,
CheckCode::ANN401 => CheckCategory::Flake8Annotations,
CheckCode::YTT101 => CheckCategory::Flake82020,
CheckCode::YTT102 => CheckCategory::Flake82020,
CheckCode::YTT103 => CheckCategory::Flake82020,
CheckCode::YTT201 => CheckCategory::Flake82020,
CheckCode::YTT202 => CheckCategory::Flake82020,
CheckCode::YTT203 => CheckCategory::Flake82020,
CheckCode::YTT204 => CheckCategory::Flake82020,
CheckCode::YTT301 => CheckCategory::Flake82020,
CheckCode::YTT302 => CheckCategory::Flake82020,
CheckCode::YTT303 => CheckCategory::Flake82020,
CheckCode::U001 => CheckCategory::Pyupgrade,
CheckCode::U003 => CheckCategory::Pyupgrade,
CheckCode::U004 => CheckCategory::Pyupgrade,
CheckCode::U005 => CheckCategory::Pyupgrade,
CheckCode::U006 => CheckCategory::Pyupgrade,
CheckCode::U007 => CheckCategory::Pyupgrade,
CheckCode::U008 => CheckCategory::Pyupgrade,
CheckCode::U009 => CheckCategory::Pyupgrade,
CheckCode::U010 => CheckCategory::Pyupgrade,
CheckCode::U011 => CheckCategory::Pyupgrade,
CheckCode::U012 => CheckCategory::Pyupgrade,
CheckCode::U013 => CheckCategory::Pyupgrade,
CheckCode::U014 => CheckCategory::Pyupgrade,
CheckCode::U015 => CheckCategory::Pyupgrade,
CheckCode::C901 => CheckCategory::McCabe,
CheckCode::D100 => CheckCategory::Pydocstyle,
CheckCode::D101 => CheckCategory::Pydocstyle,
CheckCode::D102 => CheckCategory::Pydocstyle,
@@ -1142,6 +1063,69 @@ impl CheckCode {
CheckCode::D417 => CheckCategory::Pydocstyle,
CheckCode::D418 => CheckCategory::Pydocstyle,
CheckCode::D419 => CheckCategory::Pydocstyle,
CheckCode::E402 => CheckCategory::Pycodestyle,
CheckCode::E501 => CheckCategory::Pycodestyle,
CheckCode::E711 => CheckCategory::Pycodestyle,
CheckCode::E712 => CheckCategory::Pycodestyle,
CheckCode::E713 => CheckCategory::Pycodestyle,
CheckCode::E714 => CheckCategory::Pycodestyle,
CheckCode::E721 => CheckCategory::Pycodestyle,
CheckCode::E722 => CheckCategory::Pycodestyle,
CheckCode::E731 => CheckCategory::Pycodestyle,
CheckCode::E741 => CheckCategory::Pycodestyle,
CheckCode::E742 => CheckCategory::Pycodestyle,
CheckCode::E743 => CheckCategory::Pycodestyle,
CheckCode::E902 => CheckCategory::Pycodestyle,
CheckCode::E999 => CheckCategory::Pycodestyle,
CheckCode::ERA001 => CheckCategory::Eradicate,
CheckCode::F401 => CheckCategory::Pyflakes,
CheckCode::F402 => CheckCategory::Pyflakes,
CheckCode::F403 => CheckCategory::Pyflakes,
CheckCode::F404 => CheckCategory::Pyflakes,
CheckCode::F405 => CheckCategory::Pyflakes,
CheckCode::F406 => CheckCategory::Pyflakes,
CheckCode::F407 => CheckCategory::Pyflakes,
CheckCode::F501 => CheckCategory::Pyflakes,
CheckCode::F502 => CheckCategory::Pyflakes,
CheckCode::F503 => CheckCategory::Pyflakes,
CheckCode::F504 => CheckCategory::Pyflakes,
CheckCode::F505 => CheckCategory::Pyflakes,
CheckCode::F506 => CheckCategory::Pyflakes,
CheckCode::F507 => CheckCategory::Pyflakes,
CheckCode::F508 => CheckCategory::Pyflakes,
CheckCode::F509 => CheckCategory::Pyflakes,
CheckCode::F521 => CheckCategory::Pyflakes,
CheckCode::F522 => CheckCategory::Pyflakes,
CheckCode::F523 => CheckCategory::Pyflakes,
CheckCode::F524 => CheckCategory::Pyflakes,
CheckCode::F525 => CheckCategory::Pyflakes,
CheckCode::F541 => CheckCategory::Pyflakes,
CheckCode::F601 => CheckCategory::Pyflakes,
CheckCode::F602 => CheckCategory::Pyflakes,
CheckCode::F621 => CheckCategory::Pyflakes,
CheckCode::F622 => CheckCategory::Pyflakes,
CheckCode::F631 => CheckCategory::Pyflakes,
CheckCode::F632 => CheckCategory::Pyflakes,
CheckCode::F633 => CheckCategory::Pyflakes,
CheckCode::F634 => CheckCategory::Pyflakes,
CheckCode::F701 => CheckCategory::Pyflakes,
CheckCode::F702 => CheckCategory::Pyflakes,
CheckCode::F704 => CheckCategory::Pyflakes,
CheckCode::F706 => CheckCategory::Pyflakes,
CheckCode::F707 => CheckCategory::Pyflakes,
CheckCode::F722 => CheckCategory::Pyflakes,
CheckCode::F821 => CheckCategory::Pyflakes,
CheckCode::F822 => CheckCategory::Pyflakes,
CheckCode::F823 => CheckCategory::Pyflakes,
CheckCode::F831 => CheckCategory::Pyflakes,
CheckCode::F841 => CheckCategory::Pyflakes,
CheckCode::F901 => CheckCategory::Pyflakes,
CheckCode::FBT001 => CheckCategory::Flake8BooleanTrap,
CheckCode::FBT002 => CheckCategory::Flake8BooleanTrap,
CheckCode::FBT003 => CheckCategory::Flake8BooleanTrap,
CheckCode::I001 => CheckCategory::Isort,
CheckCode::I252 => CheckCategory::Flake8TidyImports,
CheckCode::M001 => CheckCategory::Meta,
CheckCode::N801 => CheckCategory::PEP8Naming,
CheckCode::N802 => CheckCategory::PEP8Naming,
CheckCode::N803 => CheckCategory::PEP8Naming,
@@ -1157,22 +1141,49 @@ impl CheckCode {
CheckCode::N816 => CheckCategory::PEP8Naming,
CheckCode::N817 => CheckCategory::PEP8Naming,
CheckCode::N818 => CheckCategory::PEP8Naming,
CheckCode::I001 => CheckCategory::Isort,
CheckCode::Q000 => CheckCategory::Flake8Quotes,
CheckCode::Q001 => CheckCategory::Flake8Quotes,
CheckCode::Q002 => CheckCategory::Flake8Quotes,
CheckCode::Q003 => CheckCategory::Flake8Quotes,
CheckCode::RUF001 => CheckCategory::Ruff,
CheckCode::RUF002 => CheckCategory::Ruff,
CheckCode::RUF003 => CheckCategory::Ruff,
CheckCode::RUF101 => CheckCategory::Ruff,
CheckCode::S101 => CheckCategory::Flake8Bandit,
CheckCode::S102 => CheckCategory::Flake8Bandit,
CheckCode::S104 => CheckCategory::Flake8Bandit,
CheckCode::S105 => CheckCategory::Flake8Bandit,
CheckCode::S106 => CheckCategory::Flake8Bandit,
CheckCode::S107 => CheckCategory::Flake8Bandit,
CheckCode::C901 => CheckCategory::McCabe,
CheckCode::FBT001 => CheckCategory::Flake8BooleanTrap,
CheckCode::FBT002 => CheckCategory::Flake8BooleanTrap,
CheckCode::FBT003 => CheckCategory::Flake8BooleanTrap,
CheckCode::RUF001 => CheckCategory::Ruff,
CheckCode::RUF002 => CheckCategory::Ruff,
CheckCode::RUF003 => CheckCategory::Ruff,
CheckCode::RUF101 => CheckCategory::Ruff,
CheckCode::M001 => CheckCategory::Meta,
CheckCode::T100 => CheckCategory::Flake8Debugger,
CheckCode::T201 => CheckCategory::Flake8Print,
CheckCode::T203 => CheckCategory::Flake8Print,
CheckCode::U001 => CheckCategory::Pyupgrade,
CheckCode::U003 => CheckCategory::Pyupgrade,
CheckCode::U004 => CheckCategory::Pyupgrade,
CheckCode::U005 => CheckCategory::Pyupgrade,
CheckCode::U006 => CheckCategory::Pyupgrade,
CheckCode::U007 => CheckCategory::Pyupgrade,
CheckCode::U008 => CheckCategory::Pyupgrade,
CheckCode::U009 => CheckCategory::Pyupgrade,
CheckCode::U010 => CheckCategory::Pyupgrade,
CheckCode::U011 => CheckCategory::Pyupgrade,
CheckCode::U012 => CheckCategory::Pyupgrade,
CheckCode::U013 => CheckCategory::Pyupgrade,
CheckCode::U014 => CheckCategory::Pyupgrade,
CheckCode::U015 => CheckCategory::Pyupgrade,
CheckCode::W292 => CheckCategory::Pycodestyle,
CheckCode::W605 => CheckCategory::Pycodestyle,
CheckCode::YTT101 => CheckCategory::Flake82020,
CheckCode::YTT102 => CheckCategory::Flake82020,
CheckCode::YTT103 => CheckCategory::Flake82020,
CheckCode::YTT201 => CheckCategory::Flake82020,
CheckCode::YTT202 => CheckCategory::Flake82020,
CheckCode::YTT203 => CheckCategory::Flake82020,
CheckCode::YTT204 => CheckCategory::Flake82020,
CheckCode::YTT301 => CheckCategory::Flake82020,
CheckCode::YTT302 => CheckCategory::Flake82020,
CheckCode::YTT303 => CheckCategory::Flake82020,
}
}
}
@@ -1405,6 +1416,8 @@ impl CheckKind {
CheckKind::ErrorSuffixOnExceptionName(..) => &CheckCode::N818,
// isort
CheckKind::UnsortedImports => &CheckCode::I001,
// eradicate
CheckKind::CommentedOutCode => &CheckCode::ERA001,
// flake8-bandit
CheckKind::AssertUsed => &CheckCode::S101,
CheckKind::ExecUsed => &CheckCode::S102,
@@ -2117,6 +2130,8 @@ impl CheckKind {
}
// isort
CheckKind::UnsortedImports => "Import block is un-sorted or un-formatted".to_string(),
// eradicate
CheckKind::CommentedOutCode => "Found commented-out code".to_string(),
// flake8-bandit
CheckKind::AssertUsed => "Use of `assert` detected".to_string(),
CheckKind::ExecUsed => "Use of `exec` detected".to_string(),
@@ -2221,6 +2236,7 @@ impl CheckKind {
| CheckKind::BlankLineAfterSummary
| CheckKind::BlankLineBeforeSection(..)
| CheckKind::CapitalizeSectionName(..)
| CheckKind::CommentedOutCode
| CheckKind::ConvertExitToSysExit
| CheckKind::ConvertNamedTupleFunctionalToClass(..)
| CheckKind::ConvertTypedDictFunctionalToClass(..)

View File

@@ -176,6 +176,10 @@ pub enum CheckCodePrefix {
E902,
E99,
E999,
ERA,
ERA8,
ERA80,
ERA001,
F,
F4,
F40,
@@ -187,6 +191,16 @@ pub enum CheckCodePrefix {
F406,
F407,
F5,
F50,
F501,
F502,
F503,
F504,
F505,
F506,
F507,
F508,
F509,
F52,
F521,
F522,
@@ -853,6 +867,10 @@ impl CheckCodePrefix {
CheckCodePrefix::E902 => vec![CheckCode::E902],
CheckCodePrefix::E99 => vec![CheckCode::E999],
CheckCodePrefix::E999 => vec![CheckCode::E999],
CheckCodePrefix::ERA => vec![CheckCode::ERA001],
CheckCodePrefix::ERA8 => vec![CheckCode::ERA001],
CheckCodePrefix::ERA80 => vec![CheckCode::ERA001],
CheckCodePrefix::ERA001 => vec![CheckCode::ERA001],
CheckCodePrefix::F => vec![
CheckCode::F401,
CheckCode::F402,
@@ -923,6 +941,15 @@ impl CheckCodePrefix {
CheckCodePrefix::F406 => vec![CheckCode::F406],
CheckCodePrefix::F407 => vec![CheckCode::F407],
CheckCodePrefix::F5 => vec![
CheckCode::F501,
CheckCode::F502,
CheckCode::F503,
CheckCode::F504,
CheckCode::F505,
CheckCode::F506,
CheckCode::F507,
CheckCode::F508,
CheckCode::F509,
CheckCode::F521,
CheckCode::F522,
CheckCode::F523,
@@ -930,6 +957,26 @@ impl CheckCodePrefix {
CheckCode::F525,
CheckCode::F541,
],
CheckCodePrefix::F50 => vec![
CheckCode::F501,
CheckCode::F502,
CheckCode::F503,
CheckCode::F504,
CheckCode::F505,
CheckCode::F506,
CheckCode::F507,
CheckCode::F508,
CheckCode::F509,
],
CheckCodePrefix::F501 => vec![CheckCode::F501],
CheckCodePrefix::F502 => vec![CheckCode::F502],
CheckCodePrefix::F503 => vec![CheckCode::F503],
CheckCodePrefix::F504 => vec![CheckCode::F504],
CheckCodePrefix::F505 => vec![CheckCode::F505],
CheckCodePrefix::F506 => vec![CheckCode::F506],
CheckCodePrefix::F507 => vec![CheckCode::F507],
CheckCodePrefix::F508 => vec![CheckCode::F508],
CheckCodePrefix::F509 => vec![CheckCode::F509],
CheckCodePrefix::F52 => vec![
CheckCode::F521,
CheckCode::F522,
@@ -1455,6 +1502,10 @@ impl CheckCodePrefix {
CheckCodePrefix::E902 => PrefixSpecificity::Explicit,
CheckCodePrefix::E99 => PrefixSpecificity::Tens,
CheckCodePrefix::E999 => PrefixSpecificity::Explicit,
CheckCodePrefix::ERA => PrefixSpecificity::Category,
CheckCodePrefix::ERA8 => PrefixSpecificity::Hundreds,
CheckCodePrefix::ERA80 => PrefixSpecificity::Tens,
CheckCodePrefix::ERA001 => PrefixSpecificity::Explicit,
CheckCodePrefix::F => PrefixSpecificity::Category,
CheckCodePrefix::F4 => PrefixSpecificity::Hundreds,
CheckCodePrefix::F40 => PrefixSpecificity::Tens,
@@ -1466,6 +1517,16 @@ impl CheckCodePrefix {
CheckCodePrefix::F406 => PrefixSpecificity::Explicit,
CheckCodePrefix::F407 => PrefixSpecificity::Explicit,
CheckCodePrefix::F5 => PrefixSpecificity::Hundreds,
CheckCodePrefix::F50 => PrefixSpecificity::Tens,
CheckCodePrefix::F501 => PrefixSpecificity::Explicit,
CheckCodePrefix::F502 => PrefixSpecificity::Explicit,
CheckCodePrefix::F503 => PrefixSpecificity::Explicit,
CheckCodePrefix::F504 => PrefixSpecificity::Explicit,
CheckCodePrefix::F505 => PrefixSpecificity::Explicit,
CheckCodePrefix::F506 => PrefixSpecificity::Explicit,
CheckCodePrefix::F507 => PrefixSpecificity::Explicit,
CheckCodePrefix::F508 => PrefixSpecificity::Explicit,
CheckCodePrefix::F509 => PrefixSpecificity::Explicit,
CheckCodePrefix::F52 => PrefixSpecificity::Tens,
CheckCodePrefix::F521 => PrefixSpecificity::Explicit,
CheckCodePrefix::F522 => PrefixSpecificity::Explicit,
@@ -1630,6 +1691,7 @@ pub const CATEGORIES: &[CheckCodePrefix] = &[
CheckCodePrefix::C,
CheckCodePrefix::D,
CheckCodePrefix::E,
CheckCodePrefix::ERA,
CheckCodePrefix::F,
CheckCodePrefix::FBT,
CheckCodePrefix::I,

51
src/eradicate/checks.rs Normal file
View File

@@ -0,0 +1,51 @@
use rustpython_ast::Location;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checks::{CheckCode, CheckKind};
use crate::eradicate::detection::comment_contains_code;
use crate::{Check, Settings, SourceCodeLocator};
fn is_standalone_comment(line: &str) -> bool {
for char in line.chars() {
if char == '#' {
return true;
} else if !char.is_whitespace() {
return false;
}
}
unreachable!("Comment should contain '#' character")
}
/// ERA001
pub fn commented_out_code(
locator: &SourceCodeLocator,
start: Location,
end: Location,
settings: &Settings,
autofix: bool,
) -> Option<Check> {
let location = Location::new(start.row(), 0);
let end_location = Location::new(end.row() + 1, 0);
let line = locator.slice_source_code_range(&Range {
location,
end_location,
});
// Verify that the comment is on its own line, and that it contains code.
if is_standalone_comment(&line) && comment_contains_code(&line) {
let mut check = Check::new(
CheckKind::CommentedOutCode,
Range {
location: start,
end_location: end,
},
);
if autofix && settings.fixable.contains(&CheckCode::ERA001) {
check.amend(Fix::deletion(location, end_location));
}
Some(check)
} else {
None
}
}

223
src/eradicate/detection.rs Normal file
View File

@@ -0,0 +1,223 @@
/// See: [eradicate.py](https://github.com/myint/eradicate/blob/98f199940979c94447a461d50d27862b118b282d/eradicate.py)
use once_cell::sync::Lazy;
use regex::Regex;
static ALLOWLIST_REGEX: Lazy<Regex> = Lazy::new(|| {
Regex::new(
r"(?i)pylint|pyright|noqa|nosec|type:\s*ignore|fmt:\s*(on|off)|isort:\s*(on|off|skip|skip_file|split|dont-add-imports(:\s*\[.*?])?)|TODO|FIXME|XXX"
).unwrap()
});
static BRACKET_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[()\[\]{}\s]+$").unwrap());
static CODE_INDICATORS: &[&str] = &[
"(", ")", "[", "]", "{", "}", ":", "=", "%", "print", "return", "break", "continue", "import",
];
static CODE_KEYWORDS: Lazy<Vec<Regex>> = Lazy::new(|| {
vec![
Regex::new(r"^\s*elif\s+.*\s*:\s*$").unwrap(),
Regex::new(r"^\s*else\s*:\s*$").unwrap(),
Regex::new(r"^\s*try\s*:\s*$").unwrap(),
Regex::new(r"^\s*finally\s*:\s*$").unwrap(),
Regex::new(r"^\s*except\s+.*\s*:\s*$").unwrap(),
]
});
static CODING_COMMENT_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)").unwrap());
static HASH_NUMBER: Lazy<Regex> = Lazy::new(|| Regex::new(r"#\d").unwrap());
static MULTILINE_ASSIGNMENT_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^\s*\w+\s*=.*[(\[{]$").unwrap());
static PARTIAL_DICTIONARY_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r#"^\s*['"]\w+['"]\s*:.+[,{]\s*$"#).unwrap());
static PRINT_RETURN_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(print|return)\b\s*").unwrap());
/// Returns `true` if a comment contains Python code.
pub fn comment_contains_code(line: &str) -> bool {
let line = if let Some(line) = line.trim().strip_prefix('#') {
line.trim()
} else {
return false;
};
// Ignore non-comment related hashes (e.g., "# Issue #999").
if HASH_NUMBER.is_match(line) {
return false;
}
// Ignore whitelisted comments.
if ALLOWLIST_REGEX.is_match(line) {
return false;
}
if CODING_COMMENT_REGEX.is_match(line) {
return false;
}
// Check that this is possibly code.
if CODE_INDICATORS.iter().all(|symbol| !line.contains(symbol)) {
return false;
}
if multiline_case(line) {
return true;
}
if CODE_KEYWORDS.iter().any(|symbol| symbol.is_match(line)) {
return true;
}
let line = PRINT_RETURN_REGEX.replace_all(line, "");
if PARTIAL_DICTIONARY_REGEX.is_match(&line) {
return true;
}
// Finally, compile the source code.
rustpython_parser::parser::parse_program(&line, "<filename>").is_ok()
}
/// Returns `true` if a line is probably part of some multiline code.
fn multiline_case(line: &str) -> bool {
if line.ends_with('\\') {
return true;
}
if MULTILINE_ASSIGNMENT_REGEX.is_match(line) {
return true;
}
if BRACKET_REGEX.is_match(line) {
return true;
}
false
}
#[cfg(test)]
mod tests {
use crate::eradicate::detection::comment_contains_code;
#[test]
fn comment_contains_code_basic() {
assert!(comment_contains_code("# x = 1"));
assert!(comment_contains_code("#from foo import eradicate"));
assert!(comment_contains_code("#import eradicate"));
assert!(comment_contains_code(r#"#"key": value,"#));
assert!(comment_contains_code(r#"#"key": "value","#));
assert!(comment_contains_code(r#"#"key": 1 + 1,"#));
assert!(comment_contains_code("#'key': 1 + 1,"));
assert!(comment_contains_code(r#"#"key": {"#));
assert!(comment_contains_code("#}"));
assert!(comment_contains_code("#} )]"));
assert!(!comment_contains_code("#"));
assert!(!comment_contains_code("# This is a (real) comment."));
assert!(!comment_contains_code("# 123"));
assert!(!comment_contains_code("# 123.1"));
assert!(!comment_contains_code("# 1, 2, 3"));
assert!(!comment_contains_code("x = 1 # x = 1"));
assert!(!comment_contains_code(
"# pylint: disable=redefined-outer-name"
));
assert!(!comment_contains_code("# Issue #999: This is not code"));
// TODO(charlie): This should be `true` under aggressive mode.
assert!(!comment_contains_code("#},"));
}
#[test]
fn comment_contains_code_with_print() {
assert!(comment_contains_code("#print"));
assert!(comment_contains_code("#print(1)"));
assert!(comment_contains_code("#print 1"));
assert!(!comment_contains_code("#to print"));
}
#[test]
fn comment_contains_code_with_return() {
assert!(comment_contains_code("#return x"));
assert!(!comment_contains_code("#to print"));
}
#[test]
fn comment_contains_code_with_multiline() {
assert!(comment_contains_code("#else:"));
assert!(comment_contains_code("# else : "));
assert!(comment_contains_code(r#"# "foo %d" % \\"#));
assert!(comment_contains_code("#elif True:"));
assert!(comment_contains_code("#x = foo("));
assert!(comment_contains_code("#except Exception:"));
assert!(!comment_contains_code("# this is = to that :("));
assert!(!comment_contains_code("#else"));
assert!(!comment_contains_code("#or else:"));
assert!(!comment_contains_code("#else True:"));
// TODO(charlie): This should be `true` under aggressive mode.
assert!(!comment_contains_code("#def foo():"));
}
#[test]
fn comment_contains_code_with_sentences() {
assert!(!comment_contains_code("#code is good"));
}
#[test]
fn comment_contains_code_with_encoding() {
assert!(comment_contains_code("# codings=utf-8"));
assert!(!comment_contains_code("# coding=utf-8"));
assert!(!comment_contains_code("#coding= utf-8"));
assert!(!comment_contains_code("# coding: utf-8"));
assert!(!comment_contains_code("# encoding: utf8"));
}
#[test]
fn comment_contains_code_with_default_allowlist() {
assert!(!comment_contains_code("# pylint: disable=A0123"));
assert!(!comment_contains_code("# pylint:disable=A0123"));
assert!(!comment_contains_code("# pylint: disable = A0123"));
assert!(!comment_contains_code("# pylint:disable = A0123"));
assert!(!comment_contains_code("# pyright: reportErrorName=true"));
assert!(!comment_contains_code("# noqa"));
assert!(!comment_contains_code("# NOQA"));
assert!(!comment_contains_code("# noqa: A123"));
assert!(!comment_contains_code("# noqa:A123"));
assert!(!comment_contains_code("# nosec"));
assert!(!comment_contains_code("# fmt: on"));
assert!(!comment_contains_code("# fmt: off"));
assert!(!comment_contains_code("# fmt:on"));
assert!(!comment_contains_code("# fmt:off"));
assert!(!comment_contains_code("# isort: on"));
assert!(!comment_contains_code("# isort:on"));
assert!(!comment_contains_code("# isort: off"));
assert!(!comment_contains_code("# isort:off"));
assert!(!comment_contains_code("# isort: skip"));
assert!(!comment_contains_code("# isort:skip"));
assert!(!comment_contains_code("# isort: skip_file"));
assert!(!comment_contains_code("# isort:skip_file"));
assert!(!comment_contains_code("# isort: split"));
assert!(!comment_contains_code("# isort:split"));
assert!(!comment_contains_code("# isort: dont-add-imports"));
assert!(!comment_contains_code("# isort:dont-add-imports"));
assert!(!comment_contains_code(
"# isort: dont-add-imports: [\"import os\"]"
));
assert!(!comment_contains_code(
"# isort:dont-add-imports: [\"import os\"]"
));
assert!(!comment_contains_code(
"# isort: dont-add-imports:[\"import os\"]"
));
assert!(!comment_contains_code(
"# isort:dont-add-imports:[\"import os\"]"
));
assert!(!comment_contains_code("# type: ignore"));
assert!(!comment_contains_code("# type:ignore"));
assert!(!comment_contains_code("# type: ignore[import]"));
assert!(!comment_contains_code("# type:ignore[import]"));
assert!(!comment_contains_code("# TODO: Do that"));
assert!(!comment_contains_code("# FIXME: Fix that"));
assert!(!comment_contains_code("# XXX: What ever"));
}
}

2
src/eradicate/mod.rs Normal file
View File

@@ -0,0 +1,2 @@
pub mod checks;
pub mod detection;

View File

@@ -5,6 +5,19 @@ use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};
const FUNC_NAME_ALLOWLIST: &[&str] = &["get", "setdefault", "pop", "fromkeys"];
/// Returns `true` if an argument is allowed to use a boolean trap. To return
/// `true`, the function name must be explicitly allowed, and the argument must
/// be either the first or second argument in the call.
fn allow_boolean_trap(func: &Expr) -> bool {
if let ExprKind::Attribute { attr, .. } = &func.node {
FUNC_NAME_ALLOWLIST.contains(&attr.as_ref())
} else {
false
}
}
fn is_boolean_arg(arg: &Expr) -> bool {
matches!(
&arg.node,
@@ -60,8 +73,15 @@ pub fn check_boolean_default_value_in_function_definition(
}
}
pub fn check_boolean_positional_value_in_function_call(checker: &mut Checker, args: &[Expr]) {
for arg in args {
pub fn check_boolean_positional_value_in_function_call(
checker: &mut Checker,
args: &[Expr],
func: &Expr,
) {
for (index, arg) in args.iter().enumerate() {
if index < 2 && allow_boolean_trap(func) {
continue;
}
add_if_boolean(
checker,
arg,

View File

@@ -39,6 +39,7 @@ pub mod commands;
mod cst;
mod directives;
mod docstrings;
mod eradicate;
mod flake8_2020;
pub mod flake8_annotations;
pub mod flake8_bandit;

View File

@@ -399,6 +399,7 @@ mod tests {
use crate::checks::CheckCode;
use crate::linter::test_path;
use crate::settings;
use crate::settings::types::PythonVersion;
#[test_case(CheckCode::A001, Path::new("A001.py"); "A001")]
#[test_case(CheckCode::A002, Path::new("A002.py"); "A002")]
@@ -487,9 +488,9 @@ mod tests {
#[test_case(CheckCode::D414, Path::new("sections.py"); "D414")]
#[test_case(CheckCode::D415, Path::new("D.py"); "D415")]
#[test_case(CheckCode::D416, Path::new("D.py"); "D416")]
#[test_case(CheckCode::D417, Path::new("sections.py"); "D417_0")]
#[test_case(CheckCode::D417, Path::new("canonical_numpy_examples.py"); "D417_1")]
#[test_case(CheckCode::D417, Path::new("canonical_google_examples.py"); "D417_2")]
#[test_case(CheckCode::D417, Path::new("canonical_numpy_examples.py"); "D417_1")]
#[test_case(CheckCode::D417, Path::new("sections.py"); "D417_0")]
#[test_case(CheckCode::D418, Path::new("D.py"); "D418")]
#[test_case(CheckCode::D419, Path::new("D.py"); "D419")]
#[test_case(CheckCode::E402, Path::new("E402.py"); "E402")]
@@ -505,6 +506,7 @@ mod tests {
#[test_case(CheckCode::E742, Path::new("E742.py"); "E742")]
#[test_case(CheckCode::E743, Path::new("E743.py"); "E743")]
#[test_case(CheckCode::E999, Path::new("E999.py"); "E999")]
#[test_case(CheckCode::ERA001, Path::new("ERA001.py"); "ERA001")]
#[test_case(CheckCode::F401, Path::new("F401_0.py"); "F401_0")]
#[test_case(CheckCode::F401, Path::new("F401_1.py"); "F401_1")]
#[test_case(CheckCode::F401, Path::new("F401_2.py"); "F401_2")]
@@ -519,14 +521,14 @@ mod tests {
#[test_case(CheckCode::F406, Path::new("F406.py"); "F406")]
#[test_case(CheckCode::F407, Path::new("F407.py"); "F407")]
#[test_case(CheckCode::F501, Path::new("F50x.py"); "F501")]
#[test_case(CheckCode::F502, Path::new("F50x.py"); "F502_0")]
#[test_case(CheckCode::F502, Path::new("F502.py"); "F502_1")]
#[test_case(CheckCode::F503, Path::new("F50x.py"); "F503_0")]
#[test_case(CheckCode::F502, Path::new("F50x.py"); "F502_0")]
#[test_case(CheckCode::F503, Path::new("F503.py"); "F503_1")]
#[test_case(CheckCode::F504, Path::new("F50x.py"); "F504_0")]
#[test_case(CheckCode::F503, Path::new("F50x.py"); "F503_0")]
#[test_case(CheckCode::F504, Path::new("F504.py"); "F504_1")]
#[test_case(CheckCode::F505, Path::new("F50x.py"); "F505_0")]
#[test_case(CheckCode::F504, Path::new("F50x.py"); "F504_0")]
#[test_case(CheckCode::F505, Path::new("F504.py"); "F505_1")]
#[test_case(CheckCode::F505, Path::new("F50x.py"); "F505_0")]
#[test_case(CheckCode::F506, Path::new("F50x.py"); "F506")]
#[test_case(CheckCode::F507, Path::new("F50x.py"); "F507")]
#[test_case(CheckCode::F508, Path::new("F50x.py"); "F508")]
@@ -561,6 +563,9 @@ mod tests {
#[test_case(CheckCode::F831, Path::new("F831.py"); "F831")]
#[test_case(CheckCode::F841, Path::new("F841.py"); "F841")]
#[test_case(CheckCode::F901, Path::new("F901.py"); "F901")]
#[test_case(CheckCode::FBT001, Path::new("FBT.py"); "FBT001")]
#[test_case(CheckCode::FBT002, Path::new("FBT.py"); "FBT002")]
#[test_case(CheckCode::FBT003, Path::new("FBT.py"); "FBT003")]
#[test_case(CheckCode::N801, Path::new("N801.py"); "N801")]
#[test_case(CheckCode::N802, Path::new("N802.py"); "N802")]
#[test_case(CheckCode::N803, Path::new("N803.py"); "N803")]
@@ -576,6 +581,16 @@ mod tests {
#[test_case(CheckCode::N816, Path::new("N816.py"); "N816")]
#[test_case(CheckCode::N817, Path::new("N817.py"); "N817")]
#[test_case(CheckCode::N818, Path::new("N818.py"); "N818")]
#[test_case(CheckCode::RUF001, Path::new("RUF001.py"); "RUF001")]
#[test_case(CheckCode::RUF002, Path::new("RUF002.py"); "RUF002")]
#[test_case(CheckCode::RUF003, Path::new("RUF003.py"); "RUF003")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_0.py"); "RUF101_0")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_1.py"); "RUF101_1")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_2.py"); "RUF101_2")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_3.py"); "RUF101_3")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_4.py"); "RUF101_4")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_5.py"); "RUF101_5")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_6.py"); "RUF101_6")]
#[test_case(CheckCode::S101, Path::new("S101.py"); "S101")]
#[test_case(CheckCode::S102, Path::new("S102.py"); "S102")]
#[test_case(CheckCode::S104, Path::new("S104.py"); "S104")]
@@ -609,16 +624,6 @@ mod tests {
#[test_case(CheckCode::W292, Path::new("W292_2.py"); "W292_2")]
#[test_case(CheckCode::W605, Path::new("W605_0.py"); "W605_0")]
#[test_case(CheckCode::W605, Path::new("W605_1.py"); "W605_1")]
#[test_case(CheckCode::RUF001, Path::new("RUF001.py"); "RUF001")]
#[test_case(CheckCode::RUF002, Path::new("RUF002.py"); "RUF002")]
#[test_case(CheckCode::RUF003, Path::new("RUF003.py"); "RUF003")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_0.py"); "RUF101_0")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_1.py"); "RUF101_1")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_2.py"); "RUF101_2")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_3.py"); "RUF101_3")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_4.py"); "RUF101_4")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_5.py"); "RUF101_5")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_6.py"); "RUF101_6")]
#[test_case(CheckCode::YTT101, Path::new("YTT101.py"); "YTT101")]
#[test_case(CheckCode::YTT102, Path::new("YTT102.py"); "YTT102")]
#[test_case(CheckCode::YTT103, Path::new("YTT103.py"); "YTT103")]
@@ -629,9 +634,6 @@ mod tests {
#[test_case(CheckCode::YTT301, Path::new("YTT301.py"); "YTT301")]
#[test_case(CheckCode::YTT302, Path::new("YTT302.py"); "YTT302")]
#[test_case(CheckCode::YTT303, Path::new("YTT303.py"); "YTT303")]
#[test_case(CheckCode::FBT001, Path::new("FBT.py"); "FBT001")]
#[test_case(CheckCode::FBT002, Path::new("FBT.py"); "FBT002")]
#[test_case(CheckCode::FBT003, Path::new("FBT.py"); "FBT003")]
fn checks(check_code: CheckCode, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy());
let mut checks = test_path(
@@ -694,4 +696,64 @@ mod tests {
insta::assert_yaml_snapshot!(checks);
Ok(())
}
#[test]
fn future_annotations_pep_585_p37() -> Result<()> {
let mut checks = test_path(
Path::new("./resources/test/fixtures/future_annotations.py"),
&settings::Settings {
target_version: PythonVersion::Py37,
..settings::Settings::for_rule(CheckCode::U006)
},
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
Ok(())
}
#[test]
fn future_annotations_pep_585_py310() -> Result<()> {
let mut checks = test_path(
Path::new("./resources/test/fixtures/future_annotations.py"),
&settings::Settings {
target_version: PythonVersion::Py310,
..settings::Settings::for_rule(CheckCode::U006)
},
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
Ok(())
}
#[test]
fn future_annotations_pep_604_p37() -> Result<()> {
let mut checks = test_path(
Path::new("./resources/test/fixtures/future_annotations.py"),
&settings::Settings {
target_version: PythonVersion::Py37,
..settings::Settings::for_rule(CheckCode::U007)
},
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
Ok(())
}
#[test]
fn future_annotations_pep_604_py310() -> Result<()> {
let mut checks = test_path(
Path::new("./resources/test/fixtures/future_annotations.py"),
&settings::Settings {
target_version: PythonVersion::Py310,
..settings::Settings::for_rule(CheckCode::U007)
},
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
Ok(())
}
}

View File

@@ -13,9 +13,9 @@ static NO_QA_REGEX: Lazy<Regex> = Lazy::new(|| {
Regex::new(
r"(?P<spaces>\s*)(?P<noqa>(?i:# noqa)(?::\s?(?P<codes>([A-Z]+[0-9]+(?:[,\s]+)?)+))?)",
)
.expect("Invalid regex")
.unwrap()
});
static SPLIT_COMMA_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"[,\s]").expect("Invalid regex"));
static SPLIT_COMMA_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"[,\s]").unwrap());
#[derive(Debug)]
pub enum Directive<'a> {

View File

@@ -1360,7 +1360,7 @@ fn missing_args(checker: &mut Checker, definition: &Definition, docstrings_args:
// See: `GOOGLE_ARGS_REGEX` in `pydocstyle/checker.py`.
static GOOGLE_ARGS_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^\s*(\w+)\s*(\(.*?\))?\s*:\n?\s*.+").expect("Invalid regex"));
Lazy::new(|| Regex::new(r"^\s*(\w+)\s*(\(.*?\))?\s*:\n?\s*.+").unwrap());
fn args_section(checker: &mut Checker, definition: &Definition, context: &SectionContext) {
let mut args_sections: Vec<String> = vec![];

View File

@@ -0,0 +1,85 @@
---
source: src/linter.rs
expression: checks
---
- kind: CommentedOutCode
location:
row: 1
column: 0
end_location:
row: 1
column: 10
fix:
patch:
content: ""
location:
row: 1
column: 0
end_location:
row: 2
column: 0
- kind: CommentedOutCode
location:
row: 2
column: 0
end_location:
row: 2
column: 22
fix:
patch:
content: ""
location:
row: 2
column: 0
end_location:
row: 3
column: 0
- kind: CommentedOutCode
location:
row: 3
column: 0
end_location:
row: 3
column: 6
fix:
patch:
content: ""
location:
row: 3
column: 0
end_location:
row: 4
column: 0
- kind: CommentedOutCode
location:
row: 5
column: 0
end_location:
row: 5
column: 13
fix:
patch:
content: ""
location:
row: 5
column: 0
end_location:
row: 6
column: 0
- kind: CommentedOutCode
location:
row: 12
column: 4
end_location:
row: 12
column: 16
fix:
patch:
content: ""
location:
row: 12
column: 0
end_location:
row: 13
column: 0

View File

@@ -4,10 +4,26 @@ expression: checks
---
- kind: BooleanPositionalValueInFunctionCall
location:
row: 41
row: 42
column: 10
end_location:
row: 41
row: 42
column: 14
fix: ~
- kind: BooleanPositionalValueInFunctionCall
location:
row: 57
column: 10
end_location:
row: 57
column: 14
fix: ~
- kind: BooleanPositionalValueInFunctionCall
location:
row: 57
column: 16
end_location:
row: 57
column: 21
fix: ~

View File

@@ -7,27 +7,27 @@ expression: checks
- - models.Nut
- false
location:
row: 5
row: 6
column: 0
end_location:
row: 8
row: 9
column: 1
fix:
patch:
content: "from models import (\n Fruit,\n)"
location:
row: 5
row: 6
column: 0
end_location:
row: 8
row: 9
column: 1
- kind:
UndefinedName: Bar
location:
row: 25
row: 26
column: 18
end_location:
row: 25
row: 26
column: 21
fix: ~

View File

@@ -0,0 +1,22 @@
---
source: src/linter.rs
expression: checks
---
- kind:
UsePEP585Annotation: List
location:
row: 34
column: 17
end_location:
row: 34
column: 21
fix:
patch:
content: list
location:
row: 34
column: 17
end_location:
row: 34
column: 21

View File

@@ -0,0 +1,39 @@
---
source: src/linter.rs
expression: checks
---
- kind:
UsePEP585Annotation: List
location:
row: 34
column: 17
end_location:
row: 34
column: 21
fix:
patch:
content: list
location:
row: 34
column: 17
end_location:
row: 34
column: 21
- kind:
UsePEP585Annotation: List
location:
row: 35
column: 8
end_location:
row: 35
column: 12
fix:
patch:
content: list
location:
row: 35
column: 8
end_location:
row: 35
column: 12

View File

@@ -0,0 +1,21 @@
---
source: src/linter.rs
expression: checks
---
- kind: UsePEP604Annotation
location:
row: 40
column: 3
end_location:
row: 40
column: 16
fix:
patch:
content: int | None
location:
row: 40
column: 3
end_location:
row: 40
column: 16

View File

@@ -0,0 +1,21 @@
---
source: src/linter.rs
expression: checks
---
- kind: UsePEP604Annotation
location:
row: 40
column: 3
end_location:
row: 40
column: 16
fix:
patch:
content: int | None
location:
row: 40
column: 3
end_location:
row: 40
column: 16