Skip to content

Commit 37ca3dd

Browse files
committed
move checker to an extension
1 parent 4826538 commit 37ca3dd

22 files changed

+219
-185
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[MAIN]
2+
load-plugins=pylint.extensions.dunder
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
Added ``bad-dunder-name`` check which flags bad or misspelled dunder methods.
1+
Added ``bad-dunder-name`` extension check which flags bad or misspelled dunder methods.
22

33
Closes #3038

pylint/checkers/dunder_methods.py

Lines changed: 10 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -9,161 +9,30 @@
99
from astroid import Instance, Uninferable, nodes
1010

1111
from pylint.checkers import BaseChecker
12-
from pylint.checkers.utils import only_required_for_messages, safe_infer
12+
from pylint.checkers.utils import safe_infer
13+
from pylint.constants import DUNDER_METHODS
1314
from pylint.interfaces import HIGH
1415

1516
if TYPE_CHECKING:
1617
from pylint.lint import PyLinter
1718

1819

19-
DUNDER_METHODS: dict[tuple[int, int], dict[str, str]] = {
20-
(0, 0): {
21-
"__init__": "Instantiate class directly",
22-
"__del__": "Use del keyword",
23-
"__repr__": "Use repr built-in function",
24-
"__str__": "Use str built-in function",
25-
"__bytes__": "Use bytes built-in function",
26-
"__format__": "Use format built-in function, format string method, or f-string",
27-
"__lt__": "Use < operator",
28-
"__le__": "Use <= operator",
29-
"__eq__": "Use == operator",
30-
"__ne__": "Use != operator",
31-
"__gt__": "Use > operator",
32-
"__ge__": "Use >= operator",
33-
"__hash__": "Use hash built-in function",
34-
"__bool__": "Use bool built-in function",
35-
"__getattr__": "Access attribute directly or use getattr built-in function",
36-
"__getattribute__": "Access attribute directly or use getattr built-in function",
37-
"__setattr__": "Set attribute directly or use setattr built-in function",
38-
"__delattr__": "Use del keyword",
39-
"__dir__": "Use dir built-in function",
40-
"__get__": "Use get method",
41-
"__set__": "Use set method",
42-
"__delete__": "Use del keyword",
43-
"__instancecheck__": "Use isinstance built-in function",
44-
"__subclasscheck__": "Use issubclass built-in function",
45-
"__call__": "Invoke instance directly",
46-
"__len__": "Use len built-in function",
47-
"__length_hint__": "Use length_hint method",
48-
"__getitem__": "Access item via subscript",
49-
"__setitem__": "Set item via subscript",
50-
"__delitem__": "Use del keyword",
51-
"__iter__": "Use iter built-in function",
52-
"__next__": "Use next built-in function",
53-
"__reversed__": "Use reversed built-in function",
54-
"__contains__": "Use in keyword",
55-
"__add__": "Use + operator",
56-
"__sub__": "Use - operator",
57-
"__mul__": "Use * operator",
58-
"__matmul__": "Use @ operator",
59-
"__truediv__": "Use / operator",
60-
"__floordiv__": "Use // operator",
61-
"__mod__": "Use % operator",
62-
"__divmod__": "Use divmod built-in function",
63-
"__pow__": "Use ** operator or pow built-in function",
64-
"__lshift__": "Use << operator",
65-
"__rshift__": "Use >> operator",
66-
"__and__": "Use & operator",
67-
"__xor__": "Use ^ operator",
68-
"__or__": "Use | operator",
69-
"__radd__": "Use + operator",
70-
"__rsub__": "Use - operator",
71-
"__rmul__": "Use * operator",
72-
"__rmatmul__": "Use @ operator",
73-
"__rtruediv__": "Use / operator",
74-
"__rfloordiv__": "Use // operator",
75-
"__rmod__": "Use % operator",
76-
"__rdivmod__": "Use divmod built-in function",
77-
"__rpow__": "Use ** operator or pow built-in function",
78-
"__rlshift__": "Use << operator",
79-
"__rrshift__": "Use >> operator",
80-
"__rand__": "Use & operator",
81-
"__rxor__": "Use ^ operator",
82-
"__ror__": "Use | operator",
83-
"__iadd__": "Use += operator",
84-
"__isub__": "Use -= operator",
85-
"__imul__": "Use *= operator",
86-
"__imatmul__": "Use @= operator",
87-
"__itruediv__": "Use /= operator",
88-
"__ifloordiv__": "Use //= operator",
89-
"__imod__": "Use %= operator",
90-
"__ipow__": "Use **= operator",
91-
"__ilshift__": "Use <<= operator",
92-
"__irshift__": "Use >>= operator",
93-
"__iand__": "Use &= operator",
94-
"__ixor__": "Use ^= operator",
95-
"__ior__": "Use |= operator",
96-
"__neg__": "Multiply by -1 instead",
97-
"__pos__": "Multiply by +1 instead",
98-
"__abs__": "Use abs built-in function",
99-
"__invert__": "Use ~ operator",
100-
"__complex__": "Use complex built-in function",
101-
"__int__": "Use int built-in function",
102-
"__float__": "Use float built-in function",
103-
"__round__": "Use round built-in function",
104-
"__trunc__": "Use math.trunc function",
105-
"__floor__": "Use math.floor function",
106-
"__ceil__": "Use math.ceil function",
107-
"__enter__": "Invoke context manager directly",
108-
"__aenter__": "Invoke context manager directly",
109-
"__copy__": "Use copy.copy function",
110-
"__deepcopy__": "Use copy.deepcopy function",
111-
"__fspath__": "Use os.fspath function instead",
112-
},
113-
(3, 10): {
114-
"__aiter__": "Use aiter built-in function",
115-
"__anext__": "Use anext built-in function",
116-
},
117-
}
118-
119-
120-
EXTRA_DUNDER_METHODS = [
121-
"__new__",
122-
"__subclasses__",
123-
"__init_subclass__",
124-
"__set_name__",
125-
"__class_getitem__",
126-
"__missing__",
127-
"__exit__",
128-
"__await__",
129-
"__aexit__",
130-
"__getnewargs_ex__",
131-
"__getnewargs__",
132-
"__getstate__",
133-
"__setstate__",
134-
"__reduce__",
135-
"__reduce_ex__",
136-
"__post_init__", # part of `dataclasses` module
137-
]
138-
139-
DUNDER_PROPERTIES = [
140-
"__class__",
141-
"__dict__",
142-
"__doc__",
143-
"__format__",
144-
"__module__",
145-
"__sizeof__",
146-
"__subclasshook__",
147-
"__weakref__",
148-
]
149-
150-
151-
class DunderChecker(BaseChecker):
152-
"""Checks related to dunder methods.
20+
class DunderCallChecker(BaseChecker):
21+
"""Check for unnecessary dunder method calls.
15322
15423
Docs: https://docs.python.org/3/reference/datamodel.html#basic-customization
155-
156-
For `unnecessary-dunder-call` we exclude dunder names in list
157-
`EXTRA_DUNDER_METHODS` since these either have no alternative
158-
method of being called or have a genuine use case for being called manually.
24+
We exclude names in list pylint.constants.EXTRA_DUNDER_METHODS such as
25+
__index__ (see https://github.com/PyCQA/pylint/issues/6795)
26+
since these either have no alternative method of being called or
27+
have a genuine use case for being called manually.
15928
16029
Additionally, we exclude classes that are not instantiated since these
16130
might be used to access the dunder methods of a base class of an instance.
16231
We also exclude dunder method calls on super() since
16332
these can't be written in an alternative manner.
16433
"""
16534

166-
name = "dunder"
35+
name = "unnecessary-dunder-call"
16736
priority = -1
16837
msgs = {
16938
"C2801": (
@@ -172,12 +41,6 @@ class DunderChecker(BaseChecker):
17241
"Used when a dunder method is manually called instead "
17342
"of using the corresponding function/method/operator.",
17443
),
175-
"C2802": (
176-
"Bad or misspelled dunder method name %s.",
177-
"bad-dunder-name",
178-
"Used when a dunder method is misspelled or defined with a name "
179-
"not within the predefined list of dunder names.",
180-
),
18144
}
18245
options = ()
18346

@@ -201,30 +64,6 @@ def within_dunder_def(node: nodes.NodeNG) -> bool:
20164
parent = parent.parent
20265
return False
20366

204-
@only_required_for_messages("bad-dunder-name")
205-
def visit_functiondef(self, node: nodes.FunctionDef) -> None:
206-
"""Check if known dunder method is misspelled or dunder name is not one
207-
of the pre-defined names.
208-
"""
209-
# ignore module-level functions
210-
if not node.is_method():
211-
return
212-
213-
# Detect something that could be a bad dunder method
214-
if (
215-
node.name.startswith("_")
216-
and node.name.endswith("_")
217-
and node.name not in self._dunder_methods
218-
and node.name not in EXTRA_DUNDER_METHODS + DUNDER_PROPERTIES
219-
):
220-
self.add_message(
221-
"bad-dunder-name",
222-
node=node,
223-
args=(node.name),
224-
confidence=HIGH,
225-
)
226-
227-
@only_required_for_messages("unnecessary-dunder-call")
22867
def visit_call(self, node: nodes.Call) -> None:
22968
"""Check if method being called is an unnecessary dunder method."""
23069
if (
@@ -251,4 +90,4 @@ def visit_call(self, node: nodes.Call) -> None:
25190

25291

25392
def register(linter: PyLinter) -> None:
254-
linter.register_checker(DunderChecker(linter))
93+
linter.register_checker(DunderCallChecker(linter))

pylint/constants.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,133 @@ def _get_pylint_home() -> str:
168168
"typing_extensions.Never",
169169
)
170170
)
171+
172+
DUNDER_METHODS: dict[tuple[int, int], dict[str, str]] = {
173+
(0, 0): {
174+
"__init__": "Instantiate class directly",
175+
"__del__": "Use del keyword",
176+
"__repr__": "Use repr built-in function",
177+
"__str__": "Use str built-in function",
178+
"__bytes__": "Use bytes built-in function",
179+
"__format__": "Use format built-in function, format string method, or f-string",
180+
"__lt__": "Use < operator",
181+
"__le__": "Use <= operator",
182+
"__eq__": "Use == operator",
183+
"__ne__": "Use != operator",
184+
"__gt__": "Use > operator",
185+
"__ge__": "Use >= operator",
186+
"__hash__": "Use hash built-in function",
187+
"__bool__": "Use bool built-in function",
188+
"__getattr__": "Access attribute directly or use getattr built-in function",
189+
"__getattribute__": "Access attribute directly or use getattr built-in function",
190+
"__setattr__": "Set attribute directly or use setattr built-in function",
191+
"__delattr__": "Use del keyword",
192+
"__dir__": "Use dir built-in function",
193+
"__get__": "Use get method",
194+
"__set__": "Use set method",
195+
"__delete__": "Use del keyword",
196+
"__instancecheck__": "Use isinstance built-in function",
197+
"__subclasscheck__": "Use issubclass built-in function",
198+
"__call__": "Invoke instance directly",
199+
"__len__": "Use len built-in function",
200+
"__length_hint__": "Use length_hint method",
201+
"__getitem__": "Access item via subscript",
202+
"__setitem__": "Set item via subscript",
203+
"__delitem__": "Use del keyword",
204+
"__iter__": "Use iter built-in function",
205+
"__next__": "Use next built-in function",
206+
"__reversed__": "Use reversed built-in function",
207+
"__contains__": "Use in keyword",
208+
"__add__": "Use + operator",
209+
"__sub__": "Use - operator",
210+
"__mul__": "Use * operator",
211+
"__matmul__": "Use @ operator",
212+
"__truediv__": "Use / operator",
213+
"__floordiv__": "Use // operator",
214+
"__mod__": "Use % operator",
215+
"__divmod__": "Use divmod built-in function",
216+
"__pow__": "Use ** operator or pow built-in function",
217+
"__lshift__": "Use << operator",
218+
"__rshift__": "Use >> operator",
219+
"__and__": "Use & operator",
220+
"__xor__": "Use ^ operator",
221+
"__or__": "Use | operator",
222+
"__radd__": "Use + operator",
223+
"__rsub__": "Use - operator",
224+
"__rmul__": "Use * operator",
225+
"__rmatmul__": "Use @ operator",
226+
"__rtruediv__": "Use / operator",
227+
"__rfloordiv__": "Use // operator",
228+
"__rmod__": "Use % operator",
229+
"__rdivmod__": "Use divmod built-in function",
230+
"__rpow__": "Use ** operator or pow built-in function",
231+
"__rlshift__": "Use << operator",
232+
"__rrshift__": "Use >> operator",
233+
"__rand__": "Use & operator",
234+
"__rxor__": "Use ^ operator",
235+
"__ror__": "Use | operator",
236+
"__iadd__": "Use += operator",
237+
"__isub__": "Use -= operator",
238+
"__imul__": "Use *= operator",
239+
"__imatmul__": "Use @= operator",
240+
"__itruediv__": "Use /= operator",
241+
"__ifloordiv__": "Use //= operator",
242+
"__imod__": "Use %= operator",
243+
"__ipow__": "Use **= operator",
244+
"__ilshift__": "Use <<= operator",
245+
"__irshift__": "Use >>= operator",
246+
"__iand__": "Use &= operator",
247+
"__ixor__": "Use ^= operator",
248+
"__ior__": "Use |= operator",
249+
"__neg__": "Multiply by -1 instead",
250+
"__pos__": "Multiply by +1 instead",
251+
"__abs__": "Use abs built-in function",
252+
"__invert__": "Use ~ operator",
253+
"__complex__": "Use complex built-in function",
254+
"__int__": "Use int built-in function",
255+
"__float__": "Use float built-in function",
256+
"__round__": "Use round built-in function",
257+
"__trunc__": "Use math.trunc function",
258+
"__floor__": "Use math.floor function",
259+
"__ceil__": "Use math.ceil function",
260+
"__enter__": "Invoke context manager directly",
261+
"__aenter__": "Invoke context manager directly",
262+
"__copy__": "Use copy.copy function",
263+
"__deepcopy__": "Use copy.deepcopy function",
264+
"__fspath__": "Use os.fspath function instead",
265+
},
266+
(3, 10): {
267+
"__aiter__": "Use aiter built-in function",
268+
"__anext__": "Use anext built-in function",
269+
},
270+
}
271+
272+
EXTRA_DUNDER_METHODS = [
273+
"__new__",
274+
"__subclasses__",
275+
"__init_subclass__",
276+
"__set_name__",
277+
"__class_getitem__",
278+
"__missing__",
279+
"__exit__",
280+
"__await__",
281+
"__aexit__",
282+
"__getnewargs_ex__",
283+
"__getnewargs__",
284+
"__getstate__",
285+
"__setstate__",
286+
"__reduce__",
287+
"__reduce_ex__",
288+
"__post_init__", # part of `dataclasses` module
289+
]
290+
291+
DUNDER_PROPERTIES = [
292+
"__class__",
293+
"__dict__",
294+
"__doc__",
295+
"__format__",
296+
"__module__",
297+
"__sizeof__",
298+
"__subclasshook__",
299+
"__weakref__",
300+
]

0 commit comments

Comments
 (0)