Skip to content

Commit

Permalink
Fix issue when inferring single-node or non-const JoinedStr (pylint-d…
Browse files Browse the repository at this point in the history
  • Loading branch information
ericvergnaud authored Sep 23, 2024
1 parent 706fcdb commit 8585ce6
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 18 deletions.
4 changes: 4 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 16 additions & 18 deletions astroid/nodes/node_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 31 additions & 0 deletions tests/test_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import pytest

from astroid import (
Assign,
Const,
Slice,
Uninferable,
Expand Down Expand Up @@ -7388,3 +7389,33 @@ 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)

0 comments on commit 8585ce6

Please sign in to comment.