diff --git a/README.md b/README.md index 04ab8e307e2fb..afee94227f688 100644 --- a/README.md +++ b/README.md @@ -698,6 +698,7 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI. | UP026 | RewriteMockImport | `mock` is deprecated, use `unittest.mock` | 🛠 | | UP027 | RewriteListComprehension | Replace unpacked list comprehension with a generator expression | 🛠 | | UP028 | RewriteYieldFrom | Replace `yield` over `for` loop with `yield from` | 🛠 | +| UP029 | UnnecessaryBuiltinImport | Unnecessary builtin import: `...` | 🛠 | ### pep8-naming (N) @@ -963,7 +964,7 @@ For more, see [flake8-simplify](https://pypi.org/project/flake8-simplify/0.19.3/ | Code | Name | Message | Fix | | ---- | ---- | ------- | --- | -| SIM105 | UseContextlibSuppress | Use 'contextlib.suppress(..)' instead of try-except-pass | | +| SIM105 | UseContextlibSuppress | Use `contextlib.suppress(...)` instead of try-except-pass | | | SIM118 | KeyInDict | Use `key in dict` instead of `key in dict.keys()` | 🛠 | | SIM220 | AAndNotA | Use `False` instead of `... and not ...` | 🛠 | | SIM221 | AOrNotA | Use `True` instead of `... or not ...` | 🛠 | @@ -1337,11 +1338,11 @@ Under those conditions, Ruff implements every rule in Flake8. Ruff also re-implements some of the most popular Flake8 plugins and related code quality tools natively, including: -- [`autoflake`](https://pypi.org/project/autoflake/) (1/7) +- [`autoflake`](https://pypi.org/project/autoflake/) ([#1647](https://github.com/charliermarsh/ruff/issues/1647)) - [`eradicate`](https://pypi.org/project/eradicate/) - [`flake8-2020`](https://pypi.org/project/flake8-2020/) - [`flake8-annotations`](https://pypi.org/project/flake8-annotations/) -- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) (7/40) +- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) ([#1646](https://github.com/charliermarsh/ruff/issues/1646)) - [`flake8-blind-except`](https://pypi.org/project/flake8-blind-except/) - [`flake8-boolean-trap`](https://pypi.org/project/flake8-boolean-trap/) - [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) @@ -1354,19 +1355,19 @@ natively, including: - [`flake8-errmsg`](https://pypi.org/project/flake8-errmsg/) - [`flake8-implicit-str-concat`](https://pypi.org/project/flake8-implicit-str-concat/) - [`flake8-import-conventions`](https://github.com/joaopalmeiro/flake8-import-conventions) -- [`flake8-pie`](https://pypi.org/project/flake8-pie/) (3/7) +- [`flake8-pie`](https://pypi.org/project/flake8-pie/) ([#1543](https://github.com/charliermarsh/ruff/issues/1543)) - [`flake8-print`](https://pypi.org/project/flake8-print/) - [`flake8-quotes`](https://pypi.org/project/flake8-quotes/) - [`flake8-return`](https://pypi.org/project/flake8-return/) -- [`flake8-simplify`](https://pypi.org/project/flake8-simplify/) (7/30) +- [`flake8-simplify`](https://pypi.org/project/flake8-simplify/) ([#998](https://github.com/charliermarsh/ruff/issues/998)) - [`flake8-super`](https://pypi.org/project/flake8-super/) - [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) - [`isort`](https://pypi.org/project/isort/) - [`mccabe`](https://pypi.org/project/mccabe/) - [`pep8-naming`](https://pypi.org/project/pep8-naming/) - [`pydocstyle`](https://pypi.org/project/pydocstyle/) -- [`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (3/6) -- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (21/33) +- [`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) ([#980](https://github.com/charliermarsh/ruff/issues/980)) +- [`pyupgrade`](https://pypi.org/project/pyupgrade/) ([#827](https://github.com/charliermarsh/ruff/issues/827)) - [`yesqa`](https://github.com/asottile/yesqa) Note that, in some cases, Ruff uses different error code prefixes than would be found in the @@ -1406,7 +1407,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl - [`flake8-2020`](https://pypi.org/project/flake8-2020/) - [`flake8-annotations`](https://pypi.org/project/flake8-annotations/) -- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) (7/40) +- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) ([#1646](https://github.com/charliermarsh/ruff/issues/1646)) - [`flake8-blind-except`](https://pypi.org/project/flake8-blind-except/) - [`flake8-boolean-trap`](https://pypi.org/project/flake8-boolean-trap/) - [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) @@ -1419,11 +1420,11 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl - [`flake8-errmsg`](https://pypi.org/project/flake8-errmsg/) - [`flake8-implicit-str-concat`](https://pypi.org/project/flake8-implicit-str-concat/) - [`flake8-import-conventions`](https://github.com/joaopalmeiro/flake8-import-conventions) -- [`flake8-pie`](https://pypi.org/project/flake8-pie/) (3/7) +- [`flake8-pie`](https://pypi.org/project/flake8-pie/) ([#1543](https://github.com/charliermarsh/ruff/issues/1543)) - [`flake8-print`](https://pypi.org/project/flake8-print/) - [`flake8-quotes`](https://pypi.org/project/flake8-quotes/) - [`flake8-return`](https://pypi.org/project/flake8-return/) -- [`flake8-simplify`](https://pypi.org/project/flake8-simplify/) (7/30) +- [`flake8-simplify`](https://pypi.org/project/flake8-simplify/) ([#998](https://github.com/charliermarsh/ruff/issues/998)) - [`flake8-super`](https://pypi.org/project/flake8-super/) - [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) - [`mccabe`](https://pypi.org/project/mccabe/) @@ -1432,8 +1433,8 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl Ruff can also replace [`isort`](https://pypi.org/project/isort/), [`yesqa`](https://github.com/asottile/yesqa), [`eradicate`](https://pypi.org/project/eradicate/), -[`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (3/6), and a subset of the rules -implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (21/33). +[`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) ([#980](https://github.com/charliermarsh/ruff/issues/980)), and a subset of the rules +implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) ([#827](https://github.com/charliermarsh/ruff/issues/827)). If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, feel free to file an Issue. diff --git a/resources/test/fixtures/pyupgrade/UP029.py b/resources/test/fixtures/pyupgrade/UP029.py new file mode 100644 index 0000000000000..305507be4da20 --- /dev/null +++ b/resources/test/fixtures/pyupgrade/UP029.py @@ -0,0 +1,9 @@ +from builtins import * +from builtins import ascii, bytes, compile +from builtins import str as _str +from six.moves import filter, zip, zip_longest +from io import open +import io +import six +import six.moves +import builtins diff --git a/ruff.schema.json b/ruff.schema.json index 45bb4f0567895..8f82f95db52f9 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -996,6 +996,7 @@ "UP026", "UP027", "UP028", + "UP029", "W", "W2", "W29", diff --git a/src/autofix/helpers.rs b/src/autofix/helpers.rs index d187d5527c892..03c66f0ab7c32 100644 --- a/src/autofix/helpers.rs +++ b/src/autofix/helpers.rs @@ -211,11 +211,33 @@ pub fn remove_unused_imports<'a>( Some(SmallStatement::ImportFrom(import_body)) => { if let ImportNames::Aliases(names) = &mut import_body.names { (names, import_body.module.as_ref()) + } else if let ImportNames::Star(..) = &import_body.names { + // Special-case: if the import is a `from ... import *`, then we delete the + // entire statement. + let mut found_star = false; + for unused_import in unused_imports { + let full_name = match import_body.module.as_ref() { + Some(module_name) => format!("{}.*", compose_module_path(module_name),), + None => "*".to_string(), + }; + if unused_import == full_name { + found_star = true; + } else { + bail!( + "Expected \"*\" for unused import (got: \"{}\")", + unused_import + ); + } + } + if !found_star { + bail!("Expected \'*\' for unused import"); + } + return delete_stmt(stmt, parent, deleted, locator); } else { - bail!("Expected ImportNames::Aliases") + bail!("Expected: ImportNames::Aliases | ImportNames::Star"); } } - _ => bail!("Expected SmallStatement::ImportFrom or SmallStatement::Import"), + _ => bail!("Expected: SmallStatement::ImportFrom | SmallStatement::Import"), }; // Preserve the trailing comma (or not) from the last entry. diff --git a/src/checkers/ast.rs b/src/checkers/ast.rs index 563acf70c6f15..71f7a1d033ca5 100644 --- a/src/checkers/ast.rs +++ b/src/checkers/ast.rs @@ -890,14 +890,19 @@ where } } - if let Some("__future__") = module.as_deref() { - if self.settings.enabled.contains(&CheckCode::UP010) { + if self.settings.enabled.contains(&CheckCode::UP010) { + if let Some("__future__") = module.as_deref() { pyupgrade::plugins::unnecessary_future_import(self, stmt, names); } } if self.settings.enabled.contains(&CheckCode::UP026) { pyupgrade::plugins::rewrite_mock_import(self, stmt); } + if self.settings.enabled.contains(&CheckCode::UP029) { + if let Some(module) = module.as_deref() { + pyupgrade::plugins::unnecessary_builtin_import(self, stmt, module, names); + } + } if self.settings.enabled.contains(&CheckCode::TID251) { if let Some(module) = module { diff --git a/src/pyupgrade/mod.rs b/src/pyupgrade/mod.rs index 308f3374e71d5..17d6a422a0f98 100644 --- a/src/pyupgrade/mod.rs +++ b/src/pyupgrade/mod.rs @@ -50,6 +50,7 @@ mod tests { #[test_case(CheckCode::UP027, Path::new("UP027.py"); "UP027")] #[test_case(CheckCode::UP028, Path::new("UP028_0.py"); "UP028_0")] #[test_case(CheckCode::UP028, Path::new("UP028_1.py"); "UP028_1")] + #[test_case(CheckCode::UP029, Path::new("UP029.py"); "UP029")] fn checks(check_code: CheckCode, path: &Path) -> Result<()> { let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy()); let checks = test_path( diff --git a/src/pyupgrade/plugins/mod.rs b/src/pyupgrade/plugins/mod.rs index a5c0fc4dc0955..50e20ef54ecfe 100644 --- a/src/pyupgrade/plugins/mod.rs +++ b/src/pyupgrade/plugins/mod.rs @@ -16,6 +16,7 @@ pub use rewrite_yield_from::rewrite_yield_from; pub use super_call_with_parameters::super_call_with_parameters; pub use type_of_primitive::type_of_primitive; pub use typing_text_str_alias::typing_text_str_alias; +pub use unnecessary_builtin_import::unnecessary_builtin_import; 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; @@ -43,6 +44,7 @@ mod rewrite_yield_from; mod super_call_with_parameters; mod type_of_primitive; mod typing_text_str_alias; +mod unnecessary_builtin_import; mod unnecessary_encode_utf8; mod unnecessary_future_import; mod unnecessary_lru_cache_params; diff --git a/src/pyupgrade/plugins/unnecessary_builtin_import.rs b/src/pyupgrade/plugins/unnecessary_builtin_import.rs new file mode 100644 index 0000000000000..cad110891d919 --- /dev/null +++ b/src/pyupgrade/plugins/unnecessary_builtin_import.rs @@ -0,0 +1,107 @@ +use itertools::Itertools; +use log::error; +use rustpython_ast::{Alias, AliasData, Located}; +use rustpython_parser::ast::Stmt; + +use crate::ast::types::Range; +use crate::autofix; +use crate::checkers::ast::Checker; +use crate::registry::{Check, CheckKind}; + +const BUILTINS: &[&str] = &[ + "*", + "ascii", + "bytes", + "chr", + "dict", + "filter", + "hex", + "input", + "int", + "isinstance", + "list", + "map", + "max", + "min", + "next", + "object", + "oct", + "open", + "pow", + "range", + "round", + "str", + "super", + "zip", +]; +const IO: &[&str] = &["open"]; +const SIX_MOVES_BUILTINS: &[&str] = BUILTINS; +const SIX: &[&str] = &["callable", "next"]; +const SIX_MOVES: &[&str] = &["filter", "input", "map", "range", "zip"]; + +/// UP029 +pub fn unnecessary_builtin_import( + checker: &mut Checker, + stmt: &Stmt, + module: &str, + names: &[Located], +) { + let deprecated_names = match module { + "builtins" => BUILTINS, + "io" => IO, + "six" => SIX, + "six.moves" => SIX_MOVES, + "six.moves.builtins" => SIX_MOVES_BUILTINS, + _ => return, + }; + + let mut unused_imports: Vec<&Alias> = vec![]; + for alias in names { + if alias.node.asname.is_some() { + continue; + } + if deprecated_names.contains(&alias.node.name.as_str()) { + unused_imports.push(alias); + } + } + + if unused_imports.is_empty() { + return; + } + let mut check = Check::new( + CheckKind::UnnecessaryBuiltinImport( + unused_imports + .iter() + .map(|alias| alias.node.name.to_string()) + .sorted() + .collect(), + ), + Range::from_located(stmt), + ); + + if checker.patch(check.kind.code()) { + let deleted: Vec<&Stmt> = checker.deletions.iter().map(|node| node.0).collect(); + let defined_by = checker.current_stmt(); + let defined_in = checker.current_stmt_parent(); + let unused_imports: Vec = unused_imports + .iter() + .map(|alias| format!("{module}.{}", alias.node.name)) + .collect(); + match autofix::helpers::remove_unused_imports( + unused_imports.iter().map(String::as_str), + defined_by.0, + defined_in.map(|node| node.0), + &deleted, + checker.locator, + ) { + Ok(fix) => { + if fix.content.is_empty() || fix.content == "pass" { + checker.deletions.insert(defined_by.clone()); + } + check.amend(fix); + } + Err(e) => error!("Failed to remove builtin import: {e}"), + } + } + checker.add_check(check); +} diff --git a/src/pyupgrade/plugins/unnecessary_future_import.rs b/src/pyupgrade/plugins/unnecessary_future_import.rs index f1d2fa69f85c7..980aab681af82 100644 --- a/src/pyupgrade/plugins/unnecessary_future_import.rs +++ b/src/pyupgrade/plugins/unnecessary_future_import.rs @@ -38,6 +38,9 @@ pub fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, names: &[Lo let mut unused_imports: Vec<&Alias> = vec![]; for alias in names { + if alias.node.asname.is_some() { + continue; + } if (target_version >= PythonVersion::Py33 && PY33_PLUS_REMOVE_FUTURES.contains(&alias.node.name.as_str())) || (target_version >= PythonVersion::Py37 diff --git a/src/pyupgrade/snapshots/ruff__pyupgrade__tests__UP029_UP029.py.snap b/src/pyupgrade/snapshots/ruff__pyupgrade__tests__UP029_UP029.py.snap new file mode 100644 index 0000000000000..cc213cec1345e --- /dev/null +++ b/src/pyupgrade/snapshots/ruff__pyupgrade__tests__UP029_UP029.py.snap @@ -0,0 +1,79 @@ +--- +source: src/pyupgrade/mod.rs +expression: checks +--- +- kind: + UnnecessaryBuiltinImport: + - "*" + location: + row: 1 + column: 0 + end_location: + row: 1 + column: 22 + fix: + content: "" + location: + row: 1 + column: 0 + end_location: + row: 2 + column: 0 + parent: ~ +- kind: + UnnecessaryBuiltinImport: + - ascii + - bytes + location: + row: 2 + column: 0 + end_location: + row: 2 + column: 42 + fix: + content: from builtins import compile + location: + row: 2 + column: 0 + end_location: + row: 2 + column: 42 + parent: ~ +- kind: + UnnecessaryBuiltinImport: + - filter + - zip + location: + row: 4 + column: 0 + end_location: + row: 4 + column: 46 + fix: + content: from six.moves import zip_longest + location: + row: 4 + column: 0 + end_location: + row: 4 + column: 46 + parent: ~ +- kind: + UnnecessaryBuiltinImport: + - open + location: + row: 5 + column: 0 + end_location: + row: 5 + column: 19 + fix: + content: "" + location: + row: 5 + column: 0 + end_location: + row: 6 + column: 0 + parent: ~ + diff --git a/src/registry.rs b/src/registry.rs index e2b54121fd66f..4d1b489fb9919 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -252,6 +252,7 @@ pub enum CheckCode { UP026, UP027, UP028, + UP029, // pydocstyle D100, D101, @@ -957,33 +958,34 @@ pub enum CheckKind { OrTrue, YodaConditions(String, String), // pyupgrade - TypeOfPrimitive(Primitive), - UselessMetaclassType, - TypingTextStrAlias, - DeprecatedUnittestAlias(String, String), - UselessObjectInheritance(String), - UsePEP585Annotation(String), - UsePEP604Annotation, - SuperCallWithParameters, - PEP3120UnnecessaryCodingComment, - UnnecessaryFutureImport(Vec), - UnnecessaryLRUCacheParams, - UnnecessaryEncodeUTF8, - ConvertTypedDictFunctionalToClass(String), ConvertNamedTupleFunctionalToClass(String), - RedundantOpenModes(Option), - RemoveSixCompat, + ConvertTypedDictFunctionalToClass(String), DatetimeTimezoneUTC, + DeprecatedUnittestAlias(String, String), NativeLiterals(LiteralType), + OSErrorAlias(Option), OpenAlias, - ReplaceUniversalNewlines, + PEP3120UnnecessaryCodingComment, + RedundantOpenModes(Option), + RemoveSixCompat, ReplaceStdoutStderr, + ReplaceUniversalNewlines, RewriteCElementTree, - OSErrorAlias(Option), - RewriteUnicodeLiteral, - RewriteMockImport(MockReference), RewriteListComprehension, + RewriteMockImport(MockReference), + RewriteUnicodeLiteral, RewriteYieldFrom, + SuperCallWithParameters, + TypeOfPrimitive(Primitive), + TypingTextStrAlias, + UnnecessaryBuiltinImport(Vec), + UnnecessaryEncodeUTF8, + UnnecessaryFutureImport(Vec), + UnnecessaryLRUCacheParams, + UsePEP585Annotation(String), + UsePEP604Annotation, + UselessMetaclassType, + UselessObjectInheritance(String), // pydocstyle BlankLineAfterLastSection(String), BlankLineAfterSection(String), @@ -1381,7 +1383,7 @@ impl CheckCode { // flake8-blind-except CheckCode::BLE001 => CheckKind::BlindExcept("Exception".to_string()), // flake8-simplify - CheckCode::SIM105 => CheckKind::UseContextlibSuppress("..".to_string()), + CheckCode::SIM105 => CheckKind::UseContextlibSuppress("...".to_string()), CheckCode::SIM118 => CheckKind::KeyInDict("key".to_string(), "dict".to_string()), CheckCode::SIM220 => CheckKind::AAndNotA("...".to_string()), CheckCode::SIM221 => CheckKind::AOrNotA("...".to_string()), @@ -1419,6 +1421,7 @@ impl CheckCode { CheckCode::UP026 => CheckKind::RewriteMockImport(MockReference::Import), CheckCode::UP027 => CheckKind::RewriteListComprehension, CheckCode::UP028 => CheckKind::RewriteYieldFrom, + CheckCode::UP029 => CheckKind::UnnecessaryBuiltinImport(vec!["...".to_string()]), // pydocstyle CheckCode::D100 => CheckKind::PublicModule, CheckCode::D101 => CheckKind::PublicClass, @@ -1953,6 +1956,7 @@ impl CheckCode { CheckCode::UP026 => CheckCategory::Pyupgrade, CheckCode::UP027 => CheckCategory::Pyupgrade, CheckCode::UP028 => CheckCategory::Pyupgrade, + CheckCode::UP029 => CheckCategory::Pyupgrade, // pycodestyle (warnings) CheckCode::W292 => CheckCategory::Pycodestyle, CheckCode::W605 => CheckCategory::Pycodestyle, @@ -2170,33 +2174,34 @@ impl CheckKind { CheckKind::AndFalse => &CheckCode::SIM223, CheckKind::YodaConditions(..) => &CheckCode::SIM300, // pyupgrade - CheckKind::TypeOfPrimitive(..) => &CheckCode::UP003, - CheckKind::UselessMetaclassType => &CheckCode::UP001, - CheckKind::DeprecatedUnittestAlias(..) => &CheckCode::UP005, - CheckKind::UsePEP585Annotation(..) => &CheckCode::UP006, - CheckKind::UsePEP604Annotation => &CheckCode::UP007, - CheckKind::UselessObjectInheritance(..) => &CheckCode::UP004, - CheckKind::SuperCallWithParameters => &CheckCode::UP008, - CheckKind::PEP3120UnnecessaryCodingComment => &CheckCode::UP009, - CheckKind::UnnecessaryFutureImport(..) => &CheckCode::UP010, - CheckKind::UnnecessaryLRUCacheParams => &CheckCode::UP011, - CheckKind::UnnecessaryEncodeUTF8 => &CheckCode::UP012, - CheckKind::ConvertTypedDictFunctionalToClass(..) => &CheckCode::UP013, CheckKind::ConvertNamedTupleFunctionalToClass(..) => &CheckCode::UP014, - CheckKind::RedundantOpenModes(..) => &CheckCode::UP015, - CheckKind::RemoveSixCompat => &CheckCode::UP016, + CheckKind::ConvertTypedDictFunctionalToClass(..) => &CheckCode::UP013, CheckKind::DatetimeTimezoneUTC => &CheckCode::UP017, + CheckKind::DeprecatedUnittestAlias(..) => &CheckCode::UP005, CheckKind::NativeLiterals(..) => &CheckCode::UP018, - CheckKind::TypingTextStrAlias => &CheckCode::UP019, + CheckKind::OSErrorAlias(..) => &CheckCode::UP024, CheckKind::OpenAlias => &CheckCode::UP020, - CheckKind::ReplaceUniversalNewlines => &CheckCode::UP021, + CheckKind::PEP3120UnnecessaryCodingComment => &CheckCode::UP009, + CheckKind::RedundantOpenModes(..) => &CheckCode::UP015, + CheckKind::RemoveSixCompat => &CheckCode::UP016, CheckKind::ReplaceStdoutStderr => &CheckCode::UP022, + CheckKind::ReplaceUniversalNewlines => &CheckCode::UP021, CheckKind::RewriteCElementTree => &CheckCode::UP023, - CheckKind::OSErrorAlias(..) => &CheckCode::UP024, - CheckKind::RewriteUnicodeLiteral => &CheckCode::UP025, - CheckKind::RewriteMockImport(..) => &CheckCode::UP026, CheckKind::RewriteListComprehension => &CheckCode::UP027, + CheckKind::RewriteMockImport(..) => &CheckCode::UP026, + CheckKind::RewriteUnicodeLiteral => &CheckCode::UP025, CheckKind::RewriteYieldFrom => &CheckCode::UP028, + CheckKind::SuperCallWithParameters => &CheckCode::UP008, + CheckKind::TypeOfPrimitive(..) => &CheckCode::UP003, + CheckKind::TypingTextStrAlias => &CheckCode::UP019, + CheckKind::UnnecessaryBuiltinImport(..) => &CheckCode::UP029, + CheckKind::UnnecessaryEncodeUTF8 => &CheckCode::UP012, + CheckKind::UnnecessaryFutureImport(..) => &CheckCode::UP010, + CheckKind::UnnecessaryLRUCacheParams => &CheckCode::UP011, + CheckKind::UsePEP585Annotation(..) => &CheckCode::UP006, + CheckKind::UsePEP604Annotation => &CheckCode::UP007, + CheckKind::UselessMetaclassType => &CheckCode::UP001, + CheckKind::UselessObjectInheritance(..) => &CheckCode::UP004, // pydocstyle CheckKind::BlankLineAfterLastSection(..) => &CheckCode::D413, CheckKind::BlankLineAfterSection(..) => &CheckCode::D410, @@ -2678,7 +2683,7 @@ impl CheckKind { by python as a joined string rather than a docstring." .to_string(), CheckKind::UseContextlibSuppress(exception) => { - format!("Use 'contextlib.suppress({exception})' instead of try-except-pass") + format!("Use `contextlib.suppress({exception})` instead of try-except-pass") } CheckKind::UselessContextlibSuppress => { "No arguments passed to `contextlib.suppress`. No exceptions will be suppressed \ @@ -2966,6 +2971,15 @@ impl CheckKind { format!("Unnecessary `__future__` imports {imports} for target Python version") } } + CheckKind::UnnecessaryBuiltinImport(names) => { + if names.len() == 1 { + let import = &names[0]; + format!("Unnecessary builtin import: `{import}`") + } else { + let imports = names.iter().map(|name| format!("`{name}`")).join(", "); + format!("Unnecessary builtin imports: {imports}") + } + } CheckKind::UnnecessaryLRUCacheParams => { "Unnecessary parameters to `functools.lru_cache`".to_string() } @@ -3605,6 +3619,7 @@ impl CheckKind { | CheckKind::TrueFalseComparison(..) | CheckKind::TypeOfPrimitive(..) | CheckKind::TypingTextStrAlias + | CheckKind::UnnecessaryBuiltinImport(..) | CheckKind::UnnecessaryCallAroundSorted(..) | CheckKind::UnnecessaryCollectionCall(..) | CheckKind::UnnecessaryComprehension(..) @@ -3826,6 +3841,9 @@ impl CheckKind { primitive.builtin() )), CheckKind::TypingTextStrAlias => Some("Replace with `str`".to_string()), + CheckKind::UnnecessaryBuiltinImport(..) => { + Some("Remove unnecessary builtin import".to_string()) + } CheckKind::UnnecessaryCallAroundSorted(func) => { Some(format!("Remove unnecessary `{func}` call")) } diff --git a/src/registry_gen.rs b/src/registry_gen.rs index df72ca607a546..34f113ce9ee1d 100644 --- a/src/registry_gen.rs +++ b/src/registry_gen.rs @@ -595,6 +595,7 @@ pub enum CheckCodePrefix { UP026, UP027, UP028, + UP029, W, W2, W29, @@ -838,6 +839,7 @@ impl CheckCodePrefix { CheckCode::UP026, CheckCode::UP027, CheckCode::UP028, + CheckCode::UP029, CheckCode::D100, CheckCode::D101, CheckCode::D102, @@ -2701,6 +2703,7 @@ impl CheckCodePrefix { CheckCode::UP026, CheckCode::UP027, CheckCode::UP028, + CheckCode::UP029, ] } CheckCodePrefix::U0 => { @@ -2738,6 +2741,7 @@ impl CheckCodePrefix { CheckCode::UP026, CheckCode::UP027, CheckCode::UP028, + CheckCode::UP029, ] } CheckCodePrefix::U00 => { @@ -2959,6 +2963,7 @@ impl CheckCodePrefix { CheckCode::UP026, CheckCode::UP027, CheckCode::UP028, + CheckCode::UP029, ], CheckCodePrefix::UP0 => vec![ CheckCode::UP001, @@ -2988,6 +2993,7 @@ impl CheckCodePrefix { CheckCode::UP026, CheckCode::UP027, CheckCode::UP028, + CheckCode::UP029, ], CheckCodePrefix::UP00 => vec![ CheckCode::UP001, @@ -3039,6 +3045,7 @@ impl CheckCodePrefix { CheckCode::UP026, CheckCode::UP027, CheckCode::UP028, + CheckCode::UP029, ], CheckCodePrefix::UP020 => vec![CheckCode::UP020], CheckCodePrefix::UP021 => vec![CheckCode::UP021], @@ -3049,6 +3056,7 @@ impl CheckCodePrefix { CheckCodePrefix::UP026 => vec![CheckCode::UP026], CheckCodePrefix::UP027 => vec![CheckCode::UP027], CheckCodePrefix::UP028 => vec![CheckCode::UP028], + CheckCodePrefix::UP029 => vec![CheckCode::UP029], CheckCodePrefix::W => vec![CheckCode::W292, CheckCode::W605], CheckCodePrefix::W2 => vec![CheckCode::W292], CheckCodePrefix::W29 => vec![CheckCode::W292], @@ -3675,6 +3683,7 @@ impl CheckCodePrefix { CheckCodePrefix::UP026 => SuffixLength::Three, CheckCodePrefix::UP027 => SuffixLength::Three, CheckCodePrefix::UP028 => SuffixLength::Three, + CheckCodePrefix::UP029 => SuffixLength::Three, CheckCodePrefix::W => SuffixLength::Zero, CheckCodePrefix::W2 => SuffixLength::One, CheckCodePrefix::W29 => SuffixLength::Two,