mirror of
				https://github.com/python/cpython.git
				synced 2025-11-01 06:01:29 +00:00 
			
		
		
		
	bpo-31071: Avoid masking original TypeError in call with * unpacking (#2957)
when other arguments are passed.
This commit is contained in:
		
							parent
							
								
									49b2734bf1
								
							
						
					
					
						commit
						25e4f779d7
					
				
					 3 changed files with 52 additions and 29 deletions
				
			
		|  | @ -163,6 +163,10 @@ | ||||||
|     Traceback (most recent call last): |     Traceback (most recent call last): | ||||||
|       ... |       ... | ||||||
|     TypeError: myerror |     TypeError: myerror | ||||||
|  |     >>> g(*range(1), *(broken() for i in range(1))) | ||||||
|  |     Traceback (most recent call last): | ||||||
|  |       ... | ||||||
|  |     TypeError: myerror | ||||||
| 
 | 
 | ||||||
|     >>> class BrokenIterable1: |     >>> class BrokenIterable1: | ||||||
|     ...     def __iter__(self): |     ...     def __iter__(self): | ||||||
|  | @ -172,6 +176,10 @@ | ||||||
|     Traceback (most recent call last): |     Traceback (most recent call last): | ||||||
|       ... |       ... | ||||||
|     TypeError: myerror |     TypeError: myerror | ||||||
|  |     >>> g(*range(1), *BrokenIterable1()) | ||||||
|  |     Traceback (most recent call last): | ||||||
|  |       ... | ||||||
|  |     TypeError: myerror | ||||||
| 
 | 
 | ||||||
|     >>> class BrokenIterable2: |     >>> class BrokenIterable2: | ||||||
|     ...     def __iter__(self): |     ...     def __iter__(self): | ||||||
|  | @ -182,6 +190,10 @@ | ||||||
|     Traceback (most recent call last): |     Traceback (most recent call last): | ||||||
|       ... |       ... | ||||||
|     TypeError: myerror |     TypeError: myerror | ||||||
|  |     >>> g(*range(1), *BrokenIterable2()) | ||||||
|  |     Traceback (most recent call last): | ||||||
|  |       ... | ||||||
|  |     TypeError: myerror | ||||||
| 
 | 
 | ||||||
|     >>> class BrokenSequence: |     >>> class BrokenSequence: | ||||||
|     ...     def __getitem__(self, idx): |     ...     def __getitem__(self, idx): | ||||||
|  | @ -191,6 +203,10 @@ | ||||||
|     Traceback (most recent call last): |     Traceback (most recent call last): | ||||||
|       ... |       ... | ||||||
|     TypeError: myerror |     TypeError: myerror | ||||||
|  |     >>> g(*range(1), *BrokenSequence()) | ||||||
|  |     Traceback (most recent call last): | ||||||
|  |       ... | ||||||
|  |     TypeError: myerror | ||||||
| 
 | 
 | ||||||
| Make sure that the function doesn't stomp the dictionary | Make sure that the function doesn't stomp the dictionary | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | Avoid masking original TypeError in call with * unpacking when other | ||||||
|  | arguments are passed. | ||||||
|  | @ -66,6 +66,8 @@ static void format_exc_unbound(PyCodeObject *co, int oparg); | ||||||
| static PyObject * unicode_concatenate(PyObject *, PyObject *, | static PyObject * unicode_concatenate(PyObject *, PyObject *, | ||||||
|                                       PyFrameObject *, const _Py_CODEUNIT *); |                                       PyFrameObject *, const _Py_CODEUNIT *); | ||||||
| static PyObject * special_lookup(PyObject *, _Py_Identifier *); | static PyObject * special_lookup(PyObject *, _Py_Identifier *); | ||||||
|  | static int check_args_iterable(PyObject *func, PyObject *vararg); | ||||||
|  | static void format_kwargs_mapping_error(PyObject *func, PyObject *kwargs); | ||||||
| 
 | 
 | ||||||
| #define NAME_ERROR_MSG \ | #define NAME_ERROR_MSG \ | ||||||
|     "name '%.200s' is not defined" |     "name '%.200s' is not defined" | ||||||
|  | @ -2512,14 +2514,9 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) | ||||||
|                 none_val = _PyList_Extend((PyListObject *)sum, PEEK(i)); |                 none_val = _PyList_Extend((PyListObject *)sum, PEEK(i)); | ||||||
|                 if (none_val == NULL) { |                 if (none_val == NULL) { | ||||||
|                     if (opcode == BUILD_TUPLE_UNPACK_WITH_CALL && |                     if (opcode == BUILD_TUPLE_UNPACK_WITH_CALL && | ||||||
|                         PyErr_ExceptionMatches(PyExc_TypeError)) { |                         PyErr_ExceptionMatches(PyExc_TypeError)) | ||||||
|                         PyObject *func = PEEK(1 + oparg); |                     { | ||||||
|                         PyErr_Format(PyExc_TypeError, |                         check_args_iterable(PEEK(1 + oparg), PEEK(i)); | ||||||
|                                 "%.200s%.200s argument after * " |  | ||||||
|                                 "must be an iterable, not %.200s", |  | ||||||
|                                 PyEval_GetFuncName(func), |  | ||||||
|                                 PyEval_GetFuncDesc(func), |  | ||||||
|                                 PEEK(i)->ob_type->tp_name); |  | ||||||
|                     } |                     } | ||||||
|                     Py_DECREF(sum); |                     Py_DECREF(sum); | ||||||
|                     goto error; |                     goto error; | ||||||
|  | @ -2732,12 +2729,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) | ||||||
|                 if (_PyDict_MergeEx(sum, arg, 2) < 0) { |                 if (_PyDict_MergeEx(sum, arg, 2) < 0) { | ||||||
|                     PyObject *func = PEEK(2 + oparg); |                     PyObject *func = PEEK(2 + oparg); | ||||||
|                     if (PyErr_ExceptionMatches(PyExc_AttributeError)) { |                     if (PyErr_ExceptionMatches(PyExc_AttributeError)) { | ||||||
|                         PyErr_Format(PyExc_TypeError, |                         format_kwargs_mapping_error(func, arg); | ||||||
|                                 "%.200s%.200s argument after ** " |  | ||||||
|                                 "must be a mapping, not %.200s", |  | ||||||
|                                 PyEval_GetFuncName(func), |  | ||||||
|                                 PyEval_GetFuncDesc(func), |  | ||||||
|                                 arg->ob_type->tp_name); |  | ||||||
|                     } |                     } | ||||||
|                     else if (PyErr_ExceptionMatches(PyExc_KeyError)) { |                     else if (PyErr_ExceptionMatches(PyExc_KeyError)) { | ||||||
|                         PyObject *exc, *val, *tb; |                         PyObject *exc, *val, *tb; | ||||||
|  | @ -3390,13 +3382,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) | ||||||
|                          * is not a mapping. |                          * is not a mapping. | ||||||
|                          */ |                          */ | ||||||
|                         if (PyErr_ExceptionMatches(PyExc_AttributeError)) { |                         if (PyErr_ExceptionMatches(PyExc_AttributeError)) { | ||||||
|                             func = SECOND(); |                             format_kwargs_mapping_error(SECOND(), kwargs); | ||||||
|                             PyErr_Format(PyExc_TypeError, |  | ||||||
|                                          "%.200s%.200s argument after ** " |  | ||||||
|                                          "must be a mapping, not %.200s", |  | ||||||
|                                          PyEval_GetFuncName(func), |  | ||||||
|                                          PyEval_GetFuncDesc(func), |  | ||||||
|                                          kwargs->ob_type->tp_name); |  | ||||||
|                         } |                         } | ||||||
|                         Py_DECREF(kwargs); |                         Py_DECREF(kwargs); | ||||||
|                         goto error; |                         goto error; | ||||||
|  | @ -3409,14 +3395,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) | ||||||
|             callargs = POP(); |             callargs = POP(); | ||||||
|             func = TOP(); |             func = TOP(); | ||||||
|             if (!PyTuple_CheckExact(callargs)) { |             if (!PyTuple_CheckExact(callargs)) { | ||||||
|                 if (Py_TYPE(callargs)->tp_iter == NULL && |                 if (check_args_iterable(func, callargs) < 0) { | ||||||
|                         !PySequence_Check(callargs)) { |  | ||||||
|                     PyErr_Format(PyExc_TypeError, |  | ||||||
|                                  "%.200s%.200s argument after * " |  | ||||||
|                                  "must be an iterable, not %.200s", |  | ||||||
|                                  PyEval_GetFuncName(func), |  | ||||||
|                                  PyEval_GetFuncDesc(func), |  | ||||||
|                                  callargs->ob_type->tp_name); |  | ||||||
|                     Py_DECREF(callargs); |                     Py_DECREF(callargs); | ||||||
|                     goto error; |                     goto error; | ||||||
|                 } |                 } | ||||||
|  | @ -5179,6 +5158,32 @@ import_all_from(PyObject *locals, PyObject *v) | ||||||
|     return err; |     return err; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int | ||||||
|  | check_args_iterable(PyObject *func, PyObject *args) | ||||||
|  | { | ||||||
|  |     if (args->ob_type->tp_iter == NULL && !PySequence_Check(args)) { | ||||||
|  |         PyErr_Format(PyExc_TypeError, | ||||||
|  |                      "%.200s%.200s argument after * " | ||||||
|  |                      "must be an iterable, not %.200s", | ||||||
|  |                      PyEval_GetFuncName(func), | ||||||
|  |                      PyEval_GetFuncDesc(func), | ||||||
|  |                      args->ob_type->tp_name); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | format_kwargs_mapping_error(PyObject *func, PyObject *kwargs) | ||||||
|  | { | ||||||
|  |     PyErr_Format(PyExc_TypeError, | ||||||
|  |                  "%.200s%.200s argument after ** " | ||||||
|  |                  "must be a mapping, not %.200s", | ||||||
|  |                  PyEval_GetFuncName(func), | ||||||
|  |                  PyEval_GetFuncDesc(func), | ||||||
|  |                  kwargs->ob_type->tp_name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void | static void | ||||||
| format_exc_check_arg(PyObject *exc, const char *format_str, PyObject *obj) | format_exc_check_arg(PyObject *exc, const char *format_str, PyObject *obj) | ||||||
| { | { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Serhiy Storchaka
						Serhiy Storchaka