From a6c1071a7a15bd5bf8f58c10da42f95cbdbab97d Mon Sep 17 00:00:00 2001 From: Denis Gavrilyuk Date: Sat, 26 Nov 2022 12:58:13 +0300 Subject: [PATCH] Minor changes --- LICENSE | 25 ++++++ README.md | 9 ++ flake8_to_ruff/src/plugin.rs | 9 +- resources/test/fixtures/T100.py | 8 ++ src/check_ast.rs | 34 +++++++- src/checks.rs | 20 +++++ src/checks_gen.rs | 11 ++- src/flake8_debugger/checks.rs | 84 +++++++++++++++++++ src/flake8_debugger/mod.rs | 2 + src/flake8_debugger/types.rs | 9 ++ src/lib.rs | 1 + src/linter.rs | 1 + .../ruff__linter__tests__T100_T100.py.snap | 71 ++++++++++++++++ 13 files changed, 278 insertions(+), 6 deletions(-) create mode 100644 resources/test/fixtures/T100.py create mode 100644 src/flake8_debugger/checks.rs create mode 100644 src/flake8_debugger/mod.rs create mode 100644 src/flake8_debugger/types.rs create mode 100644 src/snapshots/ruff__linter__tests__T100_T100.py.snap diff --git a/LICENSE b/LICENSE index 41a40ef3cb964d..a4b8c442ea61dd 100644 --- a/LICENSE +++ b/LICENSE @@ -242,6 +242,31 @@ are: SOFTWARE. """ +- flake8-debugger, licensed as follows: + """ + MIT License + + Copyright (c) 2016 Joseph Kahn + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + """ + - flake8-tidy-imports, licensed as follows: """ MIT License diff --git a/README.md b/README.md index 1d244fd0ca25bd..135e82aeff4fd2 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-mu 1. [flake8-boolean-trap (FBT)](#flake8-boolean-trap) 1. [flake8-bugbear (B)](#flake8-bugbear) 1. [flake8-builtins (A)](#flake8-builtins) + 1. [flake8-debugger (T)](#flake8-debugger) 1. [flake8-tidy-imports (I25)](#flake8-tidy-imports) 1. [flake8-print (T)](#flake8-print) 1. [flake8-quotes (Q)](#flake8-quotes) @@ -538,6 +539,14 @@ For more, see [flake8-comprehensions](https://pypi.org/project/flake8-comprehens | C416 | UnnecessaryComprehension | Unnecessary `(list\|set)` comprehension (rewrite using `(list\|set)()`) | 🛠 | | C417 | UnnecessaryMap | Unnecessary `map` usage (rewrite using a `(list\|set\|dict)` comprehension) | | +### flake8-debugger + +For more, see [flake8-debugger](https://pypi.org/project/flake8-debugger/4.1.2/) on PyPI. + +| Code | Name | Message | Fix | +| ---- | ---- | ------- | --- | +| T100 | Debugger | Debugger import found | | + ### flake8-boolean-trap For more, see [flake8-boolean-trap](https://pypi.org/project/flake8-boolean-trap/0.1.0/) on PyPI. diff --git a/flake8_to_ruff/src/plugin.rs b/flake8_to_ruff/src/plugin.rs index 4d96524fd42fb1..7c0262c798c4c7 100644 --- a/flake8_to_ruff/src/plugin.rs +++ b/flake8_to_ruff/src/plugin.rs @@ -10,6 +10,7 @@ pub enum Plugin { Flake8Bugbear, Flake8Builtins, Flake8Comprehensions, + Flake8Debugger, Flake8Docstrings, Flake8TidyImports, Flake8Print, @@ -30,6 +31,7 @@ impl FromStr for Plugin { "flake8-bugbear" => Ok(Plugin::Flake8Bugbear), "flake8-builtins" => Ok(Plugin::Flake8Builtins), "flake8-comprehensions" => Ok(Plugin::Flake8Comprehensions), + "flake8-debugger" => Ok(Plugin::Flake8Debugger), "flake8-docstrings" => Ok(Plugin::Flake8Docstrings), "flake8-tidy-imports" => Ok(Plugin::Flake8TidyImports), "flake8-print" => Ok(Plugin::Flake8Print), @@ -51,9 +53,10 @@ impl Plugin { Plugin::Flake8Bugbear => CheckCodePrefix::B, Plugin::Flake8Builtins => CheckCodePrefix::A, Plugin::Flake8Comprehensions => CheckCodePrefix::C4, + Plugin::Flake8Debugger => CheckCodePrefix::T1, Plugin::Flake8Docstrings => CheckCodePrefix::D, Plugin::Flake8TidyImports => CheckCodePrefix::I25, - Plugin::Flake8Print => CheckCodePrefix::T, + Plugin::Flake8Print => CheckCodePrefix::T2, Plugin::Flake8Quotes => CheckCodePrefix::Q, Plugin::Flake8Annotations => CheckCodePrefix::ANN, Plugin::Flake8BlindExcept => CheckCodePrefix::BLE, @@ -69,6 +72,7 @@ impl Plugin { Plugin::Flake8Bugbear => vec![CheckCodePrefix::B], Plugin::Flake8Builtins => vec![CheckCodePrefix::A], Plugin::Flake8Comprehensions => vec![CheckCodePrefix::C4], + Plugin::Flake8Debugger => vec![CheckCodePrefix::T1], Plugin::Flake8Docstrings => { // Use the user-provided docstring. for key in ["docstring-convention", "docstring_convention"] { @@ -86,7 +90,7 @@ impl Plugin { DocstringConvention::PEP8.select() } Plugin::Flake8TidyImports => vec![CheckCodePrefix::I25], - Plugin::Flake8Print => vec![CheckCodePrefix::T], + Plugin::Flake8Print => vec![CheckCodePrefix::T2], Plugin::Flake8Quotes => vec![CheckCodePrefix::Q], Plugin::Flake8Annotations => vec![CheckCodePrefix::ANN], Plugin::Flake8BlindExcept => vec![CheckCodePrefix::BLE], @@ -364,6 +368,7 @@ pub fn infer_plugins_from_codes(codes: &BTreeSet) -> Vec { diff --git a/src/checks.rs b/src/checks.rs index 251a5539fb6c26..d6d30d5d3d91bf 100644 --- a/src/checks.rs +++ b/src/checks.rs @@ -7,6 +7,7 @@ use strum_macros::{AsRefStr, EnumIter, EnumString}; use crate::ast::types::Range; use crate::autofix::Fix; +use crate::flake8_debugger::types::DebuggerUsingType; use crate::flake8_quotes::settings::Quote; use crate::flake8_tidy_imports::settings::Strictness; use crate::pyupgrade::types::Primitive; @@ -129,6 +130,8 @@ pub enum CheckCode { C415, C416, C417, + // flake8-debugger + T100, // mccabe C901, // flake8-tidy-imports @@ -272,6 +275,7 @@ pub enum CheckCategory { PEP8Naming, Flake8Bandit, Flake8Comprehensions, + Flake8Debugger, Flake8BooleanTrap, Flake8Bugbear, Flake8Builtins, @@ -297,6 +301,7 @@ impl CheckCategory { CheckCategory::Flake8Builtins => "flake8-builtins", CheckCategory::Flake8Bugbear => "flake8-bugbear", CheckCategory::Flake8Comprehensions => "flake8-comprehensions", + CheckCategory::Flake8Debugger => "flake8-debugger", CheckCategory::Flake8TidyImports => "flake8-tidy-imports", CheckCategory::Flake8Print => "flake8-print", CheckCategory::Flake8Quotes => "flake8-quotes", @@ -326,6 +331,9 @@ impl CheckCategory { CheckCategory::Flake8Comprehensions => { Some("https://pypi.org/project/flake8-comprehensions/3.10.1/") } + CheckCategory::Flake8Debugger => { + Some("https://pypi.org/project/flake8-debugger/4.1.2/") + } CheckCategory::Flake8TidyImports => { Some("https://pypi.org/project/flake8-tidy-imports/4.8.0/") } @@ -472,6 +480,8 @@ pub enum CheckKind { UnnecessarySubscriptReversal(String), UnnecessaryComprehension(String), UnnecessaryMap(String), + // flake8-debugger + Debugger(DebuggerUsingType), // flake8-tidy-imports BannedRelativeImport(Strictness), // flake8-print @@ -754,6 +764,8 @@ impl CheckCode { } CheckCode::C416 => CheckKind::UnnecessaryComprehension("(list|set)".to_string()), CheckCode::C417 => CheckKind::UnnecessaryMap("(list|set|dict)".to_string()), + // flake8-debugger + CheckCode::T100 => CheckKind::Debugger(DebuggerUsingType::Import), // flake8-tidy-imports CheckCode::I252 => CheckKind::BannedRelativeImport(Strictness::All), // flake8-print @@ -1007,6 +1019,7 @@ impl CheckCode { CheckCode::C415 => CheckCategory::Flake8Comprehensions, CheckCode::C416 => CheckCategory::Flake8Comprehensions, CheckCode::C417 => CheckCategory::Flake8Comprehensions, + CheckCode::T100 => CheckCategory::Flake8Debugger, CheckCode::I252 => CheckCategory::Flake8TidyImports, CheckCode::T201 => CheckCategory::Flake8Print, CheckCode::T203 => CheckCategory::Flake8Print, @@ -1234,6 +1247,8 @@ impl CheckKind { CheckKind::UnnecessarySubscriptReversal(_) => &CheckCode::C415, CheckKind::UnnecessaryComprehension(..) => &CheckCode::C416, CheckKind::UnnecessaryMap(_) => &CheckCode::C417, + // flake8-debugger + CheckKind::Debugger(_) => &CheckCode::T100, // flake8-tidy-imports CheckKind::BannedRelativeImport(_) => &CheckCode::I252, // flake8-print @@ -1718,6 +1733,11 @@ impl CheckKind { format!("Unnecessary `map` usage (rewrite using a `{obj_type}` comprehension)") } } + // flake8-debugger + CheckKind::Debugger(using_type) => match using_type { + DebuggerUsingType::Call(func_name) => format!("`{func_name}` call found"), + DebuggerUsingType::Import => "Debugger import found".to_string(), + } // flake8-tidy-imports CheckKind::BannedRelativeImport(strictness) => match strictness { Strictness::Parents => { diff --git a/src/checks_gen.rs b/src/checks_gen.rs index b333442ff495d8..4d7e34c0cb77ba 100644 --- a/src/checks_gen.rs +++ b/src/checks_gen.rs @@ -290,6 +290,9 @@ pub enum CheckCodePrefix { S106, S107, T, + T1, + T10, + T100, T2, T20, T201, @@ -1149,7 +1152,10 @@ impl CheckCodePrefix { CheckCodePrefix::S105 => vec![CheckCode::S105], CheckCodePrefix::S106 => vec![CheckCode::S106], CheckCodePrefix::S107 => vec![CheckCode::S107], - CheckCodePrefix::T => vec![CheckCode::T201, CheckCode::T203], + CheckCodePrefix::T => vec![CheckCode::T100, CheckCode::T201, CheckCode::T203], + CheckCodePrefix::T1 => vec![CheckCode::T100], + CheckCodePrefix::T10 => vec![CheckCode::T100], + CheckCodePrefix::T100 => vec![CheckCode::T100], CheckCodePrefix::T2 => vec![CheckCode::T201, CheckCode::T203], CheckCodePrefix::T20 => vec![CheckCode::T201, CheckCode::T203], CheckCodePrefix::T201 => vec![CheckCode::T201], @@ -1554,6 +1560,9 @@ impl CheckCodePrefix { CheckCodePrefix::S106 => PrefixSpecificity::Explicit, CheckCodePrefix::S107 => PrefixSpecificity::Explicit, CheckCodePrefix::T => PrefixSpecificity::Category, + CheckCodePrefix::T1 => PrefixSpecificity::Hundreds, + CheckCodePrefix::T10 => PrefixSpecificity::Tens, + CheckCodePrefix::T100 => PrefixSpecificity::Explicit, CheckCodePrefix::T2 => PrefixSpecificity::Hundreds, CheckCodePrefix::T20 => PrefixSpecificity::Tens, CheckCodePrefix::T201 => PrefixSpecificity::Explicit, diff --git a/src/flake8_debugger/checks.rs b/src/flake8_debugger/checks.rs new file mode 100644 index 00000000000000..45528f92c4bb03 --- /dev/null +++ b/src/flake8_debugger/checks.rs @@ -0,0 +1,84 @@ +use fnv::FnvHashMap; +use rustpython_ast::{Expr, ExprKind, Stmt}; + +use crate::ast::types::Range; +use crate::checks::{Check, CheckKind}; +use crate::flake8_debugger::types::{Debugger, DebuggerUsingType}; + +const BREAKPOINT_FN: &str = "breakpoint"; + +const BUILTINS_MODULE: &str = "builtins"; + +const DEBUGGERS: [Debugger; 7] = [ + ("pdb", &["set_trace"]), + ("pudb", &["set_trace"]), + ("ipdb", &["set_trace", "sset_trace"]), + ("IPython.terminal.embed", &["InteractiveShellEmbed"]), + ("IPython.frontend.terminal.embed", &["InteractiveShellEmbed"]), + ("celery.contrib.rdb", &["set_trace"]), + (BUILTINS_MODULE, &[BREAKPOINT_FN]), +]; + +fn get_debugger(module_name: &str) -> Option<&Debugger> { + DEBUGGERS.iter().find(|&d| d.0 == module_name) +} + +fn function_name(func: &Expr) -> Option<&str> { + if let ExprKind::Name { id, .. } = &func.node { + Some(id) + } else { + None + } +} + +/// Checks for the presence of a debugger call. +pub fn debugger_call( + expr: &Expr, + func: &Expr, + import_aliases: &FnvHashMap<&str, &str>, +) -> Option { + let raw_func_name = function_name(func)?; + let func_name = match import_aliases.get(raw_func_name) { + Some(func_name) => func_name, + None => raw_func_name, + }; + + if func_name == BREAKPOINT_FN { + return Some(Check::new( + CheckKind::Debugger(DebuggerUsingType::Call(func_name.to_string())), + Range::from_located(expr), + )); + } + + if let Some(_) = DEBUGGERS.iter().find(|&d| d.1.contains(&func_name)) { + return Some(Check::new( + CheckKind::Debugger(DebuggerUsingType::Call(raw_func_name.to_string())), + Range::from_located(expr), + )); + } + + None +} + +/// Checks for the presence of a debugger import. +pub fn debugger_import(stmt: &Stmt, module: &Option, name: &str) -> Option { + if let Some(module) = module { + if let Some(debugger) = get_debugger(module) { + if debugger.1.contains(&name) { + return Some(Check::new( + CheckKind::Debugger(DebuggerUsingType::Import), + Range::from_located(stmt), + )); + } + } + } else if name != BUILTINS_MODULE { + if let Some(_) = get_debugger(name) { + return Some(Check::new( + CheckKind::Debugger(DebuggerUsingType::Import), + Range::from_located(stmt), + )); + } + } + + None +} diff --git a/src/flake8_debugger/mod.rs b/src/flake8_debugger/mod.rs new file mode 100644 index 00000000000000..62a8881c0e41bb --- /dev/null +++ b/src/flake8_debugger/mod.rs @@ -0,0 +1,2 @@ +pub mod checks; +pub mod types; diff --git a/src/flake8_debugger/types.rs b/src/flake8_debugger/types.rs new file mode 100644 index 00000000000000..7713cf4256e192 --- /dev/null +++ b/src/flake8_debugger/types.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize, Serialize}; + +pub type Debugger<'a> = (&'a str, &'a [&'a str]); + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum DebuggerUsingType { + Call(String), + Import, +} diff --git a/src/lib.rs b/src/lib.rs index 1cbb700c19a48e..0e371f82758ff4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,7 @@ pub mod flake8_boolean_trap; pub mod flake8_bugbear; mod flake8_builtins; mod flake8_comprehensions; +mod flake8_debugger; mod flake8_print; pub mod flake8_quotes; pub mod flake8_tidy_imports; diff --git a/src/linter.rs b/src/linter.rs index 512d06cc0c6584..023ea26cf55d4d 100644 --- a/src/linter.rs +++ b/src/linter.rs @@ -558,6 +558,7 @@ mod tests { #[test_case(CheckCode::S105, Path::new("S105.py"); "S105")] #[test_case(CheckCode::S106, Path::new("S106.py"); "S106")] #[test_case(CheckCode::S107, Path::new("S107.py"); "S107")] + #[test_case(CheckCode::T100, Path::new("T100.py"); "T100")] #[test_case(CheckCode::T201, Path::new("T201.py"); "T201")] #[test_case(CheckCode::T203, Path::new("T203.py"); "T203")] #[test_case(CheckCode::U001, Path::new("U001.py"); "U001")] diff --git a/src/snapshots/ruff__linter__tests__T100_T100.py.snap b/src/snapshots/ruff__linter__tests__T100_T100.py.snap new file mode 100644 index 00000000000000..4169c9b8498589 --- /dev/null +++ b/src/snapshots/ruff__linter__tests__T100_T100.py.snap @@ -0,0 +1,71 @@ +--- +source: src/linter.rs +expression: checks +--- +- kind: + Debugger: Import + location: + row: 1 + column: 0 + end_location: + row: 1 + column: 10 + fix: ~ +- kind: + Debugger: Import + location: + row: 2 + column: 0 + end_location: + row: 2 + column: 31 + fix: ~ +- kind: + Debugger: Import + location: + row: 3 + column: 0 + end_location: + row: 3 + column: 31 + fix: ~ +- kind: + Debugger: Import + location: + row: 4 + column: 0 + end_location: + row: 4 + column: 40 + fix: ~ +- kind: + Debugger: + Call: breakpoint + location: + row: 6 + column: 0 + end_location: + row: 6 + column: 12 + fix: ~ +- kind: + Debugger: + Call: st + location: + row: 7 + column: 0 + end_location: + row: 7 + column: 4 + fix: ~ +- kind: + Debugger: + Call: set_trace + location: + row: 8 + column: 0 + end_location: + row: 8 + column: 11 + fix: ~ +