Skip to content

Infer the args of an exception instance as a tuple of unknown contents - #3241

Open
ekanshul wants to merge 1 commit into
pylint-dev:mainfrom
ekanshul:exception-args-unknown-tuple
Open

Infer the args of an exception instance as a tuple of unknown contents#3241
ekanshul wants to merge 1 commit into
pylint-dev:mainfrom
ekanshul:exception-args-unknown-tuple

Conversation

@ekanshul

Copy link
Copy Markdown
Contributor

Type of Changes

Type
🐛 Bug fix
✨ New feature
🔨 Refactoring
📜 Docs

Description

ExceptionInstanceModel.attr_args returns a nodes.Tuple with no elements. Consumers read that as a known empty tuple: pylint reports unbalanced-tuple-unpacking with "right side has 0 values" for

class PackageNotFoundError(ModuleNotFoundError):
    @property
    def name(self):
        (name,) = self.args   # stdlib, importlib/metadata/__init__.py
        return name

and for first, second = exc.args inside an except block.

The arguments an exception was created with are not known from the instance alone, so args is now inferred as an instance of builtins.tuple (unknown contents, still iterable and subscriptable). This keeps the intent of #1749 (no assumption that the first argument is a str) while no longer claiming a length of zero. The three existing tests that asserted nodes.Tuple are updated and a regression test is added; pylint's functional suite passes against this branch (apart from unbalanced_tuple_unpacking, which fails identically on current astroid main in my environment because of the ctypes brain).

Closes pylint-dev/pylint#11312

…tents

``ExceptionInstanceModel.attr_args`` returned a ``nodes.Tuple`` with no
elements, which consumers read as a known empty tuple: pylint reported
``unbalanced-tuple-unpacking`` ("right side has 0 values") for
``(message,) = exc.args``. The arguments are not known from the instance
alone, so return an instance of ``builtins.tuple`` instead.

Closes pylint-dev/pylint#11312

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@DanielNoord DanielNoord left a comment

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.

Can't we set something on the Tuple node to indicate this as well?

@ekanshul

Copy link
Copy Markdown
Contributor Author

I tried that, and a marker on the Tuple node does not fix the false positive, because the consumers dispatch on the node type before they would ever look at the marker.

pylint reaches the message through VariablesChecker._nodes_to_unpack:

if isinstance(node, (nodes.Tuple, nodes.List, nodes.Set, *DICT_TYPES)):
    return node.itered()

A Tuple with no elts returns [], so the length is 0 and _check_unpacking reports "right side has 0 values". I patched attr_args to return nodes.Tuple(parent=self._instance) with an added unknown_contents = True attribute and ran it against pylint main:

# marker on nodes.Tuple
/tmp/t.py:4:8: W0632: Possible unbalanced tuple unpacking with sequence :
    left side has 1 label, right side has 0 values (unbalanced-tuple-unpacking)

# this PR (builtins.tuple instance)
(no messages)

The difference is which way each option fails. Returning an Instance is fail-safe: _nodes_to_unpack returns None for it, _check_unpacking falls through to its "is this iterable at all" branch, and a tuple instance is iterable, so nothing is reported. Any consumer that special-cases nodes.Tuple gets its existing unknown-value path for free. A marker is fail-open: every consumer has to be taught to check it, and until each one is, it still sees a Tuple of length 0. That includes released pylint versions running against a newer astroid.

There is also precedent for this shape a few lines down in the same class, where __traceback__ returns traceback_type.instantiate_class() rather than a node.

That said, if you want the marker as well as the type change, so that consumers can distinguish "tuple of unknown contents" from any other tuple instance, I am happy to add it. I would want it on the instance rather than on a Tuple node, since a Tuple node with unknown contents is not really a Tuple node. Let me know which you prefer.

@codspeed-hq

codspeed-hq Bot commented Aug 26, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 3 untouched benchmarks
⏩ 1 skipped benchmark1


Comparing ekanshul:exception-args-unknown-tuple (347687f) with main (94b7090)

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.

@DanielNoord DanielNoord left a comment

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.

@Pierre-Sassoulas @jacobtylerwalls What do you think of this change? I guess the explanation sort of makes sense?

@codecov

codecov Bot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.65%. Comparing base (94b7090) to head (347687f).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #3241   +/-   ##
=======================================
  Coverage   93.65%   93.65%           
=======================================
  Files          93       93           
  Lines       11613    11614    +1     
=======================================
+ Hits        10876    10877    +1     
  Misses        737      737           
Flag Coverage Δ
linux 93.51% <100.00%> (+<0.01%) ⬆️
pypy 93.65% <100.00%> (+<0.01%) ⬆️
windows 93.62% <100.00%> (+<0.01%) ⬆️

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

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

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.

False positive unbalanced-tuple-unpacking when unpacking an exception's args

2 participants