Skip to content

Commit 549dbac

Browse files
cdce8pgithub-actions[bot]
authored andcommitted
Revert CallContext change since it caused a RecursionError regression (#2000)
This reverts commit a0d219c (#1982). (cherry picked from commit 72f5afb)
1 parent b644c1e commit 549dbac

8 files changed

+51
-17
lines changed

ChangeLog

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ What's New in astroid 2.14.1?
1212
=============================
1313
Release date: TBA
1414

15+
* Revert ``CallContext`` change as it caused a ``RecursionError`` regression.
16+
1517

1618

1719
What's New in astroid 2.14.0?

astroid/brain/brain_typing.py

+34
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
Const,
2929
JoinedStr,
3030
Name,
31+
NodeNG,
3132
Subscript,
3233
Tuple,
3334
)
@@ -379,6 +380,36 @@ def infer_special_alias(
379380
return iter([class_def])
380381

381382

383+
def _looks_like_typing_cast(node: Call) -> bool:
384+
return isinstance(node, Call) and (
385+
isinstance(node.func, Name)
386+
and node.func.name == "cast"
387+
or isinstance(node.func, Attribute)
388+
and node.func.attrname == "cast"
389+
)
390+
391+
392+
def infer_typing_cast(
393+
node: Call, ctx: context.InferenceContext | None = None
394+
) -> Iterator[NodeNG]:
395+
"""Infer call to cast() returning same type as casted-from var."""
396+
if not isinstance(node.func, (Name, Attribute)):
397+
raise UseInferenceDefault
398+
399+
try:
400+
func = next(node.func.infer(context=ctx))
401+
except (InferenceError, StopIteration) as exc:
402+
raise UseInferenceDefault from exc
403+
if (
404+
not isinstance(func, FunctionDef)
405+
or func.qname() != "typing.cast"
406+
or len(node.args) != 2
407+
):
408+
raise UseInferenceDefault
409+
410+
return node.args[1].infer(context=ctx)
411+
412+
382413
AstroidManager().register_transform(
383414
Call,
384415
inference_tip(infer_typing_typevar_or_newtype),
@@ -387,6 +418,9 @@ def infer_special_alias(
387418
AstroidManager().register_transform(
388419
Subscript, inference_tip(infer_typing_attr), _looks_like_typing_subscript
389420
)
421+
AstroidManager().register_transform(
422+
Call, inference_tip(infer_typing_cast), _looks_like_typing_cast
423+
)
390424

391425
if PY39_PLUS:
392426
AstroidManager().register_transform(

astroid/context.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,13 @@ def __str__(self) -> str:
161161
class CallContext:
162162
"""Holds information for a call site."""
163163

164-
__slots__ = ("args", "keywords", "callee", "parent_call_context")
164+
__slots__ = ("args", "keywords", "callee")
165165

166166
def __init__(
167167
self,
168168
args: list[NodeNG],
169169
keywords: list[Keyword] | None = None,
170170
callee: NodeNG | None = None,
171-
parent_call_context: CallContext | None = None,
172171
):
173172
self.args = args # Call positional arguments
174173
if keywords:
@@ -177,9 +176,6 @@ def __init__(
177176
arg_value_pairs = []
178177
self.keywords = arg_value_pairs # Call keyword arguments
179178
self.callee = callee # Function being called
180-
self.parent_call_context = (
181-
parent_call_context # Parent CallContext for nested calls
182-
)
183179

184180

185181
def copy_context(context: InferenceContext | None) -> InferenceContext:

astroid/inference.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,7 @@ def infer_call(
274274
try:
275275
if hasattr(callee, "infer_call_result"):
276276
callcontext.callcontext = CallContext(
277-
args=self.args,
278-
keywords=self.keywords,
279-
callee=callee,
280-
parent_call_context=callcontext.callcontext,
277+
args=self.args, keywords=self.keywords, callee=callee
281278
)
282279
yield from callee.infer_call_result(caller=self, context=callcontext)
283280
except InferenceError:

astroid/protocols.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ def arguments_assigned_stmts(
470470
# reset call context/name
471471
callcontext = context.callcontext
472472
context = copy_context(context)
473-
context.callcontext = callcontext.parent_call_context
473+
context.callcontext = None
474474
args = arguments.CallSite(callcontext, context=context)
475475
return args.infer_argument(self.parent, node_name, context)
476476
return _arguments_infer_argname(self, node_name, context)

tests/unittest_brain.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -2160,6 +2160,13 @@ class A:
21602160
assert inferred.value == 42
21612161

21622162
def test_typing_cast_multiple_inference_calls(self) -> None:
2163+
"""Inference of an outer function should not store the result for cast.
2164+
2165+
https://github.com/PyCQA/pylint/issues/8074
2166+
2167+
Possible solution caused RecursionErrors with Python 3.8 and CPython + PyPy.
2168+
https://github.com/PyCQA/astroid/pull/1982
2169+
"""
21632170
ast_nodes = builder.extract_node(
21642171
"""
21652172
from typing import TypeVar, cast
@@ -2177,7 +2184,7 @@ def ident(var: T) -> T:
21772184

21782185
i1 = next(ast_nodes[1].infer())
21792186
assert isinstance(i1, nodes.Const)
2180-
assert i1.value == "Hello"
2187+
assert i1.value == 2 # should be "Hello"!
21812188

21822189

21832190
@pytest.mark.skipif(

tests/unittest_inference.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from astroid.arguments import CallSite
2424
from astroid.bases import BoundMethod, Instance, UnboundMethod, UnionType
2525
from astroid.builder import AstroidBuilder, _extract_single_node, extract_node, parse
26-
from astroid.const import IS_PYPY, PY38_PLUS, PY39_PLUS, PY310_PLUS
26+
from astroid.const import PY38_PLUS, PY39_PLUS, PY310_PLUS
2727
from astroid.context import InferenceContext
2828
from astroid.exceptions import (
2929
AstroidTypeError,
@@ -6934,9 +6934,6 @@ def test_imported_module_var_inferable3() -> None:
69346934
assert i_w_val.as_string() == "['w', 'v']"
69356935

69366936

6937-
@pytest.mark.skipif(
6938-
IS_PYPY, reason="Test run with coverage on PyPy sometimes raises a RecursionError"
6939-
)
69406937
def test_recursion_on_inference_tip() -> None:
69416938
"""Regression test for recursion in inference tip.
69426939

tests/unittest_inference_calls.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ def g(y):
146146
def test_inner_call_with_dynamic_argument() -> None:
147147
"""Test function where return value is the result of a separate function call,
148148
with a dynamic value passed to the inner function.
149+
150+
Currently, this is Uninferable.
149151
"""
150152
node = builder.extract_node(
151153
"""
@@ -161,8 +163,7 @@ def g(y):
161163
assert isinstance(node, nodes.NodeNG)
162164
inferred = node.inferred()
163165
assert len(inferred) == 1
164-
assert isinstance(inferred[0], nodes.Const)
165-
assert inferred[0].value == 3
166+
assert inferred[0] is Uninferable
166167

167168

168169
def test_method_const_instance_attr() -> None:

0 commit comments

Comments
 (0)