Ignore unnecessary dunder calls within dunder definitions (#9496)

Closes https://github.com/astral-sh/ruff/issues/9486.
This commit is contained in:
Charlie Marsh 2024-01-12 14:48:42 -05:00 committed by GitHub
parent 009430e034
commit 957a1f35c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 32 additions and 2 deletions

View file

@ -24,6 +24,12 @@ class Thing:
super().__init__() # OK super().__init__() # OK
super().__class__(stuff=(1, 2, 3)) # OK super().__class__(stuff=(1, 2, 3)) # OK
def __getattribute__(self, item):
return object.__getattribute__(self, item) # OK
def do_thing(self, item):
return object.__getattribute__(self, item) # PLC2801
blah = lambda: {"a": 1}.__delitem__("a") # OK blah = lambda: {"a": 1}.__delitem__("a") # OK

View file

@ -1,6 +1,6 @@
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, Expr}; use ruff_python_ast::{self as ast, Expr, Stmt};
use ruff_python_semantic::SemanticModel; use ruff_python_semantic::SemanticModel;
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
@ -80,11 +80,17 @@ pub(crate) fn unnecessary_dunder_call(checker: &mut Checker, call: &ast::ExprCal
return; return;
} }
// Ignore certain dunder methods used in lambda expressions. // Ignore certain dunder method calls in lambda expressions. These methods would require
// rewriting as a statement, which is not possible in a lambda expression.
if allow_nested_expression(attr, checker.semantic()) { if allow_nested_expression(attr, checker.semantic()) {
return; return;
} }
// Ignore dunder method calls within dunder methods definitions.
if in_dunder_method_definition(checker.semantic()) {
return;
}
// Ignore dunder methods used on `super`. // Ignore dunder methods used on `super`.
if let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() { if let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() {
if checker.semantic().is_builtin("super") { if checker.semantic().is_builtin("super") {
@ -344,3 +350,13 @@ fn allow_nested_expression(dunder_name: &str, semantic: &SemanticModel) -> bool
| "__ior__" | "__ior__"
) )
} }
/// Returns `true` if the [`SemanticModel`] is currently in a dunder method definition.
fn in_dunder_method_definition(semantic: &SemanticModel) -> bool {
semantic.current_statements().any(|statement| {
let Stmt::FunctionDef(func_def) = statement else {
return false;
};
func_def.name.starts_with("__") && func_def.name.ends_with("__")
})
}

View file

@ -321,4 +321,12 @@ unnecessary_dunder_call.py:19:7: PLC2801 Unnecessary dunder call to `__neg__`. M
| |
= help: Multiply by -1 instead = help: Multiply by -1 instead
unnecessary_dunder_call.py:31:16: PLC2801 Unnecessary dunder call to `__getattribute__`. Access attribute directly or use getattr built-in function.
|
30 | def do_thing(self, item):
31 | return object.__getattribute__(self, item) # PLC2801
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC2801
|
= help: Access attribute directly or use getattr built-in function