Skip to content

Commit 8a2bab8

Browse files
committed
gh-145876: preserve AttributeError in dict unpacking
1 parent cd52172 commit 8a2bab8

3 files changed

Lines changed: 66 additions & 6 deletions

File tree

Lib/test/test_unpack_ex.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,29 @@
134134
...
135135
TypeError: 'list' object is not a mapping
136136
137+
>>> class MappingWithKeyErrors:
138+
... def __init__(self, *, keys_error=None, getitem_error=None):
139+
... self.keys_error = keys_error
140+
... self.getitem_error = getitem_error
141+
... def keys(self):
142+
... if self.keys_error is not None:
143+
... raise self.keys_error("error in keys")
144+
... return [1, 2, 3]
145+
... def __getitem__(self, key):
146+
... if self.getitem_error is not None:
147+
... raise self.getitem_error("error in __getitem__")
148+
... return key * 2
149+
150+
>>> {**MappingWithKeyErrors(keys_error=AttributeError)}
151+
Traceback (most recent call last):
152+
...
153+
AttributeError: error in keys
154+
155+
>>> {**MappingWithKeyErrors(getitem_error=AttributeError)}
156+
Traceback (most recent call last):
157+
...
158+
AttributeError: error in __getitem__
159+
137160
>>> len(eval("{" + ", ".join("**{{{}: {}}}".format(i, i)
138161
... for i in range(1000)) + "}"))
139162
1000

Python/bytecodes.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2244,9 +2244,24 @@ dummy_func(
22442244
if (err < 0) {
22452245
int matches = _PyErr_ExceptionMatches(tstate, PyExc_AttributeError);
22462246
if (matches) {
2247-
_PyErr_Format(tstate, PyExc_TypeError,
2248-
"'%.200s' object is not a mapping",
2249-
Py_TYPE(update_o)->tp_name);
2247+
PyObject *exc = PyErr_GetRaisedException();
2248+
PyObject *keys = NULL;
2249+
int has_keys = PyObject_GetOptionalAttr(update_o, &_Py_ID(keys), &keys);
2250+
2251+
if (has_keys < 0) {
2252+
Py_XDECREF(keys);
2253+
Py_DECREF(exc);
2254+
}
2255+
else if (has_keys == 0) {
2256+
Py_DECREF(exc);
2257+
_PyErr_Format(tstate, PyExc_TypeError,
2258+
"'%.200s' object is not a mapping",
2259+
Py_TYPE(update_o)->tp_name);
2260+
}
2261+
else {
2262+
Py_DECREF(keys);
2263+
PyErr_SetRaisedException(exc);
2264+
}
22502265
}
22512266
PyStackRef_CLOSE(update);
22522267
ERROR_IF(true);

Python/generated_cases.c.h

Lines changed: 25 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)