From 56c15b1d80f4d7743b29501d40834a0747413f25 Mon Sep 17 00:00:00 2001 From: Eric Vergnaud Date: Mon, 23 Sep 2024 17:37:31 +0200 Subject: [PATCH 1/3] fix issue when inferring single-node or non-const JoinedStr --- astroid/nodes/node_classes.py | 34 ++++++++++++++++------------------ tests/test_inference.py | 30 +++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/astroid/nodes/node_classes.py b/astroid/nodes/node_classes.py index 18df0d7e7b..91d7b0fa49 100644 --- a/astroid/nodes/node_classes.py +++ b/astroid/nodes/node_classes.py @@ -4685,32 +4685,30 @@ def get_children(self): def _infer( self, context: InferenceContext | None = None, **kwargs: Any ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]: - if self.format_spec is None: - yield from self.value.infer(context, **kwargs) - return + format_specs = Const("") if self.format_spec is None else self.format_spec uninferable_already_generated = False - for format_spec in self.format_spec.infer(context, **kwargs): + for format_spec in format_specs.infer(context, **kwargs): if not isinstance(format_spec, Const): if not uninferable_already_generated: yield util.Uninferable uninferable_already_generated = True continue for value in self.value.infer(context, **kwargs): + value_to_format = value if isinstance(value, Const): - try: - formatted = format(value.value, format_spec.value) - yield Const( - formatted, - lineno=self.lineno, - col_offset=self.col_offset, - end_lineno=self.end_lineno, - end_col_offset=self.end_col_offset, - ) - continue - except (ValueError, TypeError): - # happens when format_spec.value is invalid - pass # fall through - if not uninferable_already_generated: + value_to_format = value.value + try: + formatted = format(value_to_format, format_spec.value) + yield Const( + formatted, + lineno=self.lineno, + col_offset=self.col_offset, + end_lineno=self.end_lineno, + end_col_offset=self.end_col_offset, + ) + continue + except (ValueError, TypeError): + # happens when format_spec.value is invalid yield util.Uninferable uninferable_already_generated = True continue diff --git a/tests/test_inference.py b/tests/test_inference.py index daa5613565..0f28a55739 100644 --- a/tests/test_inference.py +++ b/tests/test_inference.py @@ -27,7 +27,7 @@ nodes, objects, test_utils, - util, + util, Assign, ) from astroid import decorators as decoratorsmod from astroid.arguments import CallSite @@ -7388,3 +7388,31 @@ def test_empty_format_spec() -> None: assert isinstance(node, nodes.JoinedStr) assert list(node.infer()) == [util.Uninferable] + + +@pytest.mark.parametrize( + "source, expected", + [ + (""" +class Cls: + # pylint: disable=too-few-public-methods + pass + +c_obj = Cls() + +s1 = f'{c_obj!r}' #@ +""", '<__main__.Cls'), + ("s1 = f'{5}' #@", "5") + ] +) +def test_joined_str_returns_string(source, expected) -> None: + """Regression test for https://github.com/pylint-dev/pylint/issues/9947.""" + node = extract_node(source) + assert isinstance(node, Assign) + target = node.targets[0] + assert target + inferred = list(target.inferred()) + assert len(inferred) == 1 + assert isinstance(inferred[0], Const) + inferred[0].value.startswith(expected) + From f58951f20ec408d729935b1457f794564ee16cb7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:39:46 +0000 Subject: [PATCH 2/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_inference.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/test_inference.py b/tests/test_inference.py index 0f28a55739..acc8a2b1f8 100644 --- a/tests/test_inference.py +++ b/tests/test_inference.py @@ -19,6 +19,7 @@ import pytest from astroid import ( + Assign, Const, Slice, Uninferable, @@ -27,7 +28,7 @@ nodes, objects, test_utils, - util, Assign, + util, ) from astroid import decorators as decoratorsmod from astroid.arguments import CallSite @@ -7393,7 +7394,8 @@ def test_empty_format_spec() -> None: @pytest.mark.parametrize( "source, expected", [ - (""" + ( + """ class Cls: # pylint: disable=too-few-public-methods pass @@ -7401,9 +7403,11 @@ class Cls: c_obj = Cls() s1 = f'{c_obj!r}' #@ -""", '<__main__.Cls'), - ("s1 = f'{5}' #@", "5") - ] +""", + "<__main__.Cls", + ), + ("s1 = f'{5}' #@", "5"), + ], ) def test_joined_str_returns_string(source, expected) -> None: """Regression test for https://github.com/pylint-dev/pylint/issues/9947.""" @@ -7415,4 +7419,3 @@ def test_joined_str_returns_string(source, expected) -> None: assert len(inferred) == 1 assert isinstance(inferred[0], Const) inferred[0].value.startswith(expected) - From 1cfeaa85fd768217fdfa45abd0735a256c61ea1b Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Mon, 23 Sep 2024 13:41:51 -0400 Subject: [PATCH 3/3] Add changelog --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index 1f6ee6d560..d5f5207030 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,6 +13,10 @@ What's New in astroid 3.3.4? ============================ Release date: TBA +* Fix regression with f-string inference. + + Closes pylint-dev/pylint#9947 + * Fix bug with ``manager.clear_cache()`` not fully clearing cache Refs https://github.com/pylint-dev/pylint/pull/9932#issuecomment-2364985551