Skip to content

Commit 66ae21c

Browse files
DanielNoordPierre-Sassoulas
authored andcommitted
Check py-version for async unnecessary-dunder-call (#7549)
1 parent 983d5fc commit 66ae21c

7 files changed

+147
-97
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix the message for ``unnecessary-dunder-call`` for ``__aiter__`` and ``__aneext__``. Also
2+
only emit the warning when ``py-version`` >= 3.10.
3+
4+
Closes #7529

pylint/checkers/dunder_methods.py

+107-97
Original file line numberDiff line numberDiff line change
@@ -16,101 +16,105 @@
1616
from pylint.lint import PyLinter
1717

1818

19-
DUNDER_METHODS: dict[str, str] = {
20-
"__init__": "Instantiate class directly",
21-
"__del__": "Use del keyword",
22-
"__repr__": "Use repr built-in function",
23-
"__str__": "Use str built-in function",
24-
"__bytes__": "Use bytes built-in function",
25-
"__format__": "Use format built-in function, format string method, or f-string",
26-
"__lt__": "Use < operator",
27-
"__le__": "Use <= operator",
28-
"__eq__": "Use == operator",
29-
"__ne__": "Use != operator",
30-
"__gt__": "Use > operator",
31-
"__ge__": "Use >= operator",
32-
"__hash__": "Use hash built-in function",
33-
"__bool__": "Use bool built-in function",
34-
"__getattr__": "Access attribute directly or use getattr built-in function",
35-
"__getattribute__": "Access attribute directly or use getattr built-in function",
36-
"__setattr__": "Set attribute directly or use setattr built-in function",
37-
"__delattr__": "Use del keyword",
38-
"__dir__": "Use dir built-in function",
39-
"__get__": "Use get method",
40-
"__set__": "Use set method",
41-
"__delete__": "Use del keyword",
42-
"__instancecheck__": "Use isinstance built-in function",
43-
"__subclasscheck__": "Use issubclass built-in function",
44-
"__call__": "Invoke instance directly",
45-
"__len__": "Use len built-in function",
46-
"__length_hint__": "Use length_hint method",
47-
"__getitem__": "Access item via subscript",
48-
"__setitem__": "Set item via subscript",
49-
"__delitem__": "Use del keyword",
50-
"__iter__": "Use iter built-in function",
51-
"__next__": "Use next built-in function",
52-
"__reversed__": "Use reversed built-in function",
53-
"__contains__": "Use in keyword",
54-
"__add__": "Use + operator",
55-
"__sub__": "Use - operator",
56-
"__mul__": "Use * operator",
57-
"__matmul__": "Use @ operator",
58-
"__truediv__": "Use / operator",
59-
"__floordiv__": "Use // operator",
60-
"__mod__": "Use % operator",
61-
"__divmod__": "Use divmod built-in function",
62-
"__pow__": "Use ** operator or pow built-in function",
63-
"__lshift__": "Use << operator",
64-
"__rshift__": "Use >> operator",
65-
"__and__": "Use & operator",
66-
"__xor__": "Use ^ operator",
67-
"__or__": "Use | operator",
68-
"__radd__": "Use + operator",
69-
"__rsub__": "Use - operator",
70-
"__rmul__": "Use * operator",
71-
"__rmatmul__": "Use @ operator",
72-
"__rtruediv__": "Use / operator",
73-
"__rfloordiv__": "Use // operator",
74-
"__rmod__": "Use % operator",
75-
"__rdivmod__": "Use divmod built-in function",
76-
"__rpow__": "Use ** operator or pow built-in function",
77-
"__rlshift__": "Use << operator",
78-
"__rrshift__": "Use >> operator",
79-
"__rand__": "Use & operator",
80-
"__rxor__": "Use ^ operator",
81-
"__ror__": "Use | operator",
82-
"__iadd__": "Use += operator",
83-
"__isub__": "Use -= operator",
84-
"__imul__": "Use *= operator",
85-
"__imatmul__": "Use @= operator",
86-
"__itruediv__": "Use /= operator",
87-
"__ifloordiv__": "Use //= operator",
88-
"__imod__": "Use %= operator",
89-
"__ipow__": "Use **= operator",
90-
"__ilshift__": "Use <<= operator",
91-
"__irshift__": "Use >>= operator",
92-
"__iand__": "Use &= operator",
93-
"__ixor__": "Use ^= operator",
94-
"__ior__": "Use |= operator",
95-
"__neg__": "Multiply by -1 instead",
96-
"__pos__": "Multiply by +1 instead",
97-
"__abs__": "Use abs built-in function",
98-
"__invert__": "Use ~ operator",
99-
"__complex__": "Use complex built-in function",
100-
"__int__": "Use int built-in function",
101-
"__float__": "Use float built-in function",
102-
"__index__": "Use index method",
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-
"__aiter__": "Use iter built-in function",
109-
"__anext__": "Use next built-in function",
110-
"__aenter__": "Invoke context manager directly",
111-
"__copy__": "Use copy.copy function",
112-
"__deepcopy__": "Use copy.deepcopy function",
113-
"__fspath__": "Use os.fspath function instead",
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+
"__index__": "Use index method",
104+
"__round__": "Use round built-in function",
105+
"__trunc__": "Use math.trunc function",
106+
"__floor__": "Use math.floor function",
107+
"__ceil__": "Use math.ceil function",
108+
"__enter__": "Invoke context manager directly",
109+
"__aenter__": "Invoke context manager directly",
110+
"__copy__": "Use copy.copy function",
111+
"__deepcopy__": "Use copy.deepcopy function",
112+
"__fspath__": "Use os.fspath function instead",
113+
},
114+
(3, 10): {
115+
"__aiter__": "Use aiter built-in function",
116+
"__anext__": "Use anext built-in function",
117+
},
114118
}
115119

116120

@@ -143,6 +147,12 @@ class DunderCallChecker(BaseChecker):
143147
}
144148
options = ()
145149

150+
def open(self) -> None:
151+
self._dunder_methods: dict[str, str] = {}
152+
for since_vers, dunder_methods in DUNDER_METHODS.items():
153+
if since_vers <= self.linter.config.py_version:
154+
self._dunder_methods.update(dunder_methods)
155+
146156
@staticmethod
147157
def within_dunder_def(node: nodes.NodeNG) -> bool:
148158
"""Check if dunder method call is within a dunder method definition."""
@@ -161,7 +171,7 @@ def visit_call(self, node: nodes.Call) -> None:
161171
"""Check if method being called is an unnecessary dunder method."""
162172
if (
163173
isinstance(node.func, nodes.Attribute)
164-
and node.func.attrname in DUNDER_METHODS
174+
and node.func.attrname in self._dunder_methods
165175
and not self.within_dunder_def(node)
166176
and not (
167177
isinstance(node.func.expr, nodes.Call)
@@ -177,7 +187,7 @@ def visit_call(self, node: nodes.Call) -> None:
177187
self.add_message(
178188
"unnecessary-dunder-call",
179189
node=node,
180-
args=(node.func.attrname, DUNDER_METHODS[node.func.attrname]),
190+
args=(node.func.attrname, self._dunder_methods[node.func.attrname]),
181191
confidence=HIGH,
182192
)
183193

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""Checks for unnecessary-dunder-call on __aiter__/__anext__ with py-version=3.10."""
2+
3+
4+
class MyClass:
5+
"""A class implementing __aiter__ and __anext__."""
6+
7+
def __aiter__(self):
8+
...
9+
10+
async def __anext__(self):
11+
...
12+
13+
14+
MyClass().__aiter__() # [unnecessary-dunder-call]
15+
MyClass().__anext__() # [unnecessary-dunder-call]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[master]
2+
py-version=3.10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
unnecessary-dunder-call:14:0:14:21::Unnecessarily calls dunder method __aiter__. Use aiter built-in function.:HIGH
2+
unnecessary-dunder-call:15:0:15:21::Unnecessarily calls dunder method __anext__. Use anext built-in function.:HIGH
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""Checks for unnecessary-dunder-call on __aiter__/__anext__ with py-version=3.9."""
2+
3+
4+
class MyClass:
5+
"""A class implementing __aiter__ and __anext__."""
6+
7+
def __aiter__(self):
8+
...
9+
10+
async def __anext__(self):
11+
...
12+
13+
14+
MyClass().__aiter__()
15+
MyClass().__anext__()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[MAIN]
2+
py-version=3.9

0 commit comments

Comments
 (0)