Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions astroid/interpreter/objectmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -817,8 +817,12 @@ def attr___dict__(self):

class ExceptionInstanceModel(InstanceModel):
@property
def attr_args(self) -> nodes.Tuple:
return nodes.Tuple(parent=self._instance)
def attr_args(self) -> bases.Instance:
# The arguments an exception was created with are not known from the
# instance alone, so ``args`` is a tuple of unknown contents rather
# than a known empty tuple.
builtins_ast_module = AstroidManager().builtins_module
return builtins_ast_module["tuple"].instantiate_class()

@property
def attr___traceback__(self):
Expand Down
5 changes: 5 additions & 0 deletions doc/whatsnew/fragments/11312.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
The ``args`` attribute of an exception instance is now inferred as a ``tuple``
instance of unknown contents instead of a known empty tuple, so unpacking
``exc.args`` no longer looks unbalanced.

Closes pylint-dev/pylint#11312
3 changes: 2 additions & 1 deletion tests/test_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -6193,7 +6193,8 @@ def test_subclass_of_exception(code) -> None:
inferred = next(extract_node(code).infer())
assert isinstance(inferred, Instance)
args = next(inferred.igetattr("args"))
assert isinstance(args, nodes.Tuple)
assert isinstance(args, Instance)
assert args.qname() == "builtins.tuple"


def test_ifexp_inference() -> None:
Expand Down
24 changes: 20 additions & 4 deletions tests/test_object_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,8 @@ def test_valueerror_py3() -> None:
""")
assert isinstance(ast_nodes, list)
args = next(ast_nodes[0].infer())
assert isinstance(args, nodes.Tuple)
assert isinstance(args, astroid.Instance)
assert args.qname() == "builtins.tuple"
tb = next(ast_nodes[1].infer())
# Python 3.11: If 'contextlib' is loaded, '__traceback__'
# could be set inside '__exit__' method in
Expand Down Expand Up @@ -836,9 +837,24 @@ def test_str_argument_not_required(self) -> None:
ast_node = builder.extract_node("""
BaseException() #@
""")
args: nodes.Tuple = next(ast_node.infer()).getattr("args")[0]
# BaseException doesn't have any required args, not even a string
assert not args.elts
args = next(ast_node.infer()).getattr("args")[0]
# BaseException doesn't have any required args, not even a string:
# ``args`` is a tuple whose contents are not known.
assert isinstance(args, astroid.Instance)
assert args.qname() == "builtins.tuple"

def test_args_unpacking_has_unknown_length(self) -> None:
"""``args`` is not a known empty tuple: unpacking it cannot be checked."""
ast_node = builder.extract_node("""
try:
pass
except ValueError as err:
err.args #@
""")
args = next(ast_node.infer())
assert isinstance(args, astroid.Instance)
assert args.qname() == "builtins.tuple"
assert not isinstance(args, nodes.Tuple)


@pytest.mark.parametrize("parentheses", (True, False))
Expand Down