Skip to content

Commit 8b0bb16

Browse files
clavedelunaPierre-Sassoulas
authored andcommitted
Fix astroid error for custom next method (#7622)
* short-circuit if next method doesnt have args * check for builtins.next qname * add inference confidence level
1 parent 6d66b18 commit 8b0bb16

File tree

4 files changed

+65
-14
lines changed

4 files changed

+65
-14
lines changed

doc/whatsnew/fragments/7610.bugfix

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixes edge case of custom method named ``next`` raised an astroid error.
2+
3+
Closes #7610

pylint/checkers/refactoring/refactoring_checker.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -979,7 +979,7 @@ def _check_stop_iteration_inside_generator(self, node: nodes.Raise) -> None:
979979
if not exc or not isinstance(exc, (bases.Instance, nodes.ClassDef)):
980980
return
981981
if self._check_exception_inherit_from_stopiteration(exc):
982-
self.add_message("stop-iteration-return", node=node)
982+
self.add_message("stop-iteration-return", node=node, confidence=INFERENCE)
983983

984984
@staticmethod
985985
def _check_exception_inherit_from_stopiteration(
@@ -1135,7 +1135,11 @@ def _looks_like_infinite_iterator(param: nodes.NodeNG) -> bool:
11351135
return
11361136

11371137
inferred = utils.safe_infer(node.func)
1138-
if getattr(inferred, "name", "") == "next":
1138+
1139+
if (
1140+
isinstance(inferred, nodes.FunctionDef)
1141+
and inferred.qname() == "builtins.next"
1142+
):
11391143
frame = node.frame(future=True)
11401144
# The next builtin can only have up to two
11411145
# positional arguments and no keyword arguments
@@ -1147,7 +1151,9 @@ def _looks_like_infinite_iterator(param: nodes.NodeNG) -> bool:
11471151
and not utils.node_ignores_exception(node, StopIteration)
11481152
and not _looks_like_infinite_iterator(node.args[0])
11491153
):
1150-
self.add_message("stop-iteration-return", node=node)
1154+
self.add_message(
1155+
"stop-iteration-return", node=node, confidence=INFERENCE
1156+
)
11511157

11521158
def _check_nested_blocks(
11531159
self,

tests/functional/s/stop_iteration_inside_generator.py

+46-4
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,20 @@
44
# pylint: disable=missing-docstring,invalid-name,import-error, try-except-raise, wrong-import-position,not-callable,raise-missing-from
55
import asyncio
66

7+
78
class RebornStopIteration(StopIteration):
89
"""
910
A class inheriting from StopIteration exception
1011
"""
1112

13+
1214
# This one is ok
1315
def gen_ok():
1416
yield 1
1517
yield 2
1618
yield 3
1719

20+
1821
# pylint should warn about this one
1922
# because of a direct raising of StopIteration inside generator
2023
def gen_stopiter():
@@ -23,6 +26,7 @@ def gen_stopiter():
2326
yield 3
2427
raise StopIteration # [stop-iteration-return]
2528

29+
2630
# pylint should warn about this one
2731
# because of a direct raising of an exception inheriting from StopIteration inside generator
2832
def gen_stopiterchild():
@@ -31,13 +35,15 @@ def gen_stopiterchild():
3135
yield 3
3236
raise RebornStopIteration # [stop-iteration-return]
3337

38+
3439
# pylint should warn here
3540
# because of the possibility that next raises a StopIteration exception
3641
def gen_next_raises_stopiter():
3742
g = gen_ok()
3843
while True:
3944
yield next(g) # [stop-iteration-return]
4045

46+
4147
# This one is the same as gen_next_raises_stopiter
4248
# but is ok because the next function is inside
4349
# a try/except block handling StopIteration
@@ -49,6 +55,7 @@ def gen_next_inside_try_except():
4955
except StopIteration:
5056
return
5157

58+
5259
# This one is the same as gen_next_inside_try_except
5360
# but is not ok because the next function is inside
5461
# a try/except block that don't handle StopIteration
@@ -60,6 +67,7 @@ def gen_next_inside_wrong_try_except():
6067
except ValueError:
6168
return
6269

70+
6371
# This one is the same as gen_next_inside_try_except
6472
# but is not ok because the next function is inside
6573
# a try/except block that handle StopIteration but reraise it
@@ -71,11 +79,13 @@ def gen_next_inside_wrong_try_except2():
7179
except StopIteration:
7280
raise StopIteration # [stop-iteration-return]
7381

82+
7483
# Those two last are ok
7584
def gen_in_for():
7685
for el in gen_ok():
7786
yield el
7887

88+
7989
def gen_yield_from():
8090
yield from gen_ok()
8191

@@ -84,7 +94,7 @@ def gen_dont_crash_on_no_exception():
8494
g = gen_ok()
8595
while True:
8696
try:
87-
yield next(g) # [stop-iteration-return]
97+
yield next(g) # [stop-iteration-return]
8898
except ValueError:
8999
raise
90100

@@ -97,7 +107,7 @@ def gen_dont_crash_on_uninferable():
97107

98108
# https://github.com/PyCQA/pylint/issues/1830
99109
def gen_next_with_sentinel():
100-
yield next([], 42) # No bad return
110+
yield next([], 42) # No bad return
101111

102112

103113
from itertools import count
@@ -113,6 +123,7 @@ def generator_using_next():
113123
class SomeClassWithNext:
114124
def next(self):
115125
return iter([1, 2, 3])
126+
116127
def some_gen(self):
117128
for value in self.next():
118129
yield value
@@ -122,8 +133,39 @@ def some_gen(self):
122133

123134

124135
def something_invalid():
125-
raise Exception('cannot iterate this')
136+
raise Exception("cannot iterate this")
126137

127138

128139
def invalid_object_passed_to_next():
129-
yield next(something_invalid()) # [stop-iteration-return]
140+
yield next(something_invalid()) # [stop-iteration-return]
141+
142+
143+
# pylint: disable=redefined-builtin,too-many-function-args
144+
def safeiter(it):
145+
"""Regression test for issue #7610 when ``next`` builtin is redefined"""
146+
147+
def next():
148+
while True:
149+
try:
150+
return next(it)
151+
except StopIteration:
152+
raise
153+
154+
it = iter(it)
155+
while True:
156+
yield next()
157+
158+
def other_safeiter(it):
159+
"""Regression test for issue #7610 when ``next`` builtin is redefined"""
160+
161+
def next(*things):
162+
print(*things)
163+
while True:
164+
try:
165+
return next(it)
166+
except StopIteration:
167+
raise
168+
169+
it = iter(it)
170+
while True:
171+
yield next(1, 2)
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
stop-iteration-return:24:4:24:23:gen_stopiter:Do not raise StopIteration in generator, use return statement instead:UNDEFINED
2-
stop-iteration-return:32:4:32:29:gen_stopiterchild:Do not raise StopIteration in generator, use return statement instead:UNDEFINED
3-
stop-iteration-return:39:14:39:21:gen_next_raises_stopiter:Do not raise StopIteration in generator, use return statement instead:UNDEFINED
4-
stop-iteration-return:59:18:59:25:gen_next_inside_wrong_try_except:Do not raise StopIteration in generator, use return statement instead:UNDEFINED
5-
stop-iteration-return:72:12:72:31:gen_next_inside_wrong_try_except2:Do not raise StopIteration in generator, use return statement instead:UNDEFINED
6-
stop-iteration-return:87:18:87:25:gen_dont_crash_on_no_exception:Do not raise StopIteration in generator, use return statement instead:UNDEFINED
7-
stop-iteration-return:129:10:129:35:invalid_object_passed_to_next:Do not raise StopIteration in generator, use return statement instead:UNDEFINED
1+
stop-iteration-return:27:4:27:23:gen_stopiter:Do not raise StopIteration in generator, use return statement instead:INFERENCE
2+
stop-iteration-return:36:4:36:29:gen_stopiterchild:Do not raise StopIteration in generator, use return statement instead:INFERENCE
3+
stop-iteration-return:44:14:44:21:gen_next_raises_stopiter:Do not raise StopIteration in generator, use return statement instead:INFERENCE
4+
stop-iteration-return:66:18:66:25:gen_next_inside_wrong_try_except:Do not raise StopIteration in generator, use return statement instead:INFERENCE
5+
stop-iteration-return:80:12:80:31:gen_next_inside_wrong_try_except2:Do not raise StopIteration in generator, use return statement instead:INFERENCE
6+
stop-iteration-return:97:18:97:25:gen_dont_crash_on_no_exception:Do not raise StopIteration in generator, use return statement instead:INFERENCE
7+
stop-iteration-return:140:10:140:35:invalid_object_passed_to_next:Do not raise StopIteration in generator, use return statement instead:INFERENCE

0 commit comments

Comments
 (0)