forked from astral-sh/ruff
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## Summary Checks for any misspelled dunder name method and for any method defined with `__...__` that's not one of the pre-defined methods. The pre-defined methods encompass all of Python's standard dunder methods. ref: astral-sh#970 ## Test Plan Snapshots and manual runs of pylint.
- Loading branch information
1 parent
9eb04dd
commit 5c730a5
Showing
8 changed files
with
307 additions
and
0 deletions.
There are no files selected for viewing
46 changes: 46 additions & 0 deletions
46
crates/ruff/resources/test/fixtures/pylint/bad_dunder_method_name.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
class Apples: | ||
def _init_(self): # [bad-dunder-name] | ||
pass | ||
|
||
def __hello__(self): # [bad-dunder-name] | ||
print("hello") | ||
|
||
def __init_(self): # [bad-dunder-name] | ||
# author likely unintentionally misspelled the correct init dunder. | ||
pass | ||
|
||
def _init_(self): # [bad-dunder-name] | ||
# author likely unintentionally misspelled the correct init dunder. | ||
pass | ||
|
||
def ___neg__(self): # [bad-dunder-name] | ||
# author likely accidentally added an additional `_` | ||
pass | ||
|
||
def __inv__(self): # [bad-dunder-name] | ||
# author likely meant to call the invert dunder method | ||
pass | ||
|
||
def hello(self): | ||
print("hello") | ||
|
||
def __init__(self): | ||
pass | ||
|
||
def init(self): | ||
# valid name even though someone could accidentally mean __init__ | ||
pass | ||
|
||
def _protected_method(self): | ||
print("Protected") | ||
|
||
def __private_method(self): | ||
print("Private") | ||
|
||
@property | ||
def __doc__(self): | ||
return "Docstring" | ||
|
||
|
||
def __foo_bar__(): # this is not checked by the [bad-dunder-name] rule | ||
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
192 changes: 192 additions & 0 deletions
192
crates/ruff/src/rules/pylint/rules/bad_dunder_method_name.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
use ruff_diagnostics::{Diagnostic, Violation}; | ||
use ruff_macros::{derive_message_formats, violation}; | ||
use ruff_python_ast::identifier::Identifier; | ||
use ruff_python_ast::Stmt; | ||
|
||
use crate::checkers::ast::Checker; | ||
|
||
/// ## What it does | ||
/// Checks for any misspelled dunder name method and for any method | ||
/// defined with `__...__` that's not one of the pre-defined methods. | ||
/// | ||
/// The pre-defined methods encompass all of Python's standard dunder | ||
/// methods. | ||
/// | ||
/// ## Why is this bad? | ||
/// Misspelled dunder name methods may cause your code to not function | ||
/// as expected. | ||
/// | ||
/// Since dunder methods are associated with customizing the behavior | ||
/// of a class in Python, introducing a dunder method such as `__foo__` | ||
/// that diverges from standard Python dunder methods could potentially | ||
/// confuse someone reading the code. | ||
/// | ||
/// ## Example | ||
/// ```python | ||
/// class Foo: | ||
/// def __init_(self): | ||
/// ... | ||
/// ``` | ||
/// | ||
/// Use instead: | ||
/// ```python | ||
/// class Foo: | ||
/// def __init__(self): | ||
/// ... | ||
/// ``` | ||
#[violation] | ||
pub struct BadDunderMethodName { | ||
name: String, | ||
} | ||
|
||
impl Violation for BadDunderMethodName { | ||
#[derive_message_formats] | ||
fn message(&self) -> String { | ||
let BadDunderMethodName { name } = self; | ||
format!("Bad or misspelled dunder method name `{name}`. (bad-dunder-name)") | ||
} | ||
} | ||
|
||
/// PLW3201 | ||
pub(crate) fn bad_dunder_method_name(checker: &mut Checker, class_body: &[Stmt]) { | ||
for method in class_body | ||
.iter() | ||
.filter_map(ruff_python_ast::Stmt::as_function_def_stmt) | ||
.filter(|method| { | ||
if is_known_dunder_method(&method.name) { | ||
return false; | ||
} | ||
method.name.starts_with('_') && method.name.ends_with('_') | ||
}) | ||
{ | ||
checker.diagnostics.push(Diagnostic::new( | ||
BadDunderMethodName { | ||
name: method.name.to_string(), | ||
}, | ||
method.identifier(), | ||
)); | ||
} | ||
} | ||
|
||
/// Returns `true` if a method is a known dunder method. | ||
fn is_known_dunder_method(method: &str) -> bool { | ||
matches!( | ||
method, | ||
"__abs__" | ||
| "__add__" | ||
| "__aenter__" | ||
| "__aexit__" | ||
| "__aiter__" | ||
| "__and__" | ||
| "__anext__" | ||
| "__await__" | ||
| "__bool__" | ||
| "__bytes__" | ||
| "__call__" | ||
| "__ceil__" | ||
| "__class__" | ||
| "__class_getitem__" | ||
| "__complex__" | ||
| "__contains__" | ||
| "__copy__" | ||
| "__deepcopy__" | ||
| "__del__" | ||
| "__delattr__" | ||
| "__delete__" | ||
| "__delitem__" | ||
| "__dict__" | ||
| "__dir__" | ||
| "__divmod__" | ||
| "__doc__" | ||
| "__enter__" | ||
| "__eq__" | ||
| "__exit__" | ||
| "__float__" | ||
| "__floor__" | ||
| "__floordiv__" | ||
| "__format__" | ||
| "__fspath__" | ||
| "__ge__" | ||
| "__get__" | ||
| "__getattr__" | ||
| "__getattribute__" | ||
| "__getitem__" | ||
| "__getnewargs__" | ||
| "__getnewargs_ex__" | ||
| "__getstate__" | ||
| "__gt__" | ||
| "__hash__" | ||
| "__iadd__" | ||
| "__iand__" | ||
| "__ifloordiv__" | ||
| "__ilshift__" | ||
| "__imatmul__" | ||
| "__imod__" | ||
| "__imul__" | ||
| "__init__" | ||
| "__init_subclass__" | ||
| "__instancecheck__" | ||
| "__int__" | ||
| "__invert__" | ||
| "__ior__" | ||
| "__ipow__" | ||
| "__irshift__" | ||
| "__isub__" | ||
| "__iter__" | ||
| "__itruediv__" | ||
| "__ixor__" | ||
| "__le__" | ||
| "__len__" | ||
| "__length_hint__" | ||
| "__lshift__" | ||
| "__lt__" | ||
| "__matmul__" | ||
| "__missing__" | ||
| "__mod__" | ||
| "__module__" | ||
| "__mul__" | ||
| "__ne__" | ||
| "__neg__" | ||
| "__new__" | ||
| "__next__" | ||
| "__or__" | ||
| "__pos__" | ||
| "__post_init__" | ||
| "__pow__" | ||
| "__radd__" | ||
| "__rand__" | ||
| "__rdivmod__" | ||
| "__reduce__" | ||
| "__reduce_ex__" | ||
| "__repr__" | ||
| "__reversed__" | ||
| "__rfloordiv__" | ||
| "__rlshift__" | ||
| "__rmatmul__" | ||
| "__rmod__" | ||
| "__rmul__" | ||
| "__ror__" | ||
| "__round__" | ||
| "__rpow__" | ||
| "__rrshift__" | ||
| "__rshift__" | ||
| "__rsub__" | ||
| "__rtruediv__" | ||
| "__rxor__" | ||
| "__set__" | ||
| "__set_name__" | ||
| "__setattr__" | ||
| "__setitem__" | ||
| "__setstate__" | ||
| "__sizeof__" | ||
| "__str__" | ||
| "__sub__" | ||
| "__subclasscheck__" | ||
| "__subclasses__" | ||
| "__subclasshook__" | ||
| "__truediv__" | ||
| "__trunc__" | ||
| "__weakref__" | ||
| "__xor__" | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
...rules/pylint/snapshots/ruff__rules__pylint__tests__PLW3201_bad_dunder_method_name.py.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
--- | ||
source: crates/ruff/src/rules/pylint/mod.rs | ||
--- | ||
bad_dunder_method_name.py:2:9: PLW3201 Bad or misspelled dunder method name `_init_`. (bad-dunder-name) | ||
| | ||
1 | class Apples: | ||
2 | def _init_(self): # [bad-dunder-name] | ||
| ^^^^^^ PLW3201 | ||
3 | pass | ||
| | ||
|
||
bad_dunder_method_name.py:5:9: PLW3201 Bad or misspelled dunder method name `__hello__`. (bad-dunder-name) | ||
| | ||
3 | pass | ||
4 | | ||
5 | def __hello__(self): # [bad-dunder-name] | ||
| ^^^^^^^^^ PLW3201 | ||
6 | print("hello") | ||
| | ||
|
||
bad_dunder_method_name.py:8:9: PLW3201 Bad or misspelled dunder method name `__init_`. (bad-dunder-name) | ||
| | ||
6 | print("hello") | ||
7 | | ||
8 | def __init_(self): # [bad-dunder-name] | ||
| ^^^^^^^ PLW3201 | ||
9 | # author likely unintentionally misspelled the correct init dunder. | ||
10 | pass | ||
| | ||
|
||
bad_dunder_method_name.py:12:9: PLW3201 Bad or misspelled dunder method name `_init_`. (bad-dunder-name) | ||
| | ||
10 | pass | ||
11 | | ||
12 | def _init_(self): # [bad-dunder-name] | ||
| ^^^^^^ PLW3201 | ||
13 | # author likely unintentionally misspelled the correct init dunder. | ||
14 | pass | ||
| | ||
|
||
bad_dunder_method_name.py:16:9: PLW3201 Bad or misspelled dunder method name `___neg__`. (bad-dunder-name) | ||
| | ||
14 | pass | ||
15 | | ||
16 | def ___neg__(self): # [bad-dunder-name] | ||
| ^^^^^^^^ PLW3201 | ||
17 | # author likely accidentally added an additional `_` | ||
18 | pass | ||
| | ||
|
||
bad_dunder_method_name.py:20:9: PLW3201 Bad or misspelled dunder method name `__inv__`. (bad-dunder-name) | ||
| | ||
18 | pass | ||
19 | | ||
20 | def __inv__(self): # [bad-dunder-name] | ||
| ^^^^^^^ PLW3201 | ||
21 | # author likely meant to call the invert dunder method | ||
22 | pass | ||
| | ||
|
||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.