Skip to content

Infer list augmented concatenation with tuples (#1865) - #3357

Merged
Pierre-Sassoulas merged 2 commits into
pylint-dev:mainfrom
charan-rathore:fix-1865-list-iadd-tuple
Oct 7, 2026
Merged

Pierre-Sassoulas merged 2 commits into
pylint-dev:mainfrom
charan-rathore:fix-1865-list-iadd-tuple

Conversation

@charan-rathore

Copy link
Copy Markdown
Contributor

Supersedes #3331 (re-filed; the fork behind that PR was removed).

Type of Changes

Type
✓ 🐛 Bug fix

Description

Closes #1865

list.__iadd__ accepts a tuple, unlike list.__add__. The sequence inference handler currently only handles + between two lists or two tuples. As a result, values = []; values += (1, 2) infers as Uninferable.

Handle += when the left operand is a list and the right operand is a list or tuple. The result remains a list. Normal list-plus-tuple and tuple-plus-list operations remain unsupported, and tuple augmented addition keeps its existing fallback to +.

This reuses the existing size guard and element inference, including unknown placeholders for ambiguous elements. Tests cover empty and populated operands, invalid operations, ambiguous elements, repeated elements, and keeping the source list node unchanged.

Tests: 12 regression/control cases pass; full suite 2257 passed, 60 skipped, 15 xfailed. Ruff is clean and pylint rates the changed source file 10.00/10.

Scope: only list += with a list or tuple operand is handled. list + tuple, tuple + list, tuple += list and list += int stay unsupported on purpose, and other iterable operands (strings, sets, custom iterables) are out of scope. Tested on Python 3.10 only; other versions are left to CI.

@codspeed

codspeed Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 3 untouched benchmarks
⏩ 1 skipped benchmark1


Comparing charan-rathore:fix-1865-list-iadd-tuple (6908e1b) with main (cf52489)

Open in CodSpeed

Footnotes

  1. 1 benchmark was skipped, so the baseline result was used instead. If it was deleted from the codebase, click here and archive it to remove it from the performance reports. ↩

@charan-rathore

Copy link
Copy Markdown
Contributor Author

The CodSpeed flag looks like hosted-runner walltime noise rather than a real regression. Its own report warns about the unknown walltime environment on standard hosted runners, and 3 of the 4 benchmarks were skipped with baselines substituted.

Checked locally to be sure: ran the cold-lint subprocess benchmark 7 times each on base cf52489 and on this branch's head d1e7ccb. Base median 447ms (mean 460), head median 438ms (mean 460) - identical within noise.

That matches the shape of the change: the only added hot-path cost is one short-circuited isinstance chain inside tl_infer_binary_op, nothing at import time, so a cold-start regression of this size would be surprising. Happy to re-run anything if you'd like.

@codecov

codecov Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.04%. Comparing base (cf52489) to head (6908e1b).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #3357   +/-   ##
=======================================
  Coverage   91.04%   91.04%           
=======================================
  Files          93       93           
  Lines       11808    11808           
  Branches     1945     1945           
=======================================
  Hits        10751    10751           
  Misses        737      737           
  Partials      320      320           
Flag Coverage Δ
linux 90.82% <100.00%> (ø)
pypy 91.04% <100.00%> (ø)
windows 91.00% <100.00%> (ø)

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

Files with missing lines Coverage Δ
astroid/protocols.py 90.14% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Pierre-Sassoulas Pierre-Sassoulas left a comment

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.

Let's discuss the isinstance check, rest LGTM

Comment thread astroid/protocols.py Outdated
if isinstance(other, self.__class__) and operator == "+":
# list.__iadd__ accepts a tuple, unlike list.__add__; tuples have no __iadd__.
if (isinstance(other, self.__class__) and operator == "+") or (
isinstance(self, nodes.List)

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.

Suggested change
isinstance(self, nodes.List)

# list.__iadd__ accepts a tuple, unlike list.__add__; tuples have no __iadd__.
=> So this check is always true and we can remove it ?

"+=" reaches tl_infer_binary_op only for lists: tuples have no __iadd__,
so the augmented flow falls back to "+" for them. Pointed out in review.

Signed-off-by: Charan Rathore <[email protected]>
@charan-rathore

Copy link
Copy Markdown
Contributor Author

You're right, the check was always true. "+=" reaches tl_infer_binary_op only through the augmented flow after a successful iadd lookup, and tuples have no iadd, so for a Tuple the flow falls back to "+" before getting here. Dropped isinstance(self, nodes.List) and reworded the comment to say that instead.

Pushed as 6908e1b. Verified: the augassign parametrized tests (12) pass, tests/test_inference.py fully green (487 passed), and the rest of the suite is unchanged apart from pre-existing environment failures here (pydantic version and missing gi libs, failing identically without this change). black and ruff clean on the touched file.

@Pierre-Sassoulas
Pierre-Sassoulas merged commit 81348e0 into pylint-dev:main Oct 7, 2026
42 checks passed
@charan-rathore
charan-rathore deleted the fix-1865-list-iadd-tuple branch October 7, 2026 09:28
charan-rathore added a commit to charan-rathore/charan-rathore that referenced this pull request Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Enhancement ✨ Improvement to a component inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Augmented assignment on a list with a tuple value doesn't work

2 participants