Supported starred exceptions in length-one tuple detection (#7080)

This commit is contained in:
Charlie Marsh 2023-09-03 14:31:13 +01:00 committed by GitHub
parent b70dde4a77
commit b0d171ac19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 17 deletions

View file

@ -1,3 +1,5 @@
retriable_exceptions = (FileExistsError, FileNotFoundError)
try: try:
pass pass
except (ValueError,): except (ValueError,):
@ -6,3 +8,5 @@ except AttributeError:
pass pass
except (ImportError, TypeError): except (ImportError, TypeError):
pass pass
except (*retriable_exceptions,):
pass

View file

@ -2,6 +2,7 @@ use ruff_python_ast::{self as ast, ExceptHandler, Expr};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::map_starred;
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
@ -41,11 +42,7 @@ pub struct RedundantTupleInExceptionHandler {
impl AlwaysAutofixableViolation for RedundantTupleInExceptionHandler { impl AlwaysAutofixableViolation for RedundantTupleInExceptionHandler {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
let RedundantTupleInExceptionHandler { name } = self; format!("A length-one tuple literal is redundant in exception handlers")
format!(
"A length-one tuple literal is redundant. Write `except {name}` instead of `except \
({name},)`."
)
} }
fn autofix_title(&self) -> String { fn autofix_title(&self) -> String {
@ -70,9 +67,10 @@ pub(crate) fn redundant_tuple_in_exception_handler(
let Expr::Tuple(ast::ExprTuple { elts, .. }) = type_.as_ref() else { let Expr::Tuple(ast::ExprTuple { elts, .. }) = type_.as_ref() else {
continue; continue;
}; };
let [elt] = &elts[..] else { let [elt] = elts.as_slice() else {
continue; continue;
}; };
let elt = map_starred(elt);
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
RedundantTupleInExceptionHandler { RedundantTupleInExceptionHandler {
name: checker.generator().expr(elt), name: checker.generator().expr(elt),

View file

@ -1,24 +1,43 @@
--- ---
source: crates/ruff/src/rules/flake8_bugbear/mod.rs source: crates/ruff/src/rules/flake8_bugbear/mod.rs
--- ---
B013.py:3:8: B013 [*] A length-one tuple literal is redundant. Write `except ValueError` instead of `except (ValueError,)`. B013.py:5:8: B013 [*] A length-one tuple literal is redundant in exception handlers
| |
1 | try: 3 | try:
2 | pass
3 | except (ValueError,):
| ^^^^^^^^^^^^^ B013
4 | pass 4 | pass
5 | except AttributeError: 5 | except (ValueError,):
| ^^^^^^^^^^^^^ B013
6 | pass
7 | except AttributeError:
| |
= help: Replace with `except ValueError` = help: Replace with `except ValueError`
Fix Fix
1 1 | try: 2 2 |
2 2 | pass 3 3 | try:
3 |-except (ValueError,):
3 |+except ValueError:
4 4 | pass 4 4 | pass
5 5 | except AttributeError: 5 |-except (ValueError,):
5 |+except ValueError:
6 6 | pass 6 6 | pass
7 7 | except AttributeError:
8 8 | pass
B013.py:11:8: B013 [*] A length-one tuple literal is redundant in exception handlers
|
9 | except (ImportError, TypeError):
10 | pass
11 | except (*retriable_exceptions,):
| ^^^^^^^^^^^^^^^^^^^^^^^^ B013
12 | pass
|
= help: Replace with `except retriable_exceptions`
Fix
8 8 | pass
9 9 | except (ImportError, TypeError):
10 10 | pass
11 |-except (*retriable_exceptions,):
11 |+except retriable_exceptions:
12 12 | pass

View file

@ -653,6 +653,17 @@ pub fn map_subscript(expr: &Expr) -> &Expr {
} }
} }
/// Given an [`Expr`] that can be starred, return the underlying starred expression.
pub fn map_starred(expr: &Expr) -> &Expr {
if let Expr::Starred(ast::ExprStarred { value, .. }) = expr {
// Ex) `*args`
value
} else {
// Ex) `args`
expr
}
}
/// Return `true` if the body uses `locals()`, `globals()`, `vars()`, `eval()`. /// Return `true` if the body uses `locals()`, `globals()`, `vars()`, `eval()`.
/// ///
/// Accepts a closure that determines whether a given name (e.g., `"list"`) is a Python builtin. /// Accepts a closure that determines whether a given name (e.g., `"list"`) is a Python builtin.