Skip to content

Wrong inference for the str(const)#2995

Open
jkmnt wants to merge 6 commits into
pylint-dev:mainfrom
jkmnt:bug-2994
Open

Wrong inference for the str(const)#2995
jkmnt wants to merge 6 commits into
pylint-dev:mainfrom
jkmnt:bug-2994

Conversation

@jkmnt
Copy link
Copy Markdown
Contributor

@jkmnt jkmnt commented Mar 5, 2026

This PR fixes the wrong inference for the str(a), then the a is inferred to be Const.

Closes #2994

I preserved returning Const("") in other cases. Maybe it should raise UseInferenceDefault like the infer_int does ?

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.40%. Comparing base (85226dd) to head (d6e686b).
⚠️ Report is 43 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2995      +/-   ##
==========================================
+ Coverage   93.27%   93.40%   +0.12%     
==========================================
  Files          92       92              
  Lines       11348    11323      -25     
==========================================
- Hits        10585    10576       -9     
+ Misses        763      747      -16     
Flag Coverage Δ
linux 93.27% <100.00%> (+0.13%) ⬆️
pypy 93.40% <100.00%> (+0.12%) ⬆️
windows 93.37% <100.00%> (+0.11%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
astroid/brain/brain_builtin_inference.py 93.30% <100.00%> (+0.61%) ⬆️

... and 10 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.


if call.positional_arguments:
try:
first_value = next(call.positional_arguments[0].infer(context=context))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why only use the first value? If we infer multiple values I don't think we should default to the first one.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other functions in builtin brain seems to do the same.

Do you mean infer_str should return an interator, e.g. inference result of str(unknown if unknown else "a") would be [Const(""), Const("a")] ?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other functions in builtin brain seems to do the same.

Unfortunately not all existing code is of the same quality 😅

As for the solution, I'm not sure to be honest. I think it would probably be best to only return a Const with a value if we can be 100% sure it is that value, and otherwise keep falling back.

Something like if all(v == first_value for v in node.infer(): value = first_value. If you get what I mean 😅

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good solution. Added just that in the last commit.

return nodes.Const("")

if isinstance(first_value, nodes.Const):
return nodes.Const(str(first_value.value))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this face similar issues as fixed in #2971? 7**10000 to str will raise an exception

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I missed the str() may fail

Copy link
Copy Markdown
Collaborator

@DanielNoord DanielNoord left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Pierre-Sassoulas @jacobtylerwalls You want to review this? It LGTM!

Copy link
Copy Markdown
Member

@jacobtylerwalls jacobtylerwalls left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR 👍
Can you add a changelog?

Comment on lines +871 to +872
if len(candidates) == 1:
return nodes.Const(candidates.pop())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like an unnecessary source of indeterminism.

return fallback

try:
candidates.add(str(inferred.value))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we only run str() if the value is a builtin? I wouldn't want a more involved str() implementation from user code to run (we should keep pylint static).

Comment thread tests/brain/test_brain.py
str(2 + 2) #@
str() #@
str(int) #@
str(2 if unknown() else 3) #@
Copy link
Copy Markdown
Member

@jacobtylerwalls jacobtylerwalls Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add a test ensuring user __str__ doesn't run:

class StrWillFail:
    def __str__(self):
        raise RuntimeError

str(StrWillFail()) #@

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wrong inference for str(const)

3 participants