Skip to content

Commit 2e338eb

Browse files
Log RecursionError out as warning during inference
Partner to pylint-dev/astroid#2385
1 parent fd6790b commit 2e338eb

File tree

15 files changed

+173
-32
lines changed

15 files changed

+173
-32
lines changed

doc/whatsnew/fragments/9139.bugfix

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Log RecursionError out as warning during inference.
2+
3+
Closes #9139

pylint/checkers/base/basic_checker.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,9 @@ def _check_using_constant_test(
374374
call_inferred = list(inferred.infer_call_result(node))
375375
except astroid.InferenceError:
376376
call_inferred = None
377+
except RecursionError:
378+
utils.warn_on_recursion_error()
379+
call_inferred = None
377380
if call_inferred:
378381
self.add_message(
379382
"missing-parentheses-for-call-in-test",
@@ -608,6 +611,9 @@ def is_iterable(internal_node: nodes.NodeNG) -> bool:
608611
value = next(default.infer())
609612
except astroid.InferenceError:
610613
continue
614+
except RecursionError:
615+
utils.warn_on_recursion_error()
616+
continue
611617

612618
if (
613619
isinstance(value, astroid.Instance)
@@ -839,6 +845,9 @@ def _check_reversed(self, node: nodes.Call) -> None:
839845
func = next(node.args[0].func.infer())
840846
except astroid.InferenceError:
841847
return
848+
except RecursionError:
849+
utils.warn_on_recursion_error()
850+
return
842851
if getattr(
843852
func, "name", None
844853
) == "iter" and utils.is_builtin_object(func):

pylint/checkers/base/comparison_checker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def _is_float_nan(node: nodes.NodeNG) -> bool:
148148
if isinstance(node, nodes.Call) and len(node.args) == 1:
149149
if (
150150
node.args[0].value.lower() == "nan"
151-
and node.inferred()[0].pytype() == "builtins.float"
151+
and utils.safe_infer(node).pytype() == "builtins.float"
152152
):
153153
return True
154154
return False

pylint/checkers/classes/class_checker.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,12 @@ def _has_different_parameters_default_value(
252252
if is_same_fn is None:
253253
# If the default value comparison is unhandled, assume the value is different
254254
return True
255-
if not is_same_fn(original_default, overridden_default):
256-
# Two args with same type but different values
257-
return True
255+
try:
256+
if not is_same_fn(original_default, overridden_default):
257+
# Two args with same type but different values
258+
return True
259+
except RecursionError:
260+
utils.warn_on_recursion_error()
258261
return False
259262

260263

@@ -409,6 +412,9 @@ def _has_data_descriptor(cls: nodes.ClassDef, attr: str) -> bool:
409412
except astroid.InferenceError:
410413
# Can't infer, avoid emitting a false positive in this case.
411414
return True
415+
except RecursionError:
416+
utils.warn_on_recursion_error()
417+
return True
412418
return False
413419

414420

@@ -435,6 +441,9 @@ def _called_in_methods(
435441
bound = next(call.func.infer())
436442
except (astroid.InferenceError, StopIteration):
437443
continue
444+
except RecursionError:
445+
utils.warn_on_recursion_error()
446+
continue
438447
if not isinstance(bound, astroid.BoundMethod):
439448
continue
440449
func_obj = bound._proxied
@@ -466,6 +475,9 @@ def _is_attribute_property(name: str, klass: nodes.ClassDef) -> bool:
466475
inferred = next(attr.infer())
467476
except astroid.InferenceError:
468477
continue
478+
except RecursionError:
479+
utils.warn_on_recursion_error()
480+
continue
469481
if isinstance(inferred, nodes.FunctionDef) and decorated_with_property(
470482
inferred
471483
):
@@ -483,7 +495,11 @@ def _is_attribute_property(name: str, klass: nodes.ClassDef) -> bool:
483495
def _has_same_layout_slots(
484496
slots: list[nodes.Const | None], assigned_value: nodes.Name
485497
) -> bool:
486-
inferred = next(assigned_value.infer())
498+
try:
499+
inferred = next(assigned_value.infer())
500+
except RecursionError:
501+
utils.warn_on_recursion_error()
502+
return False
487503
if isinstance(inferred, nodes.ClassDef):
488504
other_slots = inferred.slots()
489505
if all(
@@ -1278,6 +1294,9 @@ def visit_functiondef(self, node: nodes.FunctionDef) -> None:
12781294
inferred = next(inferred.infer_call_result(inferred))
12791295
except astroid.InferenceError:
12801296
return
1297+
except RecursionError:
1298+
utils.warn_on_recursion_error()
1299+
return
12811300
try:
12821301
if (
12831302
isinstance(inferred, (astroid.Instance, nodes.ClassDef))
@@ -1513,6 +1532,9 @@ def _check_slots(self, node: nodes.ClassDef) -> None:
15131532
self._check_slots_elt(elt, node)
15141533
except astroid.InferenceError:
15151534
continue
1535+
except RecursionError:
1536+
utils.warn_on_recursion_error()
1537+
continue
15161538
self._check_redefined_slots(node, slots, values)
15171539

15181540
def _check_redefined_slots(
@@ -2196,6 +2218,9 @@ def _check_init(self, node: nodes.FunctionDef, klass_node: nodes.ClassDef) -> No
21962218
)
21972219
except astroid.InferenceError:
21982220
continue
2221+
except RecursionError:
2222+
utils.warn_on_recursion_error()
2223+
continue
21992224
for klass, method in not_called_yet.items():
22002225
# Check if the init of the class that defines this init has already
22012226
# been called.
@@ -2350,4 +2375,7 @@ def _ancestors_to_call(
23502375
to_call[base_node] = init_node
23512376
except astroid.InferenceError:
23522377
continue
2378+
except RecursionError:
2379+
utils.warn_on_recursion_error()
2380+
continue
23532381
return to_call

pylint/checkers/classes/special_methods_checker.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
is_function_body_ellipsis,
2222
only_required_for_messages,
2323
safe_infer,
24+
warn_on_recursion_error,
2425
)
2526
from pylint.lint.pylinter import PyLinter
2627

@@ -44,13 +45,19 @@ def _safe_infer_call_result(
4445
return None # inference failed
4546
except StopIteration:
4647
return None # no values inferred
48+
except RecursionError:
49+
warn_on_recursion_error()
50+
return None
4751
try:
4852
next(inferit)
4953
return None # there is ambiguity on the inferred node
5054
except astroid.InferenceError:
5155
return None # there is some kind of ambiguity
5256
except StopIteration:
5357
return value
58+
except RecursionError:
59+
warn_on_recursion_error()
60+
return value
5461

5562

5663
class SpecialMethodsChecker(BaseChecker):

pylint/checkers/newstyle.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
has_known_bases,
1717
node_frame_class,
1818
only_required_for_messages,
19+
warn_on_recursion_error,
1920
)
2021
from pylint.typing import MessageDefinitionTuple
2122

@@ -107,6 +108,9 @@ def visit_functiondef(self, node: nodes.FunctionDef) -> None:
107108
supcls = call.args and next(call.args[0].infer(), None)
108109
except astroid.InferenceError:
109110
continue
111+
except RecursionError:
112+
warn_on_recursion_error()
113+
continue
110114

111115
# If the supcls is in the ancestors of klass super can be used to skip
112116
# a step in the mro() and get a method from a higher parent

pylint/checkers/refactoring/implicit_booleaness_checker.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ def visit_call(self, node: nodes.Call) -> None:
144144
except astroid.InferenceError:
145145
# Probably undefined-variable, abort check
146146
return
147+
except RecursionError:
148+
utils.warn_on_recursion_error()
149+
return
147150
mother_classes = self.base_names_of_instance(instance)
148151
affected_by_pep8 = any(
149152
t in mother_classes for t in ("str", "tuple", "list", "set")

pylint/checkers/refactoring/refactoring_checker.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from pylint import checkers
2121
from pylint.checkers import utils
2222
from pylint.checkers.base.basic_error_checker import _loop_exits_early
23-
from pylint.checkers.utils import node_frame_class
23+
from pylint.checkers.utils import node_frame_class, safe_infer
2424
from pylint.interfaces import HIGH, INFERENCE, Confidence
2525

2626
if TYPE_CHECKING:
@@ -1997,7 +1997,7 @@ def _is_node_return_ended(self, node: nodes.NodeNG) -> bool:
19971997
return True
19981998
if isinstance(node, nodes.Call):
19991999
try:
2000-
funcdef_node = node.func.inferred()[0]
2000+
funcdef_node = safe_infer(node.func)
20012001
if self._is_function_def_never_returning(funcdef_node):
20022002
return True
20032003
except astroid.InferenceError:

pylint/checkers/stdlib.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,8 @@ def _check_shallow_copy_environ(self, node: nodes.Call) -> None:
592592
confidence = INFERENCE
593593
try:
594594
inferred_args = arg.inferred()
595+
except RecursionError:
596+
utils.warn_on_recursion_error()
595597
except astroid.InferenceError:
596598
return
597599
for inferred in inferred_args:
@@ -713,6 +715,8 @@ def _check_lru_cache_decorators(self, node: nodes.FunctionDef) -> None:
713715
break
714716
except astroid.InferenceError:
715717
pass
718+
except RecursionError:
719+
utils.warn_on_recursion_error()
716720
for lru_cache_node in lru_cache_nodes:
717721
self.add_message(
718722
"method-cache-max-size-none",
@@ -767,6 +771,9 @@ def _check_datetime(self, node: nodes.NodeNG) -> None:
767771
inferred = next(node.infer())
768772
except astroid.InferenceError:
769773
return
774+
except RecursionError:
775+
utils.warn_on_recursion_error()
776+
return
770777
if isinstance(inferred, astroid.Instance) and inferred.qname() in {
771778
"_pydatetime.time",
772779
"datetime.time",

pylint/checkers/strings.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,9 @@ def _check_new_format(self, node: nodes.Call, func: bases.BoundMethod) -> None:
470470
strnode = next(func.bound.infer())
471471
except astroid.InferenceError:
472472
return
473+
except RecursionError:
474+
utils.warn_on_recursion_error()
475+
return
473476
if not (isinstance(strnode, nodes.Const) and isinstance(strnode.value, str)):
474477
return
475478
try:
@@ -634,6 +637,9 @@ def _check_new_format_specifiers(
634637
except astroid.InferenceError:
635638
# can't check further if we can't infer it
636639
break
640+
except RecursionError:
641+
utils.warn_on_recursion_error()
642+
break
637643

638644

639645
class StringConstantChecker(BaseTokenChecker, BaseRawFileChecker):

0 commit comments

Comments
 (0)