Compare commits

...

14 Commits

Author SHA1 Message Date
Charlie Marsh
da9ae6a42a Bump version to 0.0.114 2022-11-12 11:55:18 -05:00
Martin Lehoux
afa59d78bb feat: no unnecessary encode utf8 (#686) 2022-11-12 11:54:36 -05:00
Charlie Marsh
bbc38fea73 Avoid generating empty statement bodies (#700) 2022-11-12 11:39:09 -05:00
Chammika Mannakkara
6bcc11a90f add fixes for __future__ import removal (#682) 2022-11-12 11:28:05 -05:00
Harutaka Kawamura
6f36e5dd25 Implement B019 (#695) 2022-11-12 11:14:03 -05:00
Anders Kaseorg
1d13752eb1 Remove static isort classifications for __main__, disutils (#694) 2022-11-12 09:13:38 -05:00
Anders Kaseorg
394af0dcff Disable default features of chrono (#696) 2022-11-12 09:02:02 -05:00
Charlie Marsh
51cee471a0 Add test case for import-from wrapping 2022-11-11 23:46:19 -05:00
Charlie Marsh
8df3a5437a Take indentation into account for import-from wrapping (#693) 2022-11-11 23:45:04 -05:00
Charlie Marsh
a21fe716f2 Bump version to 0.0.113 2022-11-11 22:42:02 -05:00
Charlie Marsh
558883299a Default to isort's import sort logic (#691) 2022-11-11 22:41:39 -05:00
Charlie Marsh
048a13c795 Add a separate local folder category for imports (#690) 2022-11-11 22:12:48 -05:00
Anders Kaseorg
5a8b7c1d20 Implement flake8-2020 (sys.version, sys.version_info misuse) (#688) 2022-11-11 20:39:37 -05:00
Charlie Marsh
f8932ec12b Add some TODOs around import tracking 2022-11-11 19:07:40 -05:00
77 changed files with 2223 additions and 319 deletions

View File

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

26
Cargo.lock generated
View File

@@ -427,11 +427,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
dependencies = [
"iana-time-zone",
"js-sys",
"num-integer",
"num-traits",
"time",
"wasm-bindgen",
"winapi 0.3.9",
]
@@ -933,7 +930,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8-to-ruff"
version = "0.0.112-dev.0"
version = "0.0.114-dev.0"
dependencies = [
"anyhow",
"clap 4.0.22",
@@ -2240,7 +2237,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.112"
version = "0.0.114"
dependencies = [
"anyhow",
"assert_cmd",
@@ -2287,7 +2284,7 @@ dependencies = [
[[package]]
name = "ruff_dev"
version = "0.0.112"
version = "0.0.114"
dependencies = [
"anyhow",
"clap 4.0.22",
@@ -2781,17 +2778,6 @@ dependencies = [
"syn",
]
[[package]]
name = "time"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi 0.3.9",
]
[[package]]
name = "tiny-keccak"
version = "2.0.2"
@@ -3078,12 +3064,6 @@ version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"

View File

@@ -6,7 +6,7 @@ members = [
[package]
name = "ruff"
version = "0.0.112"
version = "0.0.114"
edition = "2021"
[lib]
@@ -16,7 +16,7 @@ name = "ruff"
anyhow = { version = "1.0.66" }
bincode = { version = "1.3.3" }
bitflags = { version = "1.3.2" }
chrono = { version = "0.4.21" }
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
clap = { version = "4.0.1", features = ["derive"] }
colored = { version = "2.0.0" }
common-path = { version = "1.0.0" }

View File

@@ -57,8 +57,9 @@ Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-mu
9. [flake8-print](#flake8-print)
10. [flake8-quotes](#flake8-quotes)
11. [flake8-annotations](#flake8-annotations)
12. [Ruff-specific rules](#ruff-specific-rules)
13. [Meta rules](#meta-rules)
12. [flake8-2020](#flake8-2020)
13. [Ruff-specific rules](#ruff-specific-rules)
14. [Meta rules](#meta-rules)
5. [Editor Integrations](#editor-integrations)
6. [FAQ](#faq)
7. [Development](#development)
@@ -98,7 +99,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
```yaml
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.112
rev: v0.0.114
hooks:
- id: ruff
```
@@ -440,8 +441,9 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
| U007 | UsePEP604Annotation | Use `X \| Y` for type annotations | 🛠 |
| U008 | SuperCallWithParameters | Use `super()` instead of `super(__class__, self)` | 🛠 |
| U009 | PEP3120UnnecessaryCodingComment | utf-8 encoding declaration is unnecessary | 🛠 |
| U010 | UnnecessaryFutureImport | Unnessary __future__ import `...` for target Python version | |
| U011 | UnnecessaryLRUCacheParams | Unnessary parameters to functools.lru_cache | 🛠 |
| U010 | UnnecessaryFutureImport | Unnecessary `__future__` import `...` for target Python version | 🛠 |
| U011 | UnnecessaryLRUCacheParams | Unnecessary parameters to functools.lru_cache | 🛠 |
| U012 | UnnecessaryEncodeUTF8 | Unnecessary call to `encode` as UTF-8 | 🛠 |
### pep8-naming
@@ -510,6 +512,7 @@ For more, see [flake8-bugbear](https://pypi.org/project/flake8-bugbear/22.10.27/
| B016 | CannotRaiseLiteral | Cannot raise a literal. Did you intend to return it or raise an Exception? | |
| B017 | NoAssertRaisesException | `assertRaises(Exception):` should be considered evil. | |
| B018 | UselessExpression | Found useless expression. Either assign it to a variable or remove it. | |
| B019 | CachedInstanceMethod | Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks. | |
| B025 | DuplicateTryBlockException | try-except block with duplicate exception `Exception` | |
| B026 | StarArgUnpackingAfterKeywordArg | Star-arg unpacking after a keyword argument is strongly discouraged. | |
@@ -561,6 +564,23 @@ For more, see [flake8-annotations](https://pypi.org/project/flake8-annotations/2
| ANN206 | MissingReturnTypeClassMethod | Missing return type annotation for classmethod `...` | |
| ANN401 | DynamicallyTypedExpression | Dynamically typed expressions (typing.Any) are disallowed in `...` | |
### flake8-2020
For more, see [flake8-2020](https://pypi.org/project/flake8-2020/1.7.0/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| YTT101 | SysVersionSlice3Referenced | `sys.version[:3]` referenced (python3.10), use `sys.version_info` | |
| YTT102 | SysVersion2Referenced | `sys.version[2]` referenced (python3.10), use `sys.version_info` | |
| YTT103 | SysVersionCmpStr3 | `sys.version` compared to string (python3.10), use `sys.version_info` | |
| YTT201 | SysVersionInfo0Eq3Referenced | `sys.version_info[0] == 3` referenced (python4), use `>=` | |
| YTT202 | SixPY3Referenced | `six.PY3` referenced (python4), use `not six.PY2` | |
| YTT203 | SysVersionInfo1CmpInt | `sys.version_info[1]` compared to integer (python4), compare `sys.version_info` to tuple | |
| YTT204 | SysVersionInfoMinorCmpInt | `sys.version_info.minor` compared to integer (python4), compare `sys.version_info` to tuple | |
| YTT301 | SysVersion0Referenced | `sys.version[0]` referenced (python10), use `sys.version_info` | |
| YTT302 | SysVersionCmpStr10 | `sys.version` compared to string (python10), use `sys.version_info` | |
| YTT303 | SysVersionSlice1Referenced | `sys.version[:1]` referenced (python10), use `sys.version_info` | |
### Ruff-specific rules
| Code | Name | Message | Fix |
@@ -666,8 +686,9 @@ including:
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (20/32)
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (14/34)
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (21/32)
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (15/34)
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
Beyond rule-set parity, Ruff suffers from the following limitations vis-à-vis Flake8:
@@ -689,10 +710,11 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (20/32)
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (21/32)
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
Ruff can also replace [`isort`](https://pypi.org/project/isort/), [`yesqa`](https://github.com/asottile/yesqa),
and a subset of the rules implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (14/34).
and a subset of the rules implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (15/34).
If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, free to file an Issue.

View File

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

View File

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

108
resources/test/fixtures/B019.py vendored Normal file
View File

@@ -0,0 +1,108 @@
"""
Should emit:
B019 - on lines 73, 77, 81, 85, 89, 93, 97, 101
"""
import functools
from functools import cache, cached_property, lru_cache
def some_other_cache():
...
@functools.cache
def compute_func(self, y):
...
class Foo:
def __init__(self, x):
self.x = x
def compute_method(self, y):
...
@some_other_cache
def user_cached_instance_method(self, y):
...
@classmethod
@functools.cache
def cached_classmethod(cls, y):
...
@classmethod
@cache
def other_cached_classmethod(cls, y):
...
@classmethod
@functools.lru_cache
def lru_cached_classmethod(cls, y):
...
@classmethod
@lru_cache
def other_lru_cached_classmethod(cls, y):
...
@staticmethod
@functools.cache
def cached_staticmethod(y):
...
@staticmethod
@cache
def other_cached_staticmethod(y):
...
@staticmethod
@functools.lru_cache
def lru_cached_staticmethod(y):
...
@staticmethod
@lru_cache
def other_lru_cached_staticmethod(y):
...
@functools.cached_property
def some_cached_property(self):
...
@cached_property
def some_other_cached_property(self):
...
# Remaining methods should emit B019
@functools.cache
def cached_instance_method(self, y):
...
@cache
def another_cached_instance_method(self, y):
...
@functools.cache()
def called_cached_instance_method(self, y):
...
@cache()
def another_called_cached_instance_method(self, y):
...
@functools.lru_cache
def lru_cached_instance_method(self, y):
...
@lru_cache
def another_lru_cached_instance_method(self, y):
...
@functools.lru_cache()
def called_lru_cached_instance_method(self, y):
...
@lru_cache()
def another_called_lru_cached_instance_method(self, y):
...

View File

@@ -28,7 +28,6 @@ from blah import ClassA, ClassB, ClassC
if TYPE_CHECKING:
from models import Fruit, Nut, Vegetable
if TYPE_CHECKING:
import shelve
import importlib

View File

@@ -1,5 +1,14 @@
from __future__ import annotations, nested_scopes, generators
from __future__ import nested_scopes, generators
from __future__ import with_statement, unicode_literals
from __future__ import absolute_import, division
from __future__ import generator_stop
from __future__ import print_function, generator_stop
from __future__ import invalid_module, generators
if True:
from __future__ import generator_stop
from __future__ import generators
if True:
from __future__ import generator_stop
from __future__ import invalid_module, generators

52
resources/test/fixtures/U012.py vendored Normal file
View File

@@ -0,0 +1,52 @@
# ASCII literals should be replaced by a bytes literal
"foo".encode("utf-8") # b"foo"
"foo".encode("u8") # b"foo"
"foo".encode() # b"foo"
"foo".encode("UTF8") # b"foo"
U"foo".encode("utf-8") # b"foo"
"foo".encode(encoding="utf-8") # b"foo"
"""
Lorem
Ipsum
""".encode(
"utf-8"
)
# b"""
# Lorem
#
# Ipsum
# """
# `encode` on variables should not be processed.
string = "hello there"
string.encode("utf-8")
bar = "bar"
f"foo{bar}".encode("utf-8") # f"foo{bar}".encode()
encoding = "latin"
"foo".encode(encoding)
f"foo{bar}".encode(encoding)
# `encode` with custom args and kwargs should not be processed.
"foo".encode("utf-8", errors="replace")
"foo".encode("utf-8", "replace")
"foo".encode(errors="replace")
"foo".encode(encoding="utf-8", errors="replace")
# `encode` with custom args and kwargs on unicode should not be processed.
"unicode text©".encode("utf-8", errors="replace")
"unicode text©".encode("utf-8", "replace")
"unicode text©".encode(errors="replace")
"unicode text©".encode(encoding="utf-8", errors="replace")
# Unicode literals should only be stripped of default encoding.
"unicode text©".encode("utf-8") # "unicode text©".encode()
"unicode text©".encode()
"unicode text©".encode(encoding="UTF8") # "unicode text©".encode()
r"fo\o".encode("utf-8") # br"fo\o"
u"foo".encode("utf-8") # b"foo"
R"fo\o".encode("utf-8") # br"fo\o"
U"foo".encode("utf-8") # b"foo"
print("foo".encode()) # print(b"foo")

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

@@ -0,0 +1,13 @@
import sys
from sys import version, version as v
print(sys.version)
print(sys.version[:3])
print(version[:3])
# ignore from imports with aliases, patches welcome
print(v[:3])
# the tool is timid and only flags certain numeric slices
i = 3
print(sys.version[:i])

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

@@ -0,0 +1,5 @@
import sys
from sys import version
py_minor = sys.version[2]
py_minor = version[2]

8
resources/test/fixtures/YTT103.py vendored Normal file
View File

@@ -0,0 +1,8 @@
import sys
from sys import version
version < "3.5"
sys.version < "3.5"
sys.version <= "3.5"
sys.version > "3.5"
sys.version >= "3.5"

10
resources/test/fixtures/YTT201.py vendored Normal file
View File

@@ -0,0 +1,10 @@
import sys
from sys import version_info
print("{}.{}".format(*sys.version_info))
PY3 = sys.version_info[0] >= 3
PY3 = sys.version_info[0] == 3
PY3 = version_info[0] == 3
PY2 = sys.version_info[0] != 3
PY2 = version_info[0] != 3

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

@@ -0,0 +1,7 @@
import six
from six import PY3
if six.PY3:
print("3")
if PY3:
print("3")

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

@@ -0,0 +1,5 @@
import sys
from sys import version_info
sys.version_info[1] >= 5
version_info[1] < 6

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

@@ -0,0 +1,5 @@
import sys
from sys import version_info
sys.version_info.minor <= 7
version_info.minor > 8

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

@@ -0,0 +1,5 @@
import sys
from sys import version
py_major = sys.version[0]
py_major = version[0]

8
resources/test/fixtures/YTT302.py vendored Normal file
View File

@@ -0,0 +1,8 @@
import sys
from sys import version
version < "3"
sys.version < "3"
sys.version <= "3"
sys.version > "3"
sys.version >= "3"

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

@@ -0,0 +1,5 @@
import sys
from sys import version
print(sys.version[:1])
print(version[:1])

View File

@@ -1 +1,14 @@
from collections import Collection
from line_with_88 import aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
from line_with_89 import (
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
)
if indented:
from line_with_88 import aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
from line_with_89 import aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
from line_with_90 import aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
from line_with_91 import aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
from line_with_92 import aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
from line_with_93 import (
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
)

View File

@@ -0,0 +1,12 @@
import StringIO
import glob
import os
import shutil
import tempfile
import time
from subprocess import PIPE, Popen, STDOUT
from module import Class, CONSTANT, function, BASIC, Apple
import foo
import FOO
import BAR
import bar

View File

@@ -0,0 +1,2 @@
[tool.ruff]
line-length = 88

View File

@@ -0,0 +1,4 @@
import sys
import leading_prefix
import os
from . import leading_prefix

View File

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

View File

@@ -32,17 +32,19 @@ use crate::settings::Settings;
use crate::source_code_locator::SourceCodeLocator;
use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope};
use crate::{
docstrings, flake8_annotations, flake8_bugbear, flake8_builtins, flake8_comprehensions,
flake8_print, pep8_naming, pycodestyle, pydocstyle, pyflakes, pyupgrade,
docstrings, flake8_2020, flake8_annotations, flake8_bugbear, flake8_builtins,
flake8_comprehensions, flake8_print, pep8_naming, pycodestyle, pydocstyle, pyflakes, pyupgrade,
};
const GLOBAL_SCOPE_INDEX: usize = 0;
const TRACK_FROM_IMPORTS: [&str; 10] = [
const TRACK_FROM_IMPORTS: [&str; 12] = [
"collections",
"collections.abc",
"contextlib",
"functools",
"re",
"six",
"sys",
"typing",
"typing.io",
"typing.re",
@@ -344,6 +346,9 @@ where
if self.settings.enabled.contains(&CheckCode::B018) {
flake8_bugbear::plugins::useless_expression(self, body);
}
if self.settings.enabled.contains(&CheckCode::B019) {
flake8_bugbear::plugins::cached_instance_method(self, decorator_list);
}
self.check_builtin_shadowing(name, Range::from_located(stmt), true);
@@ -601,6 +606,12 @@ where
}
}
if let Some("__future__") = module.as_deref() {
if self.settings.enabled.contains(&CheckCode::U010) {
pyupgrade::plugins::unnecessary_future_import(self, stmt, names);
}
}
for alias in names {
if let Some("__future__") = module.as_deref() {
let name = alias.node.asname.as_ref().unwrap_or(&alias.node.name);
@@ -633,14 +644,6 @@ where
}
}
if self.settings.enabled.contains(&CheckCode::U010) {
pyupgrade::plugins::unnecessary_future_import(
self,
stmt,
&alias.node.name,
);
}
if self.settings.enabled.contains(&CheckCode::F404) && !self.futures_allowed
{
self.add_check(Check::new(
@@ -984,6 +987,14 @@ where
if self.match_typing_module(value, "Literal") {
self.in_literal = true;
}
if self.settings.enabled.contains(&CheckCode::YTT101)
|| self.settings.enabled.contains(&CheckCode::YTT102)
|| self.settings.enabled.contains(&CheckCode::YTT301)
|| self.settings.enabled.contains(&CheckCode::YTT303)
{
flake8_2020::plugins::subscript(self, value, slice);
}
}
ExprKind::Tuple { elts, ctx } | ExprKind::List { elts, ctx } => {
if matches!(ctx, ExprContext::Store) {
@@ -1001,34 +1012,40 @@ where
}
}
}
ExprKind::Name { id, ctx } => match ctx {
ExprContext::Load => {
// Ex) List[...]
if self.settings.enabled.contains(&CheckCode::U006)
&& self.settings.target_version >= PythonVersion::Py39
&& typing::is_pep585_builtin(expr, self.from_imports.get("typing"))
{
pyupgrade::plugins::use_pep585_annotation(self, expr, id);
}
self.handle_node_load(expr);
}
ExprContext::Store => {
if self.settings.enabled.contains(&CheckCode::E741) {
if let Some(check) = pycodestyle::checks::ambiguous_variable_name(
id,
Range::from_located(expr),
) {
self.add_check(check);
ExprKind::Name { id, ctx } => {
match ctx {
ExprContext::Load => {
// Ex) List[...]
if self.settings.enabled.contains(&CheckCode::U006)
&& self.settings.target_version >= PythonVersion::Py39
&& typing::is_pep585_builtin(expr, self.from_imports.get("typing"))
{
pyupgrade::plugins::use_pep585_annotation(self, expr, id);
}
self.handle_node_load(expr);
}
ExprContext::Store => {
if self.settings.enabled.contains(&CheckCode::E741) {
if let Some(check) = pycodestyle::checks::ambiguous_variable_name(
id,
Range::from_located(expr),
) {
self.add_check(check);
}
}
self.check_builtin_shadowing(id, Range::from_located(expr), true);
self.check_builtin_shadowing(id, Range::from_located(expr), true);
self.handle_node_store(expr, self.current_parent());
self.handle_node_store(expr, self.current_parent());
}
ExprContext::Del => self.handle_node_delete(expr),
}
ExprContext::Del => self.handle_node_delete(expr),
},
if self.settings.enabled.contains(&CheckCode::YTT202) {
flake8_2020::plugins::name_or_attribute(self, expr);
}
}
ExprKind::Attribute { attr, .. } => {
// Ex) typing.List[...]
if self.settings.enabled.contains(&CheckCode::U006)
@@ -1037,6 +1054,10 @@ where
{
pyupgrade::plugins::use_pep585_annotation(self, expr, attr);
}
if self.settings.enabled.contains(&CheckCode::YTT202) {
flake8_2020::plugins::name_or_attribute(self, expr);
}
}
ExprKind::Call {
func,
@@ -1052,6 +1073,10 @@ where
pyupgrade::plugins::super_call_with_parameters(self, expr, func, args);
}
if self.settings.enabled.contains(&CheckCode::U012) {
pyupgrade::plugins::unnecessary_encode_utf8(self, expr, func, args, keywords);
}
// flake8-print
if self.settings.enabled.contains(&CheckCode::T201)
|| self.settings.enabled.contains(&CheckCode::T203)
@@ -1422,6 +1447,15 @@ where
.into_iter(),
);
}
if self.settings.enabled.contains(&CheckCode::YTT103)
|| self.settings.enabled.contains(&CheckCode::YTT201)
|| self.settings.enabled.contains(&CheckCode::YTT203)
|| self.settings.enabled.contains(&CheckCode::YTT204)
|| self.settings.enabled.contains(&CheckCode::YTT302)
{
flake8_2020::plugins::compare(self, left, ops, comparators);
}
}
ExprKind::Constant {
value: Constant::Str(value),
@@ -2381,16 +2415,20 @@ impl<'a> Checker<'a> {
.iter()
.map(|index| self.parents[*index])
.collect();
let removal_fn = match kind {
match match kind {
ImportKind::Import => pyflakes::fixes::remove_unused_imports,
ImportKind::ImportFrom => pyflakes::fixes::remove_unused_import_froms,
};
match removal_fn(self.locator, &full_names, child, parent, &deleted) {
Ok(fix) => Some(fix),
}(
self.locator, &full_names, child, parent, &deleted
) {
Ok(fix) => {
if fix.patch.content.is_empty() || fix.patch.content == "pass" {
self.deletions.insert(defined_by);
}
Some(fix)
}
Err(e) => {
error!("Failed to fix unused imports: {}", e);
error!("Failed to remove unused imports: {}", e);
None
}
}

View File

@@ -93,6 +93,7 @@ pub enum CheckCode {
B016,
B017,
B018,
B019,
B025,
B026,
// flake8-comprehensions
@@ -132,6 +133,17 @@ pub enum CheckCode {
ANN205,
ANN206,
ANN401,
// flake8-2020
YTT101,
YTT102,
YTT103,
YTT201,
YTT202,
YTT203,
YTT204,
YTT301,
YTT302,
YTT303,
// pyupgrade
U001,
U002,
@@ -144,6 +156,7 @@ pub enum CheckCode {
U009,
U010,
U011,
U012,
// pydocstyle
D100,
D101,
@@ -229,6 +242,7 @@ pub enum CheckCategory {
Flake8Print,
Flake8Quotes,
Flake8Annotations,
Flake82020,
Ruff,
Meta,
}
@@ -245,6 +259,7 @@ impl CheckCategory {
CheckCategory::Flake8Print => "flake8-print",
CheckCategory::Flake8Quotes => "flake8-quotes",
CheckCategory::Flake8Annotations => "flake8-annotations",
CheckCategory::Flake82020 => "flake8-2020",
CheckCategory::Pyupgrade => "pyupgrade",
CheckCategory::Pydocstyle => "pydocstyle",
CheckCategory::PEP8Naming => "pep8-naming",
@@ -272,6 +287,7 @@ impl CheckCategory {
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/"),
@@ -366,6 +382,7 @@ pub enum CheckKind {
CannotRaiseLiteral,
NoAssertRaisesException,
UselessExpression,
CachedInstanceMethod,
DuplicateTryBlockException(String),
StarArgUnpackingAfterKeywordArg,
// flake8-comprehensions
@@ -405,6 +422,17 @@ pub enum CheckKind {
MissingReturnTypeStaticMethod(String),
MissingReturnTypeClassMethod(String),
DynamicallyTypedExpression(String),
// flake8-2020
SysVersionSlice3Referenced,
SysVersion2Referenced,
SysVersionCmpStr3,
SysVersionInfo0Eq3Referenced,
SixPY3Referenced,
SysVersionInfo1CmpInt,
SysVersionInfoMinorCmpInt,
SysVersion0Referenced,
SysVersionCmpStr10,
SysVersionSlice1Referenced,
// pyupgrade
TypeOfPrimitive(Primitive),
UnnecessaryAbspath,
@@ -415,8 +443,9 @@ pub enum CheckKind {
UsePEP604Annotation,
SuperCallWithParameters,
PEP3120UnnecessaryCodingComment,
UnnecessaryFutureImport(String),
UnnecessaryFutureImport(Vec<String>),
UnnecessaryLRUCacheParams,
UnnecessaryEncodeUTF8,
// pydocstyle
BlankLineAfterLastSection(String),
BlankLineAfterSection(String),
@@ -585,6 +614,7 @@ impl CheckCode {
CheckCode::B016 => CheckKind::CannotRaiseLiteral,
CheckCode::B017 => CheckKind::NoAssertRaisesException,
CheckCode::B018 => CheckKind::UselessExpression,
CheckCode::B019 => CheckKind::CachedInstanceMethod,
CheckCode::B025 => CheckKind::DuplicateTryBlockException("Exception".to_string()),
CheckCode::B026 => CheckKind::StarArgUnpackingAfterKeywordArg,
// flake8-comprehensions
@@ -637,6 +667,17 @@ impl CheckCode {
CheckCode::ANN205 => CheckKind::MissingReturnTypeStaticMethod("...".to_string()),
CheckCode::ANN206 => CheckKind::MissingReturnTypeClassMethod("...".to_string()),
CheckCode::ANN401 => CheckKind::DynamicallyTypedExpression("...".to_string()),
// flake8-2020
CheckCode::YTT101 => CheckKind::SysVersionSlice3Referenced,
CheckCode::YTT102 => CheckKind::SysVersion2Referenced,
CheckCode::YTT103 => CheckKind::SysVersionCmpStr3,
CheckCode::YTT201 => CheckKind::SysVersionInfo0Eq3Referenced,
CheckCode::YTT202 => CheckKind::SixPY3Referenced,
CheckCode::YTT203 => CheckKind::SysVersionInfo1CmpInt,
CheckCode::YTT204 => CheckKind::SysVersionInfoMinorCmpInt,
CheckCode::YTT301 => CheckKind::SysVersion0Referenced,
CheckCode::YTT302 => CheckKind::SysVersionCmpStr10,
CheckCode::YTT303 => CheckKind::SysVersionSlice1Referenced,
// pyupgrade
CheckCode::U001 => CheckKind::UselessMetaclassType,
CheckCode::U002 => CheckKind::UnnecessaryAbspath,
@@ -650,8 +691,9 @@ impl CheckCode {
CheckCode::U007 => CheckKind::UsePEP604Annotation,
CheckCode::U008 => CheckKind::SuperCallWithParameters,
CheckCode::U009 => CheckKind::PEP3120UnnecessaryCodingComment,
CheckCode::U010 => CheckKind::UnnecessaryFutureImport("...".to_string()),
CheckCode::U010 => CheckKind::UnnecessaryFutureImport(vec!["...".to_string()]),
CheckCode::U011 => CheckKind::UnnecessaryLRUCacheParams,
CheckCode::U012 => CheckKind::UnnecessaryEncodeUTF8,
// pydocstyle
CheckCode::D100 => CheckKind::PublicModule,
CheckCode::D101 => CheckKind::PublicClass,
@@ -805,6 +847,7 @@ impl CheckCode {
CheckCode::B016 => CheckCategory::Flake8Bugbear,
CheckCode::B017 => CheckCategory::Flake8Bugbear,
CheckCode::B018 => CheckCategory::Flake8Bugbear,
CheckCode::B019 => CheckCategory::Flake8Bugbear,
CheckCode::B025 => CheckCategory::Flake8Bugbear,
CheckCode::B026 => CheckCategory::Flake8Bugbear,
CheckCode::C400 => CheckCategory::Flake8Comprehensions,
@@ -840,6 +883,16 @@ impl CheckCode {
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::U002 => CheckCategory::Pyupgrade,
CheckCode::U003 => CheckCategory::Pyupgrade,
@@ -851,6 +904,7 @@ impl CheckCode {
CheckCode::U009 => CheckCategory::Pyupgrade,
CheckCode::U010 => CheckCategory::Pyupgrade,
CheckCode::U011 => CheckCategory::Pyupgrade,
CheckCode::U012 => CheckCategory::Pyupgrade,
CheckCode::D100 => CheckCategory::Pydocstyle,
CheckCode::D101 => CheckCategory::Pydocstyle,
CheckCode::D102 => CheckCategory::Pydocstyle,
@@ -990,6 +1044,7 @@ impl CheckKind {
CheckKind::CannotRaiseLiteral => &CheckCode::B016,
CheckKind::NoAssertRaisesException => &CheckCode::B017,
CheckKind::UselessExpression => &CheckCode::B018,
CheckKind::CachedInstanceMethod => &CheckCode::B019,
CheckKind::DuplicateTryBlockException(_) => &CheckCode::B025,
CheckKind::StarArgUnpackingAfterKeywordArg => &CheckCode::B026,
// flake8-comprehensions
@@ -1029,6 +1084,17 @@ impl CheckKind {
CheckKind::MissingReturnTypeStaticMethod(_) => &CheckCode::ANN205,
CheckKind::MissingReturnTypeClassMethod(_) => &CheckCode::ANN206,
CheckKind::DynamicallyTypedExpression(_) => &CheckCode::ANN401,
// flake8-2020
CheckKind::SysVersionSlice3Referenced => &CheckCode::YTT101,
CheckKind::SysVersion2Referenced => &CheckCode::YTT102,
CheckKind::SysVersionCmpStr3 => &CheckCode::YTT103,
CheckKind::SysVersionInfo0Eq3Referenced => &CheckCode::YTT201,
CheckKind::SixPY3Referenced => &CheckCode::YTT202,
CheckKind::SysVersionInfo1CmpInt => &CheckCode::YTT203,
CheckKind::SysVersionInfoMinorCmpInt => &CheckCode::YTT204,
CheckKind::SysVersion0Referenced => &CheckCode::YTT301,
CheckKind::SysVersionCmpStr10 => &CheckCode::YTT302,
CheckKind::SysVersionSlice1Referenced => &CheckCode::YTT303,
// pyupgrade
CheckKind::TypeOfPrimitive(_) => &CheckCode::U003,
CheckKind::UnnecessaryAbspath => &CheckCode::U002,
@@ -1041,6 +1107,7 @@ impl CheckKind {
CheckKind::PEP3120UnnecessaryCodingComment => &CheckCode::U009,
CheckKind::UnnecessaryFutureImport(_) => &CheckCode::U010,
CheckKind::UnnecessaryLRUCacheParams => &CheckCode::U011,
CheckKind::UnnecessaryEncodeUTF8 => &CheckCode::U012,
// pydocstyle
CheckKind::BlankLineAfterLastSection(_) => &CheckCode::D413,
CheckKind::BlankLineAfterSection(_) => &CheckCode::D410,
@@ -1331,6 +1398,9 @@ impl CheckKind {
CheckKind::UselessExpression => {
"Found useless expression. Either assign it to a variable or remove it.".to_string()
}
CheckKind::CachedInstanceMethod => "Use of `functools.lru_cache` or `functools.cache` \
on methods can lead to memory leaks."
.to_string(),
CheckKind::DuplicateTryBlockException(name) => {
format!("try-except block with duplicate exception `{name}`")
}
@@ -1473,6 +1543,38 @@ impl CheckKind {
CheckKind::DynamicallyTypedExpression(name) => {
format!("Dynamically typed expressions (typing.Any) are disallowed in `{name}`")
}
// flake8-2020
CheckKind::SysVersionSlice3Referenced => {
"`sys.version[:3]` referenced (python3.10), use `sys.version_info`".to_string()
}
CheckKind::SysVersion2Referenced => {
"`sys.version[2]` referenced (python3.10), use `sys.version_info`".to_string()
}
CheckKind::SysVersionCmpStr3 => {
"`sys.version` compared to string (python3.10), use `sys.version_info`".to_string()
}
CheckKind::SysVersionInfo0Eq3Referenced => {
"`sys.version_info[0] == 3` referenced (python4), use `>=`".to_string()
}
CheckKind::SixPY3Referenced => {
"`six.PY3` referenced (python4), use `not six.PY2`".to_string()
}
CheckKind::SysVersionInfo1CmpInt => "`sys.version_info[1]` compared to integer \
(python4), compare `sys.version_info` to tuple"
.to_string(),
CheckKind::SysVersionInfoMinorCmpInt => "`sys.version_info.minor` compared to integer \
(python4), compare `sys.version_info` to \
tuple"
.to_string(),
CheckKind::SysVersion0Referenced => {
"`sys.version[0]` referenced (python10), use `sys.version_info`".to_string()
}
CheckKind::SysVersionCmpStr10 => {
"`sys.version` compared to string (python10), use `sys.version_info`".to_string()
}
CheckKind::SysVersionSlice1Referenced => {
"`sys.version[:1]` referenced (python10), use `sys.version_info`".to_string()
}
// pyupgrade
CheckKind::TypeOfPrimitive(primitive) => {
format!("Use `{}` instead of `type(...)`", primitive.builtin())
@@ -1498,12 +1600,19 @@ impl CheckKind {
CheckKind::SuperCallWithParameters => {
"Use `super()` instead of `super(__class__, self)`".to_string()
}
CheckKind::UnnecessaryFutureImport(name) => {
format!("Unnessary __future__ import `{name}` for target Python version")
CheckKind::UnnecessaryFutureImport(names) => {
if names.len() == 1 {
let import = &names[0];
format!("Unnecessary `__future__` import `{import}` for target Python version")
} else {
let imports = names.iter().map(|name| format!("`{name}`")).join(", ");
format!("Unnecessary `__future__` imports {imports} for target Python version")
}
}
CheckKind::UnnecessaryLRUCacheParams => {
"Unnessary parameters to functools.lru_cache".to_string()
"Unnecessary parameters to functools.lru_cache".to_string()
}
CheckKind::UnnecessaryEncodeUTF8 => "Unnecessary call to `encode` as UTF-8".to_string(),
// pydocstyle
CheckKind::FitsOnOneLine => "One-line docstring should fit on one line".to_string(),
CheckKind::BlankLineAfterSummary => {
@@ -1770,6 +1879,8 @@ impl CheckKind {
| CheckKind::UnnecessaryAbspath
| CheckKind::UnnecessaryCollectionCall(_)
| CheckKind::UnnecessaryComprehension(_)
| CheckKind::UnnecessaryEncodeUTF8
| CheckKind::UnnecessaryFutureImport(_)
| CheckKind::UnnecessaryGeneratorDict
| CheckKind::UnnecessaryGeneratorList
| CheckKind::UnnecessaryGeneratorSet

View File

@@ -53,6 +53,7 @@ pub enum CheckCodePrefix {
B016,
B017,
B018,
B019,
B02,
B025,
B026,
@@ -264,6 +265,7 @@ pub enum CheckCodePrefix {
U01,
U010,
U011,
U012,
W,
W2,
W29,
@@ -271,6 +273,23 @@ pub enum CheckCodePrefix {
W6,
W60,
W605,
YTT,
YTT1,
YTT10,
YTT101,
YTT102,
YTT103,
YTT2,
YTT20,
YTT201,
YTT202,
YTT203,
YTT204,
YTT3,
YTT30,
YTT301,
YTT302,
YTT303,
}
#[derive(PartialEq, Eq, PartialOrd, Ord)]
@@ -351,6 +370,7 @@ impl CheckCodePrefix {
CheckCode::B016,
CheckCode::B017,
CheckCode::B018,
CheckCode::B019,
CheckCode::B025,
CheckCode::B026,
],
@@ -371,6 +391,7 @@ impl CheckCodePrefix {
CheckCode::B016,
CheckCode::B017,
CheckCode::B018,
CheckCode::B019,
CheckCode::B025,
CheckCode::B026,
],
@@ -401,6 +422,7 @@ impl CheckCodePrefix {
CheckCode::B016,
CheckCode::B017,
CheckCode::B018,
CheckCode::B019,
],
CheckCodePrefix::B010 => vec![CheckCode::B010],
CheckCodePrefix::B011 => vec![CheckCode::B011],
@@ -410,6 +432,7 @@ impl CheckCodePrefix {
CheckCodePrefix::B016 => vec![CheckCode::B016],
CheckCodePrefix::B017 => vec![CheckCode::B017],
CheckCodePrefix::B018 => vec![CheckCode::B018],
CheckCodePrefix::B019 => vec![CheckCode::B019],
CheckCodePrefix::B02 => vec![CheckCode::B025, CheckCode::B026],
CheckCodePrefix::B025 => vec![CheckCode::B025],
CheckCodePrefix::B026 => vec![CheckCode::B026],
@@ -982,6 +1005,7 @@ impl CheckCodePrefix {
CheckCode::U009,
CheckCode::U010,
CheckCode::U011,
CheckCode::U012,
],
CheckCodePrefix::U0 => vec![
CheckCode::U001,
@@ -995,6 +1019,7 @@ impl CheckCodePrefix {
CheckCode::U009,
CheckCode::U010,
CheckCode::U011,
CheckCode::U012,
],
CheckCodePrefix::U00 => vec![
CheckCode::U001,
@@ -1016,9 +1041,10 @@ impl CheckCodePrefix {
CheckCodePrefix::U007 => vec![CheckCode::U007],
CheckCodePrefix::U008 => vec![CheckCode::U008],
CheckCodePrefix::U009 => vec![CheckCode::U009],
CheckCodePrefix::U01 => vec![CheckCode::U010, CheckCode::U011],
CheckCodePrefix::U01 => vec![CheckCode::U010, CheckCode::U011, CheckCode::U012],
CheckCodePrefix::U010 => vec![CheckCode::U010],
CheckCodePrefix::U011 => vec![CheckCode::U011],
CheckCodePrefix::U012 => vec![CheckCode::U012],
CheckCodePrefix::W => vec![CheckCode::W292, CheckCode::W605],
CheckCodePrefix::W2 => vec![CheckCode::W292],
CheckCodePrefix::W29 => vec![CheckCode::W292],
@@ -1026,6 +1052,44 @@ impl CheckCodePrefix {
CheckCodePrefix::W6 => vec![CheckCode::W605],
CheckCodePrefix::W60 => vec![CheckCode::W605],
CheckCodePrefix::W605 => vec![CheckCode::W605],
CheckCodePrefix::YTT => vec![
CheckCode::YTT101,
CheckCode::YTT102,
CheckCode::YTT103,
CheckCode::YTT201,
CheckCode::YTT202,
CheckCode::YTT203,
CheckCode::YTT204,
CheckCode::YTT301,
CheckCode::YTT302,
CheckCode::YTT303,
],
CheckCodePrefix::YTT1 => vec![CheckCode::YTT101, CheckCode::YTT102, CheckCode::YTT103],
CheckCodePrefix::YTT10 => vec![CheckCode::YTT101, CheckCode::YTT102, CheckCode::YTT103],
CheckCodePrefix::YTT101 => vec![CheckCode::YTT101],
CheckCodePrefix::YTT102 => vec![CheckCode::YTT102],
CheckCodePrefix::YTT103 => vec![CheckCode::YTT103],
CheckCodePrefix::YTT2 => vec![
CheckCode::YTT201,
CheckCode::YTT202,
CheckCode::YTT203,
CheckCode::YTT204,
],
CheckCodePrefix::YTT20 => vec![
CheckCode::YTT201,
CheckCode::YTT202,
CheckCode::YTT203,
CheckCode::YTT204,
],
CheckCodePrefix::YTT201 => vec![CheckCode::YTT201],
CheckCodePrefix::YTT202 => vec![CheckCode::YTT202],
CheckCodePrefix::YTT203 => vec![CheckCode::YTT203],
CheckCodePrefix::YTT204 => vec![CheckCode::YTT204],
CheckCodePrefix::YTT3 => vec![CheckCode::YTT301, CheckCode::YTT302, CheckCode::YTT303],
CheckCodePrefix::YTT30 => vec![CheckCode::YTT301, CheckCode::YTT302, CheckCode::YTT303],
CheckCodePrefix::YTT301 => vec![CheckCode::YTT301],
CheckCodePrefix::YTT302 => vec![CheckCode::YTT302],
CheckCodePrefix::YTT303 => vec![CheckCode::YTT303],
}
}
}
@@ -1079,6 +1143,7 @@ impl CheckCodePrefix {
CheckCodePrefix::B016 => PrefixSpecificity::Explicit,
CheckCodePrefix::B017 => PrefixSpecificity::Explicit,
CheckCodePrefix::B018 => PrefixSpecificity::Explicit,
CheckCodePrefix::B019 => PrefixSpecificity::Explicit,
CheckCodePrefix::B02 => PrefixSpecificity::Tens,
CheckCodePrefix::B025 => PrefixSpecificity::Explicit,
CheckCodePrefix::B026 => PrefixSpecificity::Explicit,
@@ -1290,6 +1355,7 @@ impl CheckCodePrefix {
CheckCodePrefix::U01 => PrefixSpecificity::Tens,
CheckCodePrefix::U010 => PrefixSpecificity::Explicit,
CheckCodePrefix::U011 => PrefixSpecificity::Explicit,
CheckCodePrefix::U012 => PrefixSpecificity::Explicit,
CheckCodePrefix::W => PrefixSpecificity::Category,
CheckCodePrefix::W2 => PrefixSpecificity::Hundreds,
CheckCodePrefix::W29 => PrefixSpecificity::Tens,
@@ -1297,6 +1363,23 @@ impl CheckCodePrefix {
CheckCodePrefix::W6 => PrefixSpecificity::Hundreds,
CheckCodePrefix::W60 => PrefixSpecificity::Tens,
CheckCodePrefix::W605 => PrefixSpecificity::Explicit,
CheckCodePrefix::YTT => PrefixSpecificity::Category,
CheckCodePrefix::YTT1 => PrefixSpecificity::Hundreds,
CheckCodePrefix::YTT10 => PrefixSpecificity::Tens,
CheckCodePrefix::YTT101 => PrefixSpecificity::Explicit,
CheckCodePrefix::YTT102 => PrefixSpecificity::Explicit,
CheckCodePrefix::YTT103 => PrefixSpecificity::Explicit,
CheckCodePrefix::YTT2 => PrefixSpecificity::Hundreds,
CheckCodePrefix::YTT20 => PrefixSpecificity::Tens,
CheckCodePrefix::YTT201 => PrefixSpecificity::Explicit,
CheckCodePrefix::YTT202 => PrefixSpecificity::Explicit,
CheckCodePrefix::YTT203 => PrefixSpecificity::Explicit,
CheckCodePrefix::YTT204 => PrefixSpecificity::Explicit,
CheckCodePrefix::YTT3 => PrefixSpecificity::Hundreds,
CheckCodePrefix::YTT30 => PrefixSpecificity::Tens,
CheckCodePrefix::YTT301 => PrefixSpecificity::Explicit,
CheckCodePrefix::YTT302 => PrefixSpecificity::Explicit,
CheckCodePrefix::YTT303 => PrefixSpecificity::Explicit,
}
}
}

1
src/flake8_2020/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub mod plugins;

192
src/flake8_2020/plugins.rs Normal file
View File

@@ -0,0 +1,192 @@
use num_bigint::BigInt;
use rustpython_ast::{Cmpop, Constant, Expr, ExprKind, Located};
use crate::ast::helpers::match_name_or_attr_from_module;
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checks::{Check, CheckCode, CheckKind};
fn is_sys(checker: &Checker, expr: &Expr, target: &str) -> bool {
match_name_or_attr_from_module(expr, target, "sys", checker.from_imports.get("sys"))
}
/// YTT101, YTT102, YTT301, YTT303
pub fn subscript(checker: &mut Checker, value: &Expr, slice: &Expr) {
if is_sys(checker, value, "version") {
match &slice.node {
ExprKind::Slice {
lower: None,
upper: Some(upper),
step: None,
..
} => {
if let ExprKind::Constant {
value: Constant::Int(i),
..
} = &upper.node
{
if *i == BigInt::from(1)
&& checker.settings.enabled.contains(&CheckCode::YTT303)
{
checker.add_check(Check::new(
CheckKind::SysVersionSlice1Referenced,
Range::from_located(value),
));
} else if *i == BigInt::from(3)
&& checker.settings.enabled.contains(&CheckCode::YTT101)
{
checker.add_check(Check::new(
CheckKind::SysVersionSlice3Referenced,
Range::from_located(value),
));
}
}
}
ExprKind::Constant {
value: Constant::Int(i),
..
} => {
if *i == BigInt::from(2) && checker.settings.enabled.contains(&CheckCode::YTT102) {
checker.add_check(Check::new(
CheckKind::SysVersion2Referenced,
Range::from_located(value),
));
} else if *i == BigInt::from(0)
&& checker.settings.enabled.contains(&CheckCode::YTT301)
{
checker.add_check(Check::new(
CheckKind::SysVersion0Referenced,
Range::from_located(value),
));
}
}
_ => {}
}
}
}
/// YTT103, YTT201, YTT203, YTT204, YTT302
pub fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], comparators: &[Expr]) {
match &left.node {
ExprKind::Subscript { value, slice, .. } if is_sys(checker, value, "version_info") => {
if let ExprKind::Constant {
value: Constant::Int(i),
..
} = &slice.node
{
if *i == BigInt::from(0) {
if let (
[Cmpop::Eq | Cmpop::NotEq],
[Located {
node:
ExprKind::Constant {
value: Constant::Int(n),
..
},
..
}],
) = (ops, comparators)
{
if *n == BigInt::from(3)
&& checker.settings.enabled.contains(&CheckCode::YTT201)
{
checker.add_check(Check::new(
CheckKind::SysVersionInfo0Eq3Referenced,
Range::from_located(left),
));
}
}
} else if *i == BigInt::from(1) {
if let (
[Cmpop::Lt | Cmpop::LtE | Cmpop::Gt | Cmpop::GtE],
[Located {
node:
ExprKind::Constant {
value: Constant::Int(_),
..
},
..
}],
) = (ops, comparators)
{
if checker.settings.enabled.contains(&CheckCode::YTT203) {
checker.add_check(Check::new(
CheckKind::SysVersionInfo1CmpInt,
Range::from_located(left),
));
}
}
}
}
}
ExprKind::Attribute { value, attr, .. }
if is_sys(checker, value, "version_info") && attr == "minor" =>
{
if let (
[Cmpop::Lt | Cmpop::LtE | Cmpop::Gt | Cmpop::GtE],
[Located {
node:
ExprKind::Constant {
value: Constant::Int(_),
..
},
..
}],
) = (ops, comparators)
{
if checker.settings.enabled.contains(&CheckCode::YTT204) {
checker.add_check(Check::new(
CheckKind::SysVersionInfoMinorCmpInt,
Range::from_located(left),
));
}
}
}
_ => {}
}
if is_sys(checker, left, "version") {
if let (
[Cmpop::Lt | Cmpop::LtE | Cmpop::Gt | Cmpop::GtE],
[Located {
node:
ExprKind::Constant {
value: Constant::Str(s),
..
},
..
}],
) = (ops, comparators)
{
if s.len() == 1 {
if checker.settings.enabled.contains(&CheckCode::YTT302) {
checker.add_check(Check::new(
CheckKind::SysVersionCmpStr10,
Range::from_located(left),
));
}
} else if checker.settings.enabled.contains(&CheckCode::YTT103) {
checker.add_check(Check::new(
CheckKind::SysVersionCmpStr3,
Range::from_located(left),
));
}
}
}
}
/// YTT202
pub fn name_or_attribute(checker: &mut Checker, expr: &Expr) {
if match_name_or_attr_from_module(expr, "PY3", "six", checker.from_imports.get("six"))
&& checker.settings.enabled.contains(&CheckCode::YTT202)
{
checker.add_check(Check::new(
CheckKind::SixPY3Referenced,
Range::from_located(expr),
));
}
}

View File

@@ -0,0 +1,49 @@
use rustpython_ast::{Expr, ExprKind};
use crate::ast::helpers::{compose_call_path, match_name_or_attr_from_module};
use crate::ast::types::{Range, ScopeKind};
use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};
fn is_cache_func(checker: &Checker, expr: &Expr) -> bool {
match_name_or_attr_from_module(
expr,
"lru_cache",
"functools",
checker.from_imports.get("functools"),
) || match_name_or_attr_from_module(
expr,
"cache",
"functools",
checker.from_imports.get("functools"),
)
}
/// B019
pub fn cached_instance_method(checker: &mut Checker, decorator_list: &[Expr]) {
if matches!(checker.current_scope().kind, ScopeKind::Class(_)) {
for decorator in decorator_list {
// TODO(charlie): This should take into account `classmethod-decorators` and
// `staticmethod-decorators`.
if let Some(decorator_path) = compose_call_path(decorator) {
if decorator_path == "classmethod" || decorator_path == "staticmethod" {
return;
}
}
}
for decorator in decorator_list {
if is_cache_func(
checker,
match &decorator.node {
ExprKind::Call { func, .. } => func,
_ => decorator,
},
) {
checker.add_check(Check::new(
CheckKind::CachedInstanceMethod,
Range::from_located(decorator),
));
}
}
}
}

View File

@@ -8,6 +8,7 @@ use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};
use crate::flake8_bugbear::plugins::mutable_argument_default::is_mutable_func;
// TODO(charlie): Verify imports for each of the imported members.
const IMMUTABLE_FUNCS: [&str; 11] = [
"tuple",
"frozenset",

View File

@@ -1,6 +1,7 @@
pub use assert_false::assert_false;
pub use assert_raises_exception::assert_raises_exception;
pub use assignment_to_os_environ::assignment_to_os_environ;
pub use cached_instance_method::cached_instance_method;
pub use cannot_raise_literal::cannot_raise_literal;
pub use duplicate_exceptions::{duplicate_exceptions, duplicate_handler_exceptions};
pub use function_call_argument_default::function_call_argument_default;
@@ -19,6 +20,7 @@ pub use useless_expression::useless_expression;
mod assert_false;
mod assert_raises_exception;
mod assignment_to_os_environ;
mod cached_instance_method;
mod cannot_raise_literal;
mod duplicate_exceptions;
mod function_call_argument_default;

View File

@@ -4,6 +4,7 @@ use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};
// TODO(charlie): Verify imports for each of the imported members.
pub fn is_mutable_func(expr: &Expr) -> bool {
match &expr.node {
ExprKind::Name { id, .. }

View File

@@ -7,6 +7,7 @@ use crate::check_ast::Checker;
use crate::checks::CheckCode;
use crate::flake8_print::checks;
/// T201, T203
pub fn print_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
if let Some(mut check) = checks::print_call(
expr,
@@ -26,7 +27,6 @@ pub fn print_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
.iter()
.map(|index| checker.parents[*index])
.collect();
match helpers::remove_stmt(
checker.parents[context.defined_by],
context.defined_in.map(|index| checker.parents[index]),
@@ -38,7 +38,7 @@ pub fn print_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
}
check.amend(fix)
}
Err(e) => error!("Failed to fix unused imports: {}", e),
Err(e) => error!("Failed to remove print call: {}", e),
}
}
}

View File

@@ -12,16 +12,20 @@ pub enum ImportType {
StandardLibrary,
ThirdParty,
FirstParty,
LocalFolder,
}
pub fn categorize(
module_base: &str,
level: &Option<usize>,
src: &[PathBuf],
known_first_party: &BTreeSet<String>,
known_third_party: &BTreeSet<String>,
extra_standard_library: &BTreeSet<String>,
) -> ImportType {
if known_first_party.contains(module_base) {
if level.map(|level| level > 0).unwrap_or(false) {
ImportType::LocalFolder
} else if known_first_party.contains(module_base) {
ImportType::FirstParty
} else if known_third_party.contains(module_base) {
ImportType::ThirdParty
@@ -31,21 +35,16 @@ pub fn categorize(
import_type.clone()
} else if KNOWN_STANDARD_LIBRARY.contains(module_base) {
ImportType::StandardLibrary
} else if find_local(src, module_base) {
ImportType::FirstParty
} else {
if find_local(src, module_base) {
ImportType::FirstParty
} else {
ImportType::ThirdParty
}
ImportType::ThirdParty
}
}
static STATIC_CLASSIFICATIONS: Lazy<BTreeMap<&'static str, ImportType>> = Lazy::new(|| {
BTreeMap::from([
("__future__", ImportType::Future),
("__main__", ImportType::FirstParty),
// Force `disutils` to be considered third-party.
("disutils", ImportType::ThirdParty),
// Relative imports (e.g., `from . import module`).
("", ImportType::FirstParty),
])

View File

@@ -1,15 +1,18 @@
use std::collections::{BTreeMap, BTreeSet};
use std::path::PathBuf;
use itertools::Itertools;
use ropey::RopeBuilder;
use rustpython_ast::{Stmt, StmtKind};
use crate::isort::categorize::{categorize, ImportType};
use crate::isort::types::{AliasData, ImportBlock, ImportFromData, Importable};
use crate::isort::sorting::{member_key, module_key};
use crate::isort::types::{AliasData, ImportBlock, ImportFromData, Importable, OrderedImportBlock};
mod categorize;
pub mod plugins;
pub mod settings;
mod sorting;
pub mod track;
mod types;
@@ -62,6 +65,7 @@ fn categorize_imports<'a>(
for alias in block.import {
let import_type = categorize(
&alias.module_base(),
&None,
src,
known_first_party,
known_third_party,
@@ -77,6 +81,7 @@ fn categorize_imports<'a>(
for (import_from, aliases) in block.import_from {
let classification = categorize(
&import_from.module_base(),
import_from.level,
src,
known_first_party,
known_third_party,
@@ -91,7 +96,37 @@ fn categorize_imports<'a>(
block_by_type
}
pub fn sort_imports(
fn sort_imports(block: ImportBlock) -> OrderedImportBlock {
let mut ordered: OrderedImportBlock = Default::default();
// Sort `StmtKind::Import`.
for import in block
.import
.into_iter()
.sorted_by_cached_key(|alias| module_key(alias.name))
{
ordered.import.push(import);
}
// Sort `StmtKind::ImportFrom`.
for (import_from, aliases) in
block
.import_from
.into_iter()
.sorted_by_cached_key(|(import_from, _)| {
import_from.module.as_ref().map(|module| module_key(module))
})
{
ordered.import_from.push((
import_from,
aliases
.into_iter()
.sorted_by_cached_key(|alias| member_key(alias.name))
.collect(),
));
}
ordered
}
pub fn format_imports(
block: Vec<&Stmt>,
line_length: &usize,
src: &[PathBuf],
@@ -114,45 +149,41 @@ pub fn sort_imports(
// Generate replacement source code.
let mut output = RopeBuilder::new();
let mut first_block = true;
for import_type in [
ImportType::Future,
ImportType::StandardLibrary,
ImportType::ThirdParty,
ImportType::FirstParty,
] {
if let Some(import_block) = block_by_type.get(&import_type) {
// Add a blank line between every section.
if !first_block {
output.append("\n");
for import_block in block_by_type.into_values() {
let import_block = sort_imports(import_block);
// Add a blank line between every section.
if !first_block {
output.append("\n");
} else {
first_block = false;
}
// Format `StmtKind::Import` statements.
for AliasData { name, asname } in import_block.import.iter() {
if let Some(asname) = asname {
output.append(&format!("import {} as {}\n", name, asname));
} else {
first_block = false;
output.append(&format!("import {}\n", name));
}
}
// Format `StmtKind::Import` statements.
for AliasData { name, asname } in import_block.import.iter() {
if let Some(asname) = asname {
output.append(&format!("import {} as {}\n", name, asname));
} else {
output.append(&format!("import {}\n", name));
}
}
// Format `StmtKind::ImportFrom` statements.
for (import_from, aliases) in import_block.import_from.iter() {
let prelude: String = format!("from {} import ", import_from.module_name());
let members: Vec<String> = aliases
.iter()
.map(|AliasData { name, asname }| {
if let Some(asname) = asname {
format!("{} as {}", name, asname)
} else {
name.to_string()
}
})
.collect();
// Format `StmtKind::ImportFrom` statements.
for (import_from, aliases) in import_block.import_from.iter() {
let prelude: String = format!("from {} import ", import_from.module_name());
let members: Vec<String> = aliases
.iter()
.map(|AliasData { name, asname }| {
if let Some(asname) = asname {
format!("{} as {}", name, asname)
} else {
name.to_string()
}
})
.collect();
// Can we fit the import on a single line?
let expected_len: usize =
// Can we fit the import on a single line?
let expected_len: usize =
// `from base import `
prelude.len()
// `member( as alias)?`
@@ -160,36 +191,35 @@ pub fn sort_imports(
// `, `
+ 2 * (members.len() - 1);
if expected_len <= *line_length {
// `from base import `
output.append(&prelude);
// `member( as alias)?(, )?`
for (index, part) in members.into_iter().enumerate() {
if index > 0 {
output.append(", ");
}
output.append(&part);
if expected_len <= *line_length {
// `from base import `
output.append(&prelude);
// `member( as alias)?(, )?`
for (index, part) in members.into_iter().enumerate() {
if index > 0 {
output.append(", ");
}
// `\n`
output.append("\n");
} else {
// `from base import (\n`
output.append(&prelude);
output.append("(");
output.append("\n");
output.append(&part);
}
// `\n`
output.append("\n");
} else {
// `from base import (\n`
output.append(&prelude);
output.append("(");
output.append("\n");
// ` member( as alias)?,\n`
for part in members {
output.append(INDENT);
output.append(&part);
output.append(",");
output.append("\n");
}
// `)\n`
output.append(")");
// ` member( as alias)?,\n`
for part in members {
output.append(INDENT);
output.append(&part);
output.append(",");
output.append("\n");
}
// `)\n`
output.append(")");
output.append("\n");
}
}
}
@@ -214,10 +244,12 @@ mod tests {
#[test_case(Path::new("import_from_after_import.py"))]
#[test_case(Path::new("leading_prefix.py"))]
#[test_case(Path::new("no_reorder_within_section.py"))]
#[test_case(Path::new("order_by_type.py"))]
#[test_case(Path::new("preserve_indentation.py"))]
#[test_case(Path::new("reorder_within_section.py"))]
#[test_case(Path::new("separate_first_party_imports.py"))]
#[test_case(Path::new("separate_future_imports.py"))]
#[test_case(Path::new("separate_local_folder_imports.py"))]
#[test_case(Path::new("separate_third_party_imports.py"))]
#[test_case(Path::new("skip.py"))]
#[test_case(Path::new("trailing_suffix.py"))]

View File

@@ -5,7 +5,7 @@ use crate::ast::types::Range;
use crate::autofix::{fixer, Fix};
use crate::checks::CheckKind;
use crate::docstrings::helpers::leading_space;
use crate::isort::sort_imports;
use crate::isort::format_imports;
use crate::{Check, Settings, SourceCodeLocator};
fn extract_range(body: &[&Stmt]) -> Range {
@@ -62,9 +62,9 @@ pub fn check_imports(
let has_trailing_content = match_trailing_content(&body, locator);
// Generate the sorted import block.
let expected = sort_imports(
let expected = format_imports(
body,
&settings.line_length,
&(settings.line_length - indentation.len()),
&settings.src,
&settings.isort.known_first_party,
&settings.isort.known_third_party,

View File

@@ -2,5 +2,21 @@
source: src/isort/mod.rs
expression: checks
---
[]
- kind: UnsortedImports
location:
row: 7
column: 0
end_location:
row: 15
column: 0
fix:
patch:
content: " from line_with_88 import aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n from line_with_89 import (\n aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n )\n from line_with_90 import (\n aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n )\n from line_with_91 import (\n aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n )\n from line_with_92 import (\n aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n )\n from line_with_93 import (\n aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n )\n"
location:
row: 7
column: 0
end_location:
row: 15
column: 0
applied: false

View File

@@ -0,0 +1,22 @@
---
source: src/isort/mod.rs
expression: checks
---
- kind: UnsortedImports
location:
row: 1
column: 0
end_location:
row: 13
column: 0
fix:
patch:
content: "import glob\nimport os\nimport shutil\nimport tempfile\nimport time\nfrom subprocess import PIPE, STDOUT, Popen\n\nimport BAR\nimport bar\nimport FOO\nimport foo\nimport StringIO\nfrom module import BASIC, CONSTANT, Apple, Class, function\n"
location:
row: 1
column: 0
end_location:
row: 13
column: 0
applied: false

View File

@@ -0,0 +1,22 @@
---
source: src/isort/mod.rs
expression: checks
---
- kind: UnsortedImports
location:
row: 1
column: 0
end_location:
row: 5
column: 0
fix:
patch:
content: "import os\nimport sys\n\nimport leading_prefix\n\nfrom . import leading_prefix\n"
location:
row: 1
column: 0
end_location:
row: 5
column: 0
applied: false

34
src/isort/sorting.rs Normal file
View File

@@ -0,0 +1,34 @@
/// See: https://github.com/PyCQA/isort/blob/12cc5fbd67eebf92eb2213b03c07b138ae1fb448/isort/sorting.py#L13
use crate::python::string;
#[derive(PartialOrd, Ord, PartialEq, Eq)]
pub enum Prefix {
Constants,
Classes,
Variables,
}
pub fn module_key(module_name: &str) -> String {
module_name.to_lowercase()
}
pub fn member_key(member_name: &str) -> (Prefix, String) {
(
if member_name.len() > 1 && string::is_upper(member_name) {
// Ex) `CONSTANT`
Prefix::Constants
} else if member_name
.chars()
.next()
.map(|char| char.is_uppercase())
.unwrap_or(false)
{
// Ex) `Class`
Prefix::Classes
} else {
// Ex) `variable`
Prefix::Variables
},
member_name.to_lowercase(),
)
}

View File

@@ -53,3 +53,11 @@ pub struct ImportBlock<'a> {
// Set of (name, asname).
pub import: BTreeSet<AliasData<'a>>,
}
#[derive(Debug, Default)]
pub struct OrderedImportBlock<'a> {
// Map from (module, level) to `AliasData`.
pub import_from: Vec<(ImportFromData<'a>, Vec<AliasData<'a>>)>,
// Set of (name, asname).
pub import: Vec<AliasData<'a>>,
}

View File

@@ -27,6 +27,7 @@ pub mod code_gen;
mod cst;
mod directives;
mod docstrings;
mod flake8_2020;
pub mod flake8_annotations;
mod flake8_bugbear;
mod flake8_builtins;

View File

@@ -342,6 +342,7 @@ mod tests {
#[test_case(CheckCode::B016, Path::new("B016.py"); "B016")]
#[test_case(CheckCode::B017, Path::new("B017.py"); "B017")]
#[test_case(CheckCode::B018, Path::new("B018.py"); "B018")]
#[test_case(CheckCode::B019, Path::new("B019.py"); "B019")]
#[test_case(CheckCode::B025, Path::new("B025.py"); "B025")]
#[test_case(CheckCode::B026, Path::new("B026.py"); "B026")]
#[test_case(CheckCode::C400, Path::new("C400.py"); "C400")]
@@ -485,6 +486,7 @@ mod tests {
#[test_case(CheckCode::U010, Path::new("U010.py"); "U010")]
#[test_case(CheckCode::U011, Path::new("U011_0.py"); "U011_0")]
#[test_case(CheckCode::U011, Path::new("U011_1.py"); "U011_1")]
#[test_case(CheckCode::U012, Path::new("U012.py"); "U012")]
#[test_case(CheckCode::W292, Path::new("W292_0.py"); "W292_0")]
#[test_case(CheckCode::W292, Path::new("W292_1.py"); "W292_1")]
#[test_case(CheckCode::W292, Path::new("W292_2.py"); "W292_2")]
@@ -493,6 +495,16 @@ mod tests {
#[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::YTT101, Path::new("YTT101.py"); "YTT101")]
#[test_case(CheckCode::YTT102, Path::new("YTT102.py"); "YTT102")]
#[test_case(CheckCode::YTT103, Path::new("YTT103.py"); "YTT103")]
#[test_case(CheckCode::YTT201, Path::new("YTT201.py"); "YTT201")]
#[test_case(CheckCode::YTT202, Path::new("YTT202.py"); "YTT202")]
#[test_case(CheckCode::YTT203, Path::new("YTT203.py"); "YTT203")]
#[test_case(CheckCode::YTT204, Path::new("YTT204.py"); "YTT204")]
#[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")]
fn checks(check_code: CheckCode, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy());
let mut checks = test_path(

View File

@@ -5,6 +5,7 @@ use crate::checks::{Check, CheckKind};
use crate::pep8_naming::helpers;
use crate::pep8_naming::helpers::FunctionType;
use crate::pep8_naming::settings::Settings;
use crate::python::string;
/// N801
pub fn invalid_class_name(class_def: &Stmt, name: &str) -> Option<Check> {
@@ -133,7 +134,7 @@ pub fn constant_imported_as_non_constant(
name: &str,
asname: &str,
) -> Option<Check> {
if helpers::is_upper(name) && !helpers::is_upper(asname) {
if string::is_upper(name) && !string::is_upper(asname) {
return Some(Check::new(
CheckKind::ConstantImportedAsNonConstant(name.to_string(), asname.to_string()),
Range::from_located(import_from),
@@ -148,7 +149,7 @@ pub fn lowercase_imported_as_non_lowercase(
name: &str,
asname: &str,
) -> Option<Check> {
if !helpers::is_upper(name) && helpers::is_lower(name) && asname.to_lowercase() != asname {
if !string::is_upper(name) && string::is_lower(name) && asname.to_lowercase() != asname {
return Some(Check::new(
CheckKind::LowercaseImportedAsNonLowercase(name.to_string(), asname.to_string()),
Range::from_located(import_from),
@@ -163,7 +164,7 @@ pub fn camelcase_imported_as_lowercase(
name: &str,
asname: &str,
) -> Option<Check> {
if helpers::is_camelcase(name) && helpers::is_lower(asname) {
if helpers::is_camelcase(name) && string::is_lower(asname) {
return Some(Check::new(
CheckKind::CamelcaseImportedAsLowercase(name.to_string(), asname.to_string()),
Range::from_located(import_from),
@@ -179,8 +180,8 @@ pub fn camelcase_imported_as_constant(
asname: &str,
) -> Option<Check> {
if helpers::is_camelcase(name)
&& !helpers::is_lower(asname)
&& helpers::is_upper(asname)
&& !string::is_lower(asname)
&& string::is_upper(asname)
&& !helpers::is_acronym(name, asname)
{
return Some(Check::new(
@@ -230,8 +231,8 @@ pub fn camelcase_imported_as_acronym(
asname: &str,
) -> Option<Check> {
if helpers::is_camelcase(name)
&& !helpers::is_lower(asname)
&& helpers::is_upper(asname)
&& !string::is_lower(asname)
&& string::is_upper(asname)
&& helpers::is_acronym(name, asname)
{
return Some(Check::new(

View File

@@ -4,6 +4,7 @@ use rustpython_ast::{Expr, ExprKind};
use crate::ast::helpers::match_name_or_attr;
use crate::ast::types::{Scope, ScopeKind};
use crate::pep8_naming::settings::Settings;
use crate::python::string::{is_lower, is_upper};
const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"];
const METACLASS_BASES: [&str; 2] = ["type", "ABCMeta"];
@@ -59,30 +60,6 @@ pub fn function_type(
}
}
pub fn is_lower(s: &str) -> bool {
let mut cased = false;
for c in s.chars() {
if c.is_uppercase() {
return false;
} else if !cased && c.is_lowercase() {
cased = true;
}
}
cased
}
pub fn is_upper(s: &str) -> bool {
let mut cased = false;
for c in s.chars() {
if c.is_lowercase() {
return false;
} else if !cased && c.is_uppercase() {
cased = true;
}
}
cased
}
pub fn is_camelcase(name: &str) -> bool {
!is_lower(name) && !is_upper(name) && !name.contains('_')
}
@@ -103,31 +80,7 @@ pub fn is_acronym(name: &str, asname: &str) -> bool {
#[cfg(test)]
mod tests {
use crate::pep8_naming::helpers::{
is_acronym, is_camelcase, is_lower, is_mixed_case, is_upper,
};
#[test]
fn test_is_lower() -> () {
assert!(is_lower("abc"));
assert!(is_lower("a_b_c"));
assert!(is_lower("a2c"));
assert!(!is_lower("aBc"));
assert!(!is_lower("ABC"));
assert!(!is_lower(""));
assert!(!is_lower("_"));
}
#[test]
fn test_is_upper() -> () {
assert!(is_upper("ABC"));
assert!(is_upper("A_B_C"));
assert!(is_upper("A2C"));
assert!(!is_upper("aBc"));
assert!(!is_upper("abc"));
assert!(!is_upper(""));
assert!(!is_upper("_"));
}
use crate::pep8_naming::helpers::{is_acronym, is_camelcase, is_mixed_case};
#[test]
fn test_is_camelcase() -> () {

View File

@@ -1,5 +1,6 @@
pub mod builtins;
pub mod future;
pub mod keyword;
pub mod string;
pub mod sys;
pub mod typing;

50
src/python/string.rs Normal file
View File

@@ -0,0 +1,50 @@
pub fn is_lower(s: &str) -> bool {
let mut cased = false;
for c in s.chars() {
if c.is_uppercase() {
return false;
} else if !cased && c.is_lowercase() {
cased = true;
}
}
cased
}
pub fn is_upper(s: &str) -> bool {
let mut cased = false;
for c in s.chars() {
if c.is_lowercase() {
return false;
} else if !cased && c.is_uppercase() {
cased = true;
}
}
cased
}
#[cfg(test)]
mod tests {
use crate::python::string::{is_lower, is_upper};
#[test]
fn test_is_lower() -> () {
assert!(is_lower("abc"));
assert!(is_lower("a_b_c"));
assert!(is_lower("a2c"));
assert!(!is_lower("aBc"));
assert!(!is_lower("ABC"));
assert!(!is_lower(""));
assert!(!is_lower("_"));
}
#[test]
fn test_is_upper() -> () {
assert!(is_upper("ABC"));
assert!(is_upper("A_B_C"));
assert!(is_upper("A2C"));
assert!(!is_upper("aBc"));
assert!(!is_upper("abc"));
assert!(!is_upper(""));
assert!(!is_upper("_"));
}
}

View File

@@ -9,29 +9,6 @@ use crate::checks::{Check, CheckKind};
use crate::pyupgrade::types::Primitive;
use crate::settings::types::PythonVersion;
pub const PY33_PLUS_REMOVE_FUTURES: &[&str] = &[
"nested_scopes",
"generators",
"with_statement",
"division",
"absolute_import",
"with_statement",
"print_function",
"unicode_literals",
];
pub const PY37_PLUS_REMOVE_FUTURES: &[&str] = &[
"nested_scopes",
"generators",
"with_statement",
"division",
"absolute_import",
"with_statement",
"print_function",
"unicode_literals",
"generator_stop",
];
/// U008
pub fn super_args(
scope: &Scope,
@@ -183,23 +160,6 @@ pub fn type_of_primitive(func: &Expr, args: &[Expr], location: Range) -> Option<
None
}
/// U010
pub fn unnecessary_future_import(
version: PythonVersion,
name: &str,
location: Range,
) -> Option<Check> {
if (version >= PythonVersion::Py33 && PY33_PLUS_REMOVE_FUTURES.contains(&name))
|| (version >= PythonVersion::Py37 && PY37_PLUS_REMOVE_FUTURES.contains(&name))
{
return Some(Check::new(
CheckKind::UnnecessaryFutureImport(name.to_string()),
location,
));
}
None
}
/// U011
pub fn unnecessary_lru_cache_params(
decorator_list: &[Expr],

View File

@@ -1,11 +1,13 @@
use libcst_native::{Codegen, Expression, SmallStatement, Statement};
use rustpython_ast::{Expr, Keyword, Location};
use anyhow::Result;
use libcst_native::{Codegen, Expression, ImportNames, SmallStatement, Statement};
use rustpython_ast::{Expr, Keyword, Location, Stmt};
use rustpython_parser::lexer;
use rustpython_parser::lexer::Tok;
use crate::ast::helpers;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::autofix::{self, Fix};
use crate::cst::matchers::match_module;
use crate::source_code_locator::SourceCodeLocator;
/// Generate a fix to remove a base from a ClassDef statement.
@@ -41,7 +43,7 @@ pub fn remove_class_def_base(
}
return match (fix_start, fix_end) {
(Some(start), Some(end)) => Some(Fix::replacement("".to_string(), start, end)),
(Some(start), Some(end)) => Some(Fix::deletion(start, end)),
_ => None,
};
}
@@ -133,6 +135,63 @@ pub fn remove_super_arguments(locator: &SourceCodeLocator, expr: &Expr) -> Optio
None
}
/// U010
pub fn remove_unnecessary_future_import(
locator: &SourceCodeLocator,
removable: &[usize],
stmt: &Stmt,
parent: Option<&Stmt>,
deleted: &[&Stmt],
) -> Result<Fix> {
// TODO(charlie): DRY up with pyflakes::fixes::remove_unused_import_froms.
let module_text = locator.slice_source_code_range(&Range::from_located(stmt));
let mut tree = match_module(&module_text)?;
let body = if let Some(Statement::Simple(body)) = tree.body.first_mut() {
body
} else {
return Err(anyhow::anyhow!("Expected node to be: Statement::Simple"));
};
let body = if let Some(SmallStatement::ImportFrom(body)) = body.body.first_mut() {
body
} else {
return Err(anyhow::anyhow!(
"Expected node to be: SmallStatement::ImportFrom"
));
};
let aliases = if let ImportNames::Aliases(aliases) = &mut body.names {
aliases
} else {
return Err(anyhow::anyhow!("Expected node to be: Aliases"));
};
// Preserve the trailing comma (or not) from the last entry.
let trailing_comma = aliases.last().and_then(|alias| alias.comma.clone());
// TODO(charlie): This is quadratic.
for index in removable.iter().rev() {
aliases.remove(*index);
}
if let Some(alias) = aliases.last_mut() {
alias.comma = trailing_comma;
}
if aliases.is_empty() {
autofix::helpers::remove_stmt(stmt, parent, deleted)
} else {
let mut state = Default::default();
tree.codegen(&mut state);
Ok(Fix::replacement(
state.to_string(),
stmt.location,
stmt.end_location.unwrap(),
))
}
}
/// U011
pub fn remove_unnecessary_lru_cache_params(
locator: &SourceCodeLocator,

View File

@@ -28,6 +28,7 @@ static DEPRECATED_ALIASES: Lazy<BTreeMap<&'static str, &'static str>> = Lazy::ne
])
});
/// U005
pub fn deprecated_unittest_alias(checker: &mut Checker, expr: &Expr) {
if let ExprKind::Attribute { value, attr, .. } = &expr.node {
if let Some(target) = DEPRECATED_ALIASES.get(attr.as_str()) {

View File

@@ -2,6 +2,7 @@ pub use deprecated_unittest_alias::deprecated_unittest_alias;
pub use super_call_with_parameters::super_call_with_parameters;
pub use type_of_primitive::type_of_primitive;
pub use unnecessary_abspath::unnecessary_abspath;
pub use unnecessary_encode_utf8::unnecessary_encode_utf8;
pub use unnecessary_future_import::unnecessary_future_import;
pub use unnecessary_lru_cache_params::unnecessary_lru_cache_params;
pub use use_pep585_annotation::use_pep585_annotation;
@@ -13,6 +14,7 @@ mod deprecated_unittest_alias;
mod super_call_with_parameters;
mod type_of_primitive;
mod unnecessary_abspath;
mod unnecessary_encode_utf8;
mod unnecessary_future_import;
mod unnecessary_lru_cache_params;
mod use_pep585_annotation;

View File

@@ -5,6 +5,7 @@ use crate::check_ast::Checker;
use crate::pyupgrade;
use crate::pyupgrade::checks;
/// U008
pub fn super_call_with_parameters(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
// Only bother going through the super check at all if we're in a `super` call.
// (We check this in `check_super_args` too, so this is just an optimization.)

View File

@@ -6,6 +6,7 @@ use crate::check_ast::Checker;
use crate::checks::CheckKind;
use crate::pyupgrade::checks;
/// U003
pub fn type_of_primitive(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
if let Some(mut check) = checks::type_of_primitive(func, args, Range::from_located(expr)) {
if checker.patch() {

View File

@@ -5,6 +5,7 @@ use crate::autofix::Fix;
use crate::check_ast::Checker;
use crate::pyupgrade::checks;
/// U002
pub fn unnecessary_abspath(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
if let Some(mut check) = checks::unnecessary_abspath(func, args, Range::from_located(expr)) {
if checker.patch() {

View File

@@ -0,0 +1,152 @@
use rustpython_ast::{Constant, Expr, ExprKind, Keyword};
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};
use crate::source_code_locator::SourceCodeLocator;
const UTF8_LITERALS: &[&str] = &["utf-8", "utf8", "utf_8", "u8", "utf", "cp65001"];
fn match_encoded_variable(func: &Expr) -> Option<&Expr> {
if let ExprKind::Attribute {
value: variable,
attr,
..
} = &func.node
{
if attr == "encode" {
return Some(variable);
}
}
None
}
fn is_utf8_encoding_arg(arg: &Expr) -> bool {
if let ExprKind::Constant {
value: Constant::Str(value),
..
} = &arg.node
{
UTF8_LITERALS.contains(&value.to_lowercase().as_str())
} else {
false
}
}
fn is_default_encode(args: &Vec<Expr>, kwargs: &Vec<Keyword>) -> bool {
match (args.len(), kwargs.len()) {
// .encode()
(0, 0) => true,
// .encode(encoding)
(1, 0) => is_utf8_encoding_arg(&args[0]),
// .encode(kwarg=kwarg)
(0, 1) => {
kwargs[0].node.arg == Some("encoding".to_string())
&& is_utf8_encoding_arg(&kwargs[0].node.value)
}
// .encode(*args, **kwargs)
_ => false,
}
}
// Return a Fix for a default `encode` call removing the encoding argument,
// keyword, or positional.
fn delete_default_encode_arg_or_kwarg(
expr: &Expr,
args: &[Expr],
kwargs: &[Keyword],
patch: bool,
) -> Option<Check> {
if let Some(arg) = args.get(0) {
let mut check = Check::new(CheckKind::UnnecessaryEncodeUTF8, Range::from_located(expr));
if patch {
check.amend(Fix::deletion(arg.location, arg.end_location.unwrap()));
}
Some(check)
} else if let Some(kwarg) = kwargs.get(0) {
let mut check = Check::new(CheckKind::UnnecessaryEncodeUTF8, Range::from_located(expr));
if patch {
check.amend(Fix::deletion(kwarg.location, kwarg.end_location.unwrap()));
}
Some(check)
} else {
None
}
}
// Return a Fix replacing the call to encode by a `"b"` prefix on the string.
fn replace_with_bytes_literal(
expr: &Expr,
constant: &Expr,
locator: &SourceCodeLocator,
patch: bool,
) -> Check {
let mut check = Check::new(CheckKind::UnnecessaryEncodeUTF8, Range::from_located(expr));
if patch {
let content = locator.slice_source_code_range(&Range {
location: constant.location,
end_location: constant.end_location.unwrap(),
});
let content = format!(
"b{}",
content.trim_start_matches('u').trim_start_matches('U')
);
check.amend(Fix::replacement(
content,
expr.location,
expr.end_location.unwrap(),
))
}
check
}
/// U012
pub fn unnecessary_encode_utf8(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &Vec<Expr>,
kwargs: &Vec<Keyword>,
) {
if let Some(variable) = match_encoded_variable(func) {
match &variable.node {
ExprKind::Constant {
value: Constant::Str(literal),
..
} => {
// "str".encode()
// "str".encode("utf-8")
if is_default_encode(args, kwargs) {
if literal.is_ascii() {
// "foo".encode()
checker.add_check(replace_with_bytes_literal(
expr,
variable,
checker.locator,
checker.patch(),
));
} else {
// "unicode text©".encode("utf-8")
if let Some(check) =
delete_default_encode_arg_or_kwarg(expr, args, kwargs, checker.patch())
{
checker.add_check(check);
}
}
}
}
// f"foo{bar}".encode(*args, **kwargs)
ExprKind::JoinedStr { .. } => {
if is_default_encode(args, kwargs) {
if let Some(check) =
delete_default_encode_arg_or_kwarg(expr, args, kwargs, checker.patch())
{
checker.add_check(check);
}
}
}
_ => {}
}
}
}

View File

@@ -1,15 +1,84 @@
use std::collections::BTreeSet;
use log::error;
use rustpython_ast::{AliasData, Located};
use rustpython_parser::ast::Stmt;
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::pyupgrade::checks;
use crate::checks::{Check, CheckKind};
use crate::pyupgrade::fixes;
use crate::settings::types::PythonVersion;
pub fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, name: &str) {
if let Some(check) = checks::unnecessary_future_import(
checker.settings.target_version,
name,
Range::from_located(stmt),
) {
const PY33_PLUS_REMOVE_FUTURES: &[&str] = &[
"nested_scopes",
"generators",
"with_statement",
"division",
"absolute_import",
"with_statement",
"print_function",
"unicode_literals",
];
const PY37_PLUS_REMOVE_FUTURES: &[&str] = &[
"nested_scopes",
"generators",
"with_statement",
"division",
"absolute_import",
"with_statement",
"print_function",
"unicode_literals",
"generator_stop",
];
/// U010
pub fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, names: &[Located<AliasData>]) {
let target_version = checker.settings.target_version;
let mut removable_index: Vec<usize> = vec![];
let mut removable_names: BTreeSet<&str> = BTreeSet::new();
for (index, alias) in names.iter().enumerate() {
let name = alias.node.name.as_str();
if (target_version >= PythonVersion::Py33 && PY33_PLUS_REMOVE_FUTURES.contains(&name))
|| (target_version >= PythonVersion::Py37 && PY37_PLUS_REMOVE_FUTURES.contains(&name))
{
removable_index.push(index);
removable_names.insert(name);
}
}
if !removable_index.is_empty() {
let mut check = Check::new(
CheckKind::UnnecessaryFutureImport(
removable_names.into_iter().map(String::from).collect(),
),
Range::from_located(stmt),
);
if checker.patch() {
let context = checker.binding_context();
let deleted: Vec<&Stmt> = checker
.deletions
.iter()
.map(|index| checker.parents[*index])
.collect();
match fixes::remove_unnecessary_future_import(
checker.locator,
&removable_index,
checker.parents[context.defined_by],
context.defined_in.map(|index| checker.parents[index]),
&deleted,
) {
Ok(fix) => {
if fix.patch.content.is_empty() || fix.patch.content == "pass" {
checker.deletions.insert(context.defined_by);
}
check.amend(fix);
}
Err(e) => error!("Failed to remove __future__ import: {}", e),
}
}
checker.add_check(check);
}
}

View File

@@ -3,6 +3,7 @@ use rustpython_parser::ast::Expr;
use crate::check_ast::Checker;
use crate::pyupgrade::{checks, fixes};
/// U011
pub fn unnecessary_lru_cache_params(checker: &mut Checker, decorator_list: &[Expr]) {
if let Some(mut check) = checks::unnecessary_lru_cache_params(
decorator_list,

View File

@@ -5,6 +5,7 @@ use crate::autofix::Fix;
use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};
/// U006
pub fn use_pep585_annotation(checker: &mut Checker, expr: &Expr, id: &str) {
let mut check = Check::new(
CheckKind::UsePEP585Annotation(id.to_string()),

View File

@@ -41,6 +41,7 @@ fn union(elts: &[Expr]) -> Expr {
}
}
/// U007
pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, slice: &Expr) {
if checker.match_typing_module(value, "Optional") {
let mut check = Check::new(CheckKind::UsePEP604Annotation, Range::from_located(expr));

View File

@@ -6,6 +6,7 @@ use crate::autofix::helpers;
use crate::check_ast::Checker;
use crate::pyupgrade::checks;
/// U001
pub fn useless_metaclass_type(checker: &mut Checker, stmt: &Stmt, value: &Expr, targets: &[Expr]) {
if let Some(mut check) =
checks::useless_metaclass_type(targets, value, Range::from_located(stmt))
@@ -29,7 +30,7 @@ pub fn useless_metaclass_type(checker: &mut Checker, stmt: &Stmt, value: &Expr,
}
check.amend(fix)
}
Err(e) => error!("Failed to fix unused imports: {}", e),
Err(e) => error!("Failed to fix remove metaclass type: {}", e),
}
}
checker.add_check(check);

View File

@@ -4,6 +4,7 @@ use crate::check_ast::Checker;
use crate::pyupgrade;
use crate::pyupgrade::checks;
/// U004
pub fn useless_object_inheritance(
checker: &mut Checker,
stmt: &Stmt,

View File

@@ -0,0 +1,69 @@
---
source: src/linter.rs
expression: checks
---
- kind: CachedInstanceMethod
location:
row: 78
column: 5
end_location:
row: 78
column: 20
fix: ~
- kind: CachedInstanceMethod
location:
row: 82
column: 5
end_location:
row: 82
column: 10
fix: ~
- kind: CachedInstanceMethod
location:
row: 86
column: 5
end_location:
row: 86
column: 22
fix: ~
- kind: CachedInstanceMethod
location:
row: 90
column: 5
end_location:
row: 90
column: 12
fix: ~
- kind: CachedInstanceMethod
location:
row: 94
column: 5
end_location:
row: 94
column: 24
fix: ~
- kind: CachedInstanceMethod
location:
row: 98
column: 5
end_location:
row: 98
column: 14
fix: ~
- kind: CachedInstanceMethod
location:
row: 102
column: 5
end_location:
row: 102
column: 26
fix: ~
- kind: CachedInstanceMethod
location:
row: 106
column: 5
end_location:
row: 106
column: 16
fix: ~

View File

@@ -67,19 +67,19 @@ expression: checks
- - shelve
- false
location:
row: 33
row: 32
column: 4
end_location:
row: 33
row: 32
column: 17
fix:
patch:
content: ""
location:
row: 33
row: 32
column: 0
end_location:
row: 34
row: 33
column: 0
applied: false
- kind:
@@ -87,39 +87,39 @@ expression: checks
- - importlib
- false
location:
row: 34
row: 33
column: 4
end_location:
row: 34
row: 33
column: 20
fix:
patch:
content: ""
content: pass
location:
row: 34
column: 0
row: 33
column: 4
end_location:
row: 35
column: 0
row: 33
column: 20
applied: false
- kind:
UnusedImport:
- - pathlib
- false
location:
row: 38
row: 37
column: 4
end_location:
row: 38
row: 37
column: 18
fix:
patch:
content: ""
location:
row: 38
row: 37
column: 0
end_location:
row: 39
row: 38
column: 0
applied: false
- kind:
@@ -127,19 +127,19 @@ expression: checks
- - pickle
- false
location:
row: 53
row: 52
column: 8
end_location:
row: 53
row: 52
column: 21
fix:
patch:
content: pass
location:
row: 53
row: 52
column: 8
end_location:
row: 53
row: 52
column: 21
applied: false

View File

@@ -3,48 +3,197 @@ source: src/linter.rs
expression: checks
---
- kind:
UnnecessaryFutureImport: nested_scopes
UnnecessaryFutureImport:
- generators
- nested_scopes
location:
row: 1
column: 0
end_location:
row: 1
column: 61
fix: ~
column: 48
fix:
patch:
content: ""
location:
row: 1
column: 0
end_location:
row: 2
column: 0
applied: false
- kind:
UnnecessaryFutureImport: generators
UnnecessaryFutureImport:
- unicode_literals
- with_statement
location:
row: 1
row: 2
column: 0
end_location:
row: 1
column: 61
fix: ~
row: 2
column: 55
fix:
patch:
content: ""
location:
row: 2
column: 0
end_location:
row: 3
column: 0
applied: false
- kind:
UnnecessaryFutureImport: absolute_import
UnnecessaryFutureImport:
- absolute_import
- division
location:
row: 3
column: 0
end_location:
row: 3
column: 48
fix: ~
fix:
patch:
content: ""
location:
row: 3
column: 0
end_location:
row: 4
column: 0
applied: false
- kind:
UnnecessaryFutureImport: division
UnnecessaryFutureImport:
- generator_stop
location:
row: 3
row: 4
column: 0
end_location:
row: 3
column: 48
fix: ~
- kind:
UnnecessaryFutureImport: generator_stop
location:
row: 5
column: 0
end_location:
row: 5
row: 4
column: 37
fix: ~
fix:
patch:
content: ""
location:
row: 4
column: 0
end_location:
row: 5
column: 0
applied: false
- kind:
UnnecessaryFutureImport:
- generator_stop
- print_function
location:
row: 5
column: 0
end_location:
row: 5
column: 53
fix:
patch:
content: ""
location:
row: 5
column: 0
end_location:
row: 6
column: 0
applied: false
- kind:
UnnecessaryFutureImport:
- generators
location:
row: 6
column: 0
end_location:
row: 6
column: 49
fix:
patch:
content: from __future__ import invalid_module
location:
row: 6
column: 0
end_location:
row: 6
column: 49
applied: false
- kind:
UnnecessaryFutureImport:
- generator_stop
location:
row: 9
column: 4
end_location:
row: 9
column: 41
fix:
patch:
content: ""
location:
row: 9
column: 0
end_location:
row: 10
column: 0
applied: false
- kind:
UnnecessaryFutureImport:
- generators
location:
row: 10
column: 4
end_location:
row: 10
column: 37
fix:
patch:
content: pass
location:
row: 10
column: 4
end_location:
row: 10
column: 37
applied: false
- kind:
UnnecessaryFutureImport:
- generator_stop
location:
row: 13
column: 4
end_location:
row: 13
column: 41
fix:
patch:
content: ""
location:
row: 13
column: 0
end_location:
row: 14
column: 0
applied: false
- kind:
UnnecessaryFutureImport:
- generators
location:
row: 14
column: 4
end_location:
row: 14
column: 53
fix:
patch:
content: from __future__ import invalid_module
location:
row: 14
column: 4
end_location:
row: 14
column: 53
applied: false

View File

@@ -0,0 +1,260 @@
---
source: src/linter.rs
expression: checks
---
- kind: UnnecessaryEncodeUTF8
location:
row: 2
column: 0
end_location:
row: 2
column: 21
fix:
patch:
content: "b\"foo\""
location:
row: 2
column: 0
end_location:
row: 2
column: 21
applied: false
- kind: UnnecessaryEncodeUTF8
location:
row: 3
column: 0
end_location:
row: 3
column: 18
fix:
patch:
content: "b\"foo\""
location:
row: 3
column: 0
end_location:
row: 3
column: 18
applied: false
- kind: UnnecessaryEncodeUTF8
location:
row: 4
column: 0
end_location:
row: 4
column: 14
fix:
patch:
content: "b\"foo\""
location:
row: 4
column: 0
end_location:
row: 4
column: 14
applied: false
- kind: UnnecessaryEncodeUTF8
location:
row: 5
column: 0
end_location:
row: 5
column: 20
fix:
patch:
content: "b\"foo\""
location:
row: 5
column: 0
end_location:
row: 5
column: 20
applied: false
- kind: UnnecessaryEncodeUTF8
location:
row: 6
column: 0
end_location:
row: 6
column: 22
fix:
patch:
content: "b\"foo\""
location:
row: 6
column: 0
end_location:
row: 6
column: 22
applied: false
- kind: UnnecessaryEncodeUTF8
location:
row: 7
column: 0
end_location:
row: 7
column: 30
fix:
patch:
content: "b\"foo\""
location:
row: 7
column: 0
end_location:
row: 7
column: 30
applied: false
- kind: UnnecessaryEncodeUTF8
location:
row: 8
column: 0
end_location:
row: 14
column: 1
fix:
patch:
content: "b\"\"\"\nLorem\n\nIpsum\n\"\"\""
location:
row: 8
column: 0
end_location:
row: 14
column: 1
applied: false
- kind: UnnecessaryEncodeUTF8
location:
row: 26
column: 0
end_location:
row: 26
column: 27
fix:
patch:
content: ""
location:
row: 26
column: 19
end_location:
row: 26
column: 26
applied: false
- kind: UnnecessaryEncodeUTF8
location:
row: 44
column: 0
end_location:
row: 44
column: 31
fix:
patch:
content: ""
location:
row: 44
column: 23
end_location:
row: 44
column: 30
applied: false
- kind: UnnecessaryEncodeUTF8
location:
row: 46
column: 0
end_location:
row: 46
column: 39
fix:
patch:
content: ""
location:
row: 46
column: 23
end_location:
row: 46
column: 38
applied: false
- kind: UnnecessaryEncodeUTF8
location:
row: 48
column: 0
end_location:
row: 48
column: 23
fix:
patch:
content: "br\"fo\\o\""
location:
row: 48
column: 0
end_location:
row: 48
column: 23
applied: false
- kind: UnnecessaryEncodeUTF8
location:
row: 49
column: 0
end_location:
row: 49
column: 22
fix:
patch:
content: "b\"foo\""
location:
row: 49
column: 0
end_location:
row: 49
column: 22
applied: false
- kind: UnnecessaryEncodeUTF8
location:
row: 50
column: 0
end_location:
row: 50
column: 23
fix:
patch:
content: "bR\"fo\\o\""
location:
row: 50
column: 0
end_location:
row: 50
column: 23
applied: false
- kind: UnnecessaryEncodeUTF8
location:
row: 51
column: 0
end_location:
row: 51
column: 22
fix:
patch:
content: "b\"foo\""
location:
row: 51
column: 0
end_location:
row: 51
column: 22
applied: false
- kind: UnnecessaryEncodeUTF8
location:
row: 52
column: 6
end_location:
row: 52
column: 20
fix:
patch:
content: "b\"foo\""
location:
row: 52
column: 6
end_location:
row: 52
column: 20
applied: false

View File

@@ -0,0 +1,21 @@
---
source: src/linter.rs
expression: checks
---
- kind: SysVersionSlice3Referenced
location:
row: 6
column: 6
end_location:
row: 6
column: 17
fix: ~
- kind: SysVersionSlice3Referenced
location:
row: 7
column: 6
end_location:
row: 7
column: 13
fix: ~

View File

@@ -0,0 +1,21 @@
---
source: src/linter.rs
expression: checks
---
- kind: SysVersion2Referenced
location:
row: 4
column: 11
end_location:
row: 4
column: 22
fix: ~
- kind: SysVersion2Referenced
location:
row: 5
column: 11
end_location:
row: 5
column: 18
fix: ~

View File

@@ -0,0 +1,45 @@
---
source: src/linter.rs
expression: checks
---
- kind: SysVersionCmpStr3
location:
row: 4
column: 0
end_location:
row: 4
column: 7
fix: ~
- kind: SysVersionCmpStr3
location:
row: 5
column: 0
end_location:
row: 5
column: 11
fix: ~
- kind: SysVersionCmpStr3
location:
row: 6
column: 0
end_location:
row: 6
column: 11
fix: ~
- kind: SysVersionCmpStr3
location:
row: 7
column: 0
end_location:
row: 7
column: 11
fix: ~
- kind: SysVersionCmpStr3
location:
row: 8
column: 0
end_location:
row: 8
column: 11
fix: ~

View File

@@ -0,0 +1,37 @@
---
source: src/linter.rs
expression: checks
---
- kind: SysVersionInfo0Eq3Referenced
location:
row: 7
column: 6
end_location:
row: 7
column: 25
fix: ~
- kind: SysVersionInfo0Eq3Referenced
location:
row: 8
column: 6
end_location:
row: 8
column: 21
fix: ~
- kind: SysVersionInfo0Eq3Referenced
location:
row: 9
column: 6
end_location:
row: 9
column: 25
fix: ~
- kind: SysVersionInfo0Eq3Referenced
location:
row: 10
column: 6
end_location:
row: 10
column: 21
fix: ~

View File

@@ -0,0 +1,21 @@
---
source: src/linter.rs
expression: checks
---
- kind: SixPY3Referenced
location:
row: 4
column: 3
end_location:
row: 4
column: 10
fix: ~
- kind: SixPY3Referenced
location:
row: 6
column: 3
end_location:
row: 6
column: 6
fix: ~

View File

@@ -0,0 +1,21 @@
---
source: src/linter.rs
expression: checks
---
- kind: SysVersionInfo1CmpInt
location:
row: 4
column: 0
end_location:
row: 4
column: 19
fix: ~
- kind: SysVersionInfo1CmpInt
location:
row: 5
column: 0
end_location:
row: 5
column: 15
fix: ~

View File

@@ -0,0 +1,21 @@
---
source: src/linter.rs
expression: checks
---
- kind: SysVersionInfoMinorCmpInt
location:
row: 4
column: 0
end_location:
row: 4
column: 22
fix: ~
- kind: SysVersionInfoMinorCmpInt
location:
row: 5
column: 0
end_location:
row: 5
column: 18
fix: ~

View File

@@ -0,0 +1,21 @@
---
source: src/linter.rs
expression: checks
---
- kind: SysVersion0Referenced
location:
row: 4
column: 11
end_location:
row: 4
column: 22
fix: ~
- kind: SysVersion0Referenced
location:
row: 5
column: 11
end_location:
row: 5
column: 18
fix: ~

View File

@@ -0,0 +1,45 @@
---
source: src/linter.rs
expression: checks
---
- kind: SysVersionCmpStr10
location:
row: 4
column: 0
end_location:
row: 4
column: 7
fix: ~
- kind: SysVersionCmpStr10
location:
row: 5
column: 0
end_location:
row: 5
column: 11
fix: ~
- kind: SysVersionCmpStr10
location:
row: 6
column: 0
end_location:
row: 6
column: 11
fix: ~
- kind: SysVersionCmpStr10
location:
row: 7
column: 0
end_location:
row: 7
column: 11
fix: ~
- kind: SysVersionCmpStr10
location:
row: 8
column: 0
end_location:
row: 8
column: 11
fix: ~

View File

@@ -0,0 +1,21 @@
---
source: src/linter.rs
expression: checks
---
- kind: SysVersionSlice1Referenced
location:
row: 4
column: 6
end_location:
row: 4
column: 17
fix: ~
- kind: SysVersionSlice1Referenced
location:
row: 5
column: 6
end_location:
row: 5
column: 13
fix: ~