Skip to content

Commit 25e4f77

Browse files
bpo-31071: Avoid masking original TypeError in call with * unpacking (#2957)
when other arguments are passed.
1 parent 49b2734 commit 25e4f77

File tree

3 files changed

+52
-29
lines changed

3 files changed

+52
-29
lines changed

Lib/test/test_extcall.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@
163163
Traceback (most recent call last):
164164
...
165165
TypeError: myerror
166+
>>> g(*range(1), *(broken() for i in range(1)))
167+
Traceback (most recent call last):
168+
...
169+
TypeError: myerror
166170
167171
>>> class BrokenIterable1:
168172
... def __iter__(self):
@@ -172,6 +176,10 @@
172176
Traceback (most recent call last):
173177
...
174178
TypeError: myerror
179+
>>> g(*range(1), *BrokenIterable1())
180+
Traceback (most recent call last):
181+
...
182+
TypeError: myerror
175183
176184
>>> class BrokenIterable2:
177185
... def __iter__(self):
@@ -182,6 +190,10 @@
182190
Traceback (most recent call last):
183191
...
184192
TypeError: myerror
193+
>>> g(*range(1), *BrokenIterable2())
194+
Traceback (most recent call last):
195+
...
196+
TypeError: myerror
185197
186198
>>> class BrokenSequence:
187199
... def __getitem__(self, idx):
@@ -191,6 +203,10 @@
191203
Traceback (most recent call last):
192204
...
193205
TypeError: myerror
206+
>>> g(*range(1), *BrokenSequence())
207+
Traceback (most recent call last):
208+
...
209+
TypeError: myerror
194210
195211
Make sure that the function doesn't stomp the dictionary
196212
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Avoid masking original TypeError in call with * unpacking when other
2+
arguments are passed.

Python/ceval.c

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ static void format_exc_unbound(PyCodeObject *co, int oparg);
6666
static PyObject * unicode_concatenate(PyObject *, PyObject *,
6767
PyFrameObject *, const _Py_CODEUNIT *);
6868
static PyObject * special_lookup(PyObject *, _Py_Identifier *);
69+
static int check_args_iterable(PyObject *func, PyObject *vararg);
70+
static void format_kwargs_mapping_error(PyObject *func, PyObject *kwargs);
6971

7072
#define NAME_ERROR_MSG \
7173
"name '%.200s' is not defined"
@@ -2512,14 +2514,9 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
25122514
none_val = _PyList_Extend((PyListObject *)sum, PEEK(i));
25132515
if (none_val == NULL) {
25142516
if (opcode == BUILD_TUPLE_UNPACK_WITH_CALL &&
2515-
PyErr_ExceptionMatches(PyExc_TypeError)) {
2516-
PyObject *func = PEEK(1 + oparg);
2517-
PyErr_Format(PyExc_TypeError,
2518-
"%.200s%.200s argument after * "
2519-
"must be an iterable, not %.200s",
2520-
PyEval_GetFuncName(func),
2521-
PyEval_GetFuncDesc(func),
2522-
PEEK(i)->ob_type->tp_name);
2517+
PyErr_ExceptionMatches(PyExc_TypeError))
2518+
{
2519+
check_args_iterable(PEEK(1 + oparg), PEEK(i));
25232520
}
25242521
Py_DECREF(sum);
25252522
goto error;
@@ -2732,12 +2729,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
27322729
if (_PyDict_MergeEx(sum, arg, 2) < 0) {
27332730
PyObject *func = PEEK(2 + oparg);
27342731
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
2735-
PyErr_Format(PyExc_TypeError,
2736-
"%.200s%.200s argument after ** "
2737-
"must be a mapping, not %.200s",
2738-
PyEval_GetFuncName(func),
2739-
PyEval_GetFuncDesc(func),
2740-
arg->ob_type->tp_name);
2732+
format_kwargs_mapping_error(func, arg);
27412733
}
27422734
else if (PyErr_ExceptionMatches(PyExc_KeyError)) {
27432735
PyObject *exc, *val, *tb;
@@ -3390,13 +3382,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
33903382
* is not a mapping.
33913383
*/
33923384
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
3393-
func = SECOND();
3394-
PyErr_Format(PyExc_TypeError,
3395-
"%.200s%.200s argument after ** "
3396-
"must be a mapping, not %.200s",
3397-
PyEval_GetFuncName(func),
3398-
PyEval_GetFuncDesc(func),
3399-
kwargs->ob_type->tp_name);
3385+
format_kwargs_mapping_error(SECOND(), kwargs);
34003386
}
34013387
Py_DECREF(kwargs);
34023388
goto error;
@@ -3409,14 +3395,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
34093395
callargs = POP();
34103396
func = TOP();
34113397
if (!PyTuple_CheckExact(callargs)) {
3412-
if (Py_TYPE(callargs)->tp_iter == NULL &&
3413-
!PySequence_Check(callargs)) {
3414-
PyErr_Format(PyExc_TypeError,
3415-
"%.200s%.200s argument after * "
3416-
"must be an iterable, not %.200s",
3417-
PyEval_GetFuncName(func),
3418-
PyEval_GetFuncDesc(func),
3419-
callargs->ob_type->tp_name);
3398+
if (check_args_iterable(func, callargs) < 0) {
34203399
Py_DECREF(callargs);
34213400
goto error;
34223401
}
@@ -5179,6 +5158,32 @@ import_all_from(PyObject *locals, PyObject *v)
51795158
return err;
51805159
}
51815160

5161+
static int
5162+
check_args_iterable(PyObject *func, PyObject *args)
5163+
{
5164+
if (args->ob_type->tp_iter == NULL && !PySequence_Check(args)) {
5165+
PyErr_Format(PyExc_TypeError,
5166+
"%.200s%.200s argument after * "
5167+
"must be an iterable, not %.200s",
5168+
PyEval_GetFuncName(func),
5169+
PyEval_GetFuncDesc(func),
5170+
args->ob_type->tp_name);
5171+
return -1;
5172+
}
5173+
return 0;
5174+
}
5175+
5176+
static void
5177+
format_kwargs_mapping_error(PyObject *func, PyObject *kwargs)
5178+
{
5179+
PyErr_Format(PyExc_TypeError,
5180+
"%.200s%.200s argument after ** "
5181+
"must be a mapping, not %.200s",
5182+
PyEval_GetFuncName(func),
5183+
PyEval_GetFuncDesc(func),
5184+
kwargs->ob_type->tp_name);
5185+
}
5186+
51825187
static void
51835188
format_exc_check_arg(PyObject *exc, const char *format_str, PyObject *obj)
51845189
{

0 commit comments

Comments
 (0)