Skip to content
Closed
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: 8 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ What's New in astroid 4.3.1?
============================
Release date: TBA

* Fix a crash when a ``namedtuple`` or ``Enum`` type name or field name
contains ``str.format`` markup, as in ``namedtuple("{0}", "abc")``. The name
is interpolated into an error message that astroid then reformats, so
building the message raised ``IndexError``. Inference now falls back to its
default.

Closes #3199



What's New in astroid 4.3.0?
Expand Down
10 changes: 7 additions & 3 deletions astroid/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,14 @@ def __init__(self, message: str = "", **kws: Any) -> None:
setattr(self, key, value)

def __str__(self) -> str:
# message is the template itself and may embed interpolated user text
# (e.g. a namedtuple typename), so keep it out of the fields and fall
# back to it verbatim rather than raising or reinterpreting it.
fields = {k: v for k, v in vars(self).items() if k != "message"}
try:
return self.message.format(**vars(self))
except ValueError:
return self.message # Return raw message if formatting fails
return self.message.format(**fields)
except (LookupError, ValueError, AttributeError, TypeError):
return self.message


class AstroidBuildingError(AstroidError):
Expand Down
10 changes: 10 additions & 0 deletions tests/brain/test_named_tuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,16 @@ def test_invalid_typename_does_not_crash_inference(self) -> None:
inferred = next(node.infer())
self.assertIs(util.Uninferable, inferred) # would raise ValueError

def test_format_placeholder_typename_does_not_crash_inference(self) -> None:
"""Reported in https://github.com/pylint-dev/astroid/issues/3199 as a crash."""
node = builder.extract_node("""
from collections import namedtuple
Tuple = namedtuple("{0}", "abc")
Tuple #@
""")
inferred = next(node.infer())
self.assertIs(util.Uninferable, inferred) # would raise IndexError

def test_keyword_typename_does_not_crash_inference(self) -> None:
node = builder.extract_node("""
from collections import namedtuple
Expand Down