Implement flake8-2020 (sys.version, sys.version_info misuse) (#688)

This commit is contained in:
Anders Kaseorg 2022-11-11 17:39:37 -08:00 committed by GitHub
parent f8932ec12b
commit 5a8b7c1d20
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 787 additions and 28 deletions

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) 9. [flake8-print](#flake8-print)
10. [flake8-quotes](#flake8-quotes) 10. [flake8-quotes](#flake8-quotes)
11. [flake8-annotations](#flake8-annotations) 11. [flake8-annotations](#flake8-annotations)
12. [Ruff-specific rules](#ruff-specific-rules) 12. [flake8-2020](#flake8-2020)
13. [Meta rules](#meta-rules) 13. [Ruff-specific rules](#ruff-specific-rules)
14. [Meta rules](#meta-rules)
5. [Editor Integrations](#editor-integrations) 5. [Editor Integrations](#editor-integrations)
6. [FAQ](#faq) 6. [FAQ](#faq)
7. [Development](#development) 7. [Development](#development)
@ -561,6 +562,23 @@ For more, see [flake8-annotations](https://pypi.org/project/flake8-annotations/2
| ANN206 | MissingReturnTypeClassMethod | Missing return type annotation for classmethod `...` | | | ANN206 | MissingReturnTypeClassMethod | Missing return type annotation for classmethod `...` | |
| ANN401 | DynamicallyTypedExpression | Dynamically typed expressions (typing.Any) are disallowed in `...` | | | 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 ### Ruff-specific rules
| Code | Name | Message | Fix | | Code | Name | Message | Fix |
@ -667,6 +685,7 @@ including:
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/) - [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/) - [`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/) (20/32)
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (14/34) - [`pyupgrade`](https://pypi.org/project/pyupgrade/) (14/34)
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7) - [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
@ -690,6 +709,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/) - [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/) - [`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/) (20/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), 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/) (14/34).

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

@ -32,17 +32,19 @@ use crate::settings::Settings;
use crate::source_code_locator::SourceCodeLocator; use crate::source_code_locator::SourceCodeLocator;
use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope}; use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope};
use crate::{ use crate::{
docstrings, flake8_annotations, flake8_bugbear, flake8_builtins, flake8_comprehensions, docstrings, flake8_2020, flake8_annotations, flake8_bugbear, flake8_builtins,
flake8_print, pep8_naming, pycodestyle, pydocstyle, pyflakes, pyupgrade, flake8_comprehensions, flake8_print, pep8_naming, pycodestyle, pydocstyle, pyflakes, pyupgrade,
}; };
const GLOBAL_SCOPE_INDEX: usize = 0; const GLOBAL_SCOPE_INDEX: usize = 0;
const TRACK_FROM_IMPORTS: [&str; 10] = [ const TRACK_FROM_IMPORTS: [&str; 12] = [
"collections", "collections",
"collections.abc", "collections.abc",
"contextlib", "contextlib",
"functools", "functools",
"re", "re",
"six",
"sys",
"typing", "typing",
"typing.io", "typing.io",
"typing.re", "typing.re",
@ -984,6 +986,14 @@ where
if self.match_typing_module(value, "Literal") { if self.match_typing_module(value, "Literal") {
self.in_literal = true; 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 } => { ExprKind::Tuple { elts, ctx } | ExprKind::List { elts, ctx } => {
if matches!(ctx, ExprContext::Store) { if matches!(ctx, ExprContext::Store) {
@ -1001,7 +1011,8 @@ where
} }
} }
} }
ExprKind::Name { id, ctx } => match ctx { ExprKind::Name { id, ctx } => {
match ctx {
ExprContext::Load => { ExprContext::Load => {
// Ex) List[...] // Ex) List[...]
if self.settings.enabled.contains(&CheckCode::U006) if self.settings.enabled.contains(&CheckCode::U006)
@ -1028,7 +1039,12 @@ where
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, .. } => { ExprKind::Attribute { attr, .. } => {
// Ex) typing.List[...] // Ex) typing.List[...]
if self.settings.enabled.contains(&CheckCode::U006) if self.settings.enabled.contains(&CheckCode::U006)
@ -1037,6 +1053,10 @@ where
{ {
pyupgrade::plugins::use_pep585_annotation(self, expr, attr); 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 { ExprKind::Call {
func, func,
@ -1422,6 +1442,15 @@ where
.into_iter(), .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 { ExprKind::Constant {
value: Constant::Str(value), value: Constant::Str(value),

View file

@ -132,6 +132,17 @@ pub enum CheckCode {
ANN205, ANN205,
ANN206, ANN206,
ANN401, ANN401,
// flake8-2020
YTT101,
YTT102,
YTT103,
YTT201,
YTT202,
YTT203,
YTT204,
YTT301,
YTT302,
YTT303,
// pyupgrade // pyupgrade
U001, U001,
U002, U002,
@ -229,6 +240,7 @@ pub enum CheckCategory {
Flake8Print, Flake8Print,
Flake8Quotes, Flake8Quotes,
Flake8Annotations, Flake8Annotations,
Flake82020,
Ruff, Ruff,
Meta, Meta,
} }
@ -245,6 +257,7 @@ impl CheckCategory {
CheckCategory::Flake8Print => "flake8-print", CheckCategory::Flake8Print => "flake8-print",
CheckCategory::Flake8Quotes => "flake8-quotes", CheckCategory::Flake8Quotes => "flake8-quotes",
CheckCategory::Flake8Annotations => "flake8-annotations", CheckCategory::Flake8Annotations => "flake8-annotations",
CheckCategory::Flake82020 => "flake8-2020",
CheckCategory::Pyupgrade => "pyupgrade", CheckCategory::Pyupgrade => "pyupgrade",
CheckCategory::Pydocstyle => "pydocstyle", CheckCategory::Pydocstyle => "pydocstyle",
CheckCategory::PEP8Naming => "pep8-naming", CheckCategory::PEP8Naming => "pep8-naming",
@ -272,6 +285,7 @@ impl CheckCategory {
CheckCategory::Flake8Annotations => { CheckCategory::Flake8Annotations => {
Some("https://pypi.org/project/flake8-annotations/2.9.1/") 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::Pyupgrade => Some("https://pypi.org/project/pyupgrade/3.2.0/"),
CheckCategory::Pydocstyle => Some("https://pypi.org/project/pydocstyle/6.1.1/"), CheckCategory::Pydocstyle => Some("https://pypi.org/project/pydocstyle/6.1.1/"),
CheckCategory::PEP8Naming => Some("https://pypi.org/project/pep8-naming/0.13.2/"), CheckCategory::PEP8Naming => Some("https://pypi.org/project/pep8-naming/0.13.2/"),
@ -405,6 +419,17 @@ pub enum CheckKind {
MissingReturnTypeStaticMethod(String), MissingReturnTypeStaticMethod(String),
MissingReturnTypeClassMethod(String), MissingReturnTypeClassMethod(String),
DynamicallyTypedExpression(String), DynamicallyTypedExpression(String),
// flake8-2020
SysVersionSlice3Referenced,
SysVersion2Referenced,
SysVersionCmpStr3,
SysVersionInfo0Eq3Referenced,
SixPY3Referenced,
SysVersionInfo1CmpInt,
SysVersionInfoMinorCmpInt,
SysVersion0Referenced,
SysVersionCmpStr10,
SysVersionSlice1Referenced,
// pyupgrade // pyupgrade
TypeOfPrimitive(Primitive), TypeOfPrimitive(Primitive),
UnnecessaryAbspath, UnnecessaryAbspath,
@ -637,6 +662,17 @@ impl CheckCode {
CheckCode::ANN205 => CheckKind::MissingReturnTypeStaticMethod("...".to_string()), CheckCode::ANN205 => CheckKind::MissingReturnTypeStaticMethod("...".to_string()),
CheckCode::ANN206 => CheckKind::MissingReturnTypeClassMethod("...".to_string()), CheckCode::ANN206 => CheckKind::MissingReturnTypeClassMethod("...".to_string()),
CheckCode::ANN401 => CheckKind::DynamicallyTypedExpression("...".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 // pyupgrade
CheckCode::U001 => CheckKind::UselessMetaclassType, CheckCode::U001 => CheckKind::UselessMetaclassType,
CheckCode::U002 => CheckKind::UnnecessaryAbspath, CheckCode::U002 => CheckKind::UnnecessaryAbspath,
@ -840,6 +876,16 @@ impl CheckCode {
CheckCode::ANN205 => CheckCategory::Flake8Annotations, CheckCode::ANN205 => CheckCategory::Flake8Annotations,
CheckCode::ANN206 => CheckCategory::Flake8Annotations, CheckCode::ANN206 => CheckCategory::Flake8Annotations,
CheckCode::ANN401 => 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::U001 => CheckCategory::Pyupgrade,
CheckCode::U002 => CheckCategory::Pyupgrade, CheckCode::U002 => CheckCategory::Pyupgrade,
CheckCode::U003 => CheckCategory::Pyupgrade, CheckCode::U003 => CheckCategory::Pyupgrade,
@ -1029,6 +1075,17 @@ impl CheckKind {
CheckKind::MissingReturnTypeStaticMethod(_) => &CheckCode::ANN205, CheckKind::MissingReturnTypeStaticMethod(_) => &CheckCode::ANN205,
CheckKind::MissingReturnTypeClassMethod(_) => &CheckCode::ANN206, CheckKind::MissingReturnTypeClassMethod(_) => &CheckCode::ANN206,
CheckKind::DynamicallyTypedExpression(_) => &CheckCode::ANN401, 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 // pyupgrade
CheckKind::TypeOfPrimitive(_) => &CheckCode::U003, CheckKind::TypeOfPrimitive(_) => &CheckCode::U003,
CheckKind::UnnecessaryAbspath => &CheckCode::U002, CheckKind::UnnecessaryAbspath => &CheckCode::U002,
@ -1473,6 +1530,38 @@ impl CheckKind {
CheckKind::DynamicallyTypedExpression(name) => { CheckKind::DynamicallyTypedExpression(name) => {
format!("Dynamically typed expressions (typing.Any) are disallowed in `{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 // pyupgrade
CheckKind::TypeOfPrimitive(primitive) => { CheckKind::TypeOfPrimitive(primitive) => {
format!("Use `{}` instead of `type(...)`", primitive.builtin()) format!("Use `{}` instead of `type(...)`", primitive.builtin())

View file

@ -271,6 +271,23 @@ pub enum CheckCodePrefix {
W6, W6,
W60, W60,
W605, W605,
YTT,
YTT1,
YTT10,
YTT101,
YTT102,
YTT103,
YTT2,
YTT20,
YTT201,
YTT202,
YTT203,
YTT204,
YTT3,
YTT30,
YTT301,
YTT302,
YTT303,
} }
#[derive(PartialEq, Eq, PartialOrd, Ord)] #[derive(PartialEq, Eq, PartialOrd, Ord)]
@ -1026,6 +1043,44 @@ impl CheckCodePrefix {
CheckCodePrefix::W6 => vec![CheckCode::W605], CheckCodePrefix::W6 => vec![CheckCode::W605],
CheckCodePrefix::W60 => vec![CheckCode::W605], CheckCodePrefix::W60 => vec![CheckCode::W605],
CheckCodePrefix::W605 => 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],
} }
} }
} }
@ -1297,6 +1352,23 @@ impl CheckCodePrefix {
CheckCodePrefix::W6 => PrefixSpecificity::Hundreds, CheckCodePrefix::W6 => PrefixSpecificity::Hundreds,
CheckCodePrefix::W60 => PrefixSpecificity::Tens, CheckCodePrefix::W60 => PrefixSpecificity::Tens,
CheckCodePrefix::W605 => PrefixSpecificity::Explicit, 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

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

View file

@ -493,6 +493,16 @@ mod tests {
#[test_case(CheckCode::RUF001, Path::new("RUF001.py"); "RUF001")] #[test_case(CheckCode::RUF001, Path::new("RUF001.py"); "RUF001")]
#[test_case(CheckCode::RUF002, Path::new("RUF002.py"); "RUF002")] #[test_case(CheckCode::RUF002, Path::new("RUF002.py"); "RUF002")]
#[test_case(CheckCode::RUF003, Path::new("RUF003.py"); "RUF003")] #[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<()> { fn checks(check_code: CheckCode, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy()); let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy());
let mut checks = test_path( let mut checks = test_path(

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: ~