From 8357bd31cb80bd28045d29bc723d49825f8a5812 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 08:07:39 -0400 Subject: [PATCH 1/2] Fix pylint regression with invalid format strings (#2496) (#2497) Catch exceptions when calling string.format (cherry picked from commit 04f4f3ff34e67bf00e8567752af92fac6f66e173) Co-authored-by: Eric Vergnaud --- ChangeLog | 3 ++ astroid/nodes/node_classes.py | 31 ++++++++++-------- tests/test_inference.py | 60 ++++++++++++++++++++++++++--------- 3 files changed, 66 insertions(+), 28 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4560e5d2b7..c08b1cbf2c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,6 +13,9 @@ What's New in astroid 3.3.1? ============================ Release date: TBA +* Fix a crash introduced in 3.3.0 involving invalid format strings. + + Closes #2492 What's New in astroid 3.3.0? diff --git a/astroid/nodes/node_classes.py b/astroid/nodes/node_classes.py index c1c7af36da..1924c78eba 100644 --- a/astroid/nodes/node_classes.py +++ b/astroid/nodes/node_classes.py @@ -4687,19 +4687,24 @@ def _infer( uninferable_already_generated = True continue for value in self.value.infer(context, **kwargs): - if not isinstance(value, Const): - if not uninferable_already_generated: - yield util.Uninferable - uninferable_already_generated = True - continue - 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, - ) + 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: + yield util.Uninferable + uninferable_already_generated = True + continue MISSING_VALUE = "{MISSING_VALUE}" diff --git a/tests/test_inference.py b/tests/test_inference.py index 61378043c3..a8b11b1614 100644 --- a/tests/test_inference.py +++ b/tests/test_inference.py @@ -666,21 +666,6 @@ def test_fstring_inference(self) -> None: self.assertIsInstance(value_node, Const) self.assertEqual(value_node.value, "Hello John!") - def test_formatted_fstring_inference(self) -> None: - code = """ - width = 10 - precision = 4 - value = 12.34567 - result = f"result: {value:{width}.{precision}}!" - """ - ast = parse(code, __name__) - node = ast["result"] - inferred = node.inferred() - self.assertEqual(len(inferred), 1) - value_node = inferred[0] - self.assertIsInstance(value_node, Const) - self.assertEqual(value_node.value, "result: 12.35!") - def test_float_complex_ambiguity(self) -> None: code = ''' def no_conjugate_member(magic_flag): #@ @@ -5517,6 +5502,51 @@ class instance(object): self.assertIsInstance(inferred, Instance) +@pytest.mark.parametrize( + "code, result", + [ + # regular f-string + ( + """width = 10 +precision = 4 +value = 12.34567 +result = f"result: {value:{width}.{precision}}!" +""", + "result: 12.35!", + ), + # unsupported format + ( + """width = None +precision = 4 +value = 12.34567 +result = f"result: {value:{width}.{precision}}!" +""", + None, + ), + # unsupported value + ( + """width = 10 +precision = 4 +value = None +result = f"result: {value:{width}.{precision}}!" +""", + None, + ), + ], +) +def test_formatted_fstring_inference(code, result) -> None: + ast = parse(code, __name__) + node = ast["result"] + inferred = node.inferred() + assert len(inferred) == 1 + value_node = inferred[0] + if result is None: + assert value_node is util.Uninferable + else: + assert isinstance(value_node, Const) + assert value_node.value == result + + def test_augassign_recursion() -> None: """Make sure inference doesn't throw a RecursionError. From de58003b577c6111df1505b794f836fb1d643ef0 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Tue, 6 Aug 2024 08:13:34 -0400 Subject: [PATCH 2/2] Bump astroid to 3.3.1, update changelog --- ChangeLog | 8 +++++++- astroid/__pkginfo__.py | 2 +- tbump.toml | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index c08b1cbf2c..c67ec5dd18 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,10 +9,16 @@ Release date: TBA -What's New in astroid 3.3.1? +What's New in astroid 3.3.2? ============================ Release date: TBA + + +What's New in astroid 3.3.1? +============================ +Release date: 2024-08-06 + * Fix a crash introduced in 3.3.0 involving invalid format strings. Closes #2492 diff --git a/astroid/__pkginfo__.py b/astroid/__pkginfo__.py index fa3f8a4f47..f3dc052c63 100644 --- a/astroid/__pkginfo__.py +++ b/astroid/__pkginfo__.py @@ -2,5 +2,5 @@ # For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE # Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt -__version__ = "3.3.0" +__version__ = "3.3.1" version = __version__ diff --git a/tbump.toml b/tbump.toml index a25b9c889f..ce5d1f3ddd 100644 --- a/tbump.toml +++ b/tbump.toml @@ -1,7 +1,7 @@ github_url = "https://github.com/pylint-dev/astroid" [version] -current = "3.3.0" +current = "3.3.1" regex = ''' ^(?P0|[1-9]\d*) \.