mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 21:51:50 +00:00 
			
		
		
		
	Issue #29029: Speed up processing positional arguments in
PyArg_ParseTupleAndKeywords(), _PyArg_ParseTupleAndKeywordsFast() and like.
This commit is contained in:
		
							parent
							
								
									f6b96c7bb5
								
							
						
					
					
						commit
						1741441649
					
				
					 1 changed files with 91 additions and 103 deletions
				
			
		
							
								
								
									
										194
									
								
								Python/getargs.c
									
										
									
									
									
								
							
							
						
						
									
										194
									
								
								Python/getargs.c
									
										
									
									
									
								
							|  | @ -1598,7 +1598,7 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, | ||||||
| { | { | ||||||
|     char msgbuf[512]; |     char msgbuf[512]; | ||||||
|     int levels[32]; |     int levels[32]; | ||||||
|     const char *fname, *msg, *custom_msg, *keyword; |     const char *fname, *msg, *custom_msg; | ||||||
|     int min = INT_MAX; |     int min = INT_MAX; | ||||||
|     int max = INT_MAX; |     int max = INT_MAX; | ||||||
|     int i, pos, len; |     int i, pos, len; | ||||||
|  | @ -1666,7 +1666,6 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, | ||||||
| 
 | 
 | ||||||
|     /* convert tuple args and keyword args in same loop, using kwlist to drive process */ |     /* convert tuple args and keyword args in same loop, using kwlist to drive process */ | ||||||
|     for (i = 0; i < len; i++) { |     for (i = 0; i < len; i++) { | ||||||
|         keyword = kwlist[i]; |  | ||||||
|         if (*format == '|') { |         if (*format == '|') { | ||||||
|             if (min != INT_MAX) { |             if (min != INT_MAX) { | ||||||
|                 PyErr_SetString(PyExc_SystemError, |                 PyErr_SetString(PyExc_SystemError, | ||||||
|  | @ -1720,26 +1719,17 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, | ||||||
|             return cleanreturn(0, &freelist); |             return cleanreturn(0, &freelist); | ||||||
|         } |         } | ||||||
|         if (!skip) { |         if (!skip) { | ||||||
|             current_arg = NULL; |             if (i < nargs) { | ||||||
|             if (nkwargs && i >= pos) { |  | ||||||
|                 current_arg = PyDict_GetItemString(kwargs, keyword); |  | ||||||
|                 if (!current_arg && PyErr_Occurred()) { |  | ||||||
|                     return cleanreturn(0, &freelist); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             if (current_arg) { |  | ||||||
|                 --nkwargs; |  | ||||||
|                 if (i < nargs) { |  | ||||||
|                     /* arg present in tuple and in dict */ |  | ||||||
|                     PyErr_Format(PyExc_TypeError, |  | ||||||
|                                  "Argument given by name ('%s') " |  | ||||||
|                                  "and position (%d)", |  | ||||||
|                                  keyword, i+1); |  | ||||||
|                     return cleanreturn(0, &freelist); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else if (i < nargs) |  | ||||||
|                 current_arg = PyTuple_GET_ITEM(args, i); |                 current_arg = PyTuple_GET_ITEM(args, i); | ||||||
|  |             } | ||||||
|  |             else if (nkwargs && i >= pos) { | ||||||
|  |                 current_arg = PyDict_GetItemString(kwargs, kwlist[i]); | ||||||
|  |                 if (current_arg) | ||||||
|  |                     --nkwargs; | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 current_arg = NULL; | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             if (current_arg) { |             if (current_arg) { | ||||||
|                 msg = convertitem(current_arg, &format, p_va, flags, |                 msg = convertitem(current_arg, &format, p_va, flags, | ||||||
|  | @ -1763,8 +1753,8 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, | ||||||
|                 } |                 } | ||||||
|                 else { |                 else { | ||||||
|                     PyErr_Format(PyExc_TypeError, "Required argument " |                     PyErr_Format(PyExc_TypeError, "Required argument " | ||||||
|                                 "'%s' (pos %d) not found", |                                  "'%s' (pos %d) not found", | ||||||
|                                 keyword, i+1); |                                  kwlist[i], i+1); | ||||||
|                     return cleanreturn(0, &freelist); |                     return cleanreturn(0, &freelist); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -1803,19 +1793,32 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, | ||||||
|         return cleanreturn(0, &freelist); |         return cleanreturn(0, &freelist); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /* make sure there are no extraneous keyword arguments */ |  | ||||||
|     if (nkwargs > 0) { |     if (nkwargs > 0) { | ||||||
|         PyObject *key, *value; |         PyObject *key; | ||||||
|         Py_ssize_t pos = 0; |         Py_ssize_t j; | ||||||
|         while (PyDict_Next(kwargs, &pos, &key, &value)) { |         /* make sure there are no arguments given by name and position */ | ||||||
|  |         for (i = pos; i < nargs; i++) { | ||||||
|  |             current_arg = PyDict_GetItemString(kwargs, kwlist[i]); | ||||||
|  |             if (current_arg) { | ||||||
|  |                 /* arg present in tuple and in dict */ | ||||||
|  |                 PyErr_Format(PyExc_TypeError, | ||||||
|  |                              "Argument given by name ('%s') " | ||||||
|  |                              "and position (%d)", | ||||||
|  |                              kwlist[i], i+1); | ||||||
|  |                 return cleanreturn(0, &freelist); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         /* make sure there are no extraneous keyword arguments */ | ||||||
|  |         j = 0; | ||||||
|  |         while (PyDict_Next(kwargs, &j, &key, NULL)) { | ||||||
|             int match = 0; |             int match = 0; | ||||||
|             if (!PyUnicode_Check(key)) { |             if (!PyUnicode_Check(key)) { | ||||||
|                 PyErr_SetString(PyExc_TypeError, |                 PyErr_SetString(PyExc_TypeError, | ||||||
|                                 "keywords must be strings"); |                                 "keywords must be strings"); | ||||||
|                 return cleanreturn(0, &freelist); |                 return cleanreturn(0, &freelist); | ||||||
|             } |             } | ||||||
|             for (i = 0; i < len; i++) { |             for (i = pos; i < len; i++) { | ||||||
|                 if (*kwlist[i] && _PyUnicode_EqualToASCIIString(key, kwlist[i])) { |                 if (_PyUnicode_EqualToASCIIString(key, kwlist[i])) { | ||||||
|                     match = 1; |                     match = 1; | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|  | @ -1963,10 +1966,13 @@ parser_clear(struct _PyArg_Parser *parser) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static PyObject* | static PyObject* | ||||||
| find_keyword(PyObject *kwnames, PyObject **kwstack, PyObject *key) | find_keyword(PyObject *kwargs, PyObject *kwnames, PyObject **kwstack, PyObject *key) | ||||||
| { | { | ||||||
|     Py_ssize_t i, nkwargs; |     Py_ssize_t i, nkwargs; | ||||||
| 
 | 
 | ||||||
|  |     if (kwargs != NULL) { | ||||||
|  |         return PyDict_GetItem(kwargs, key); | ||||||
|  |     } | ||||||
|     nkwargs = PyTuple_GET_SIZE(kwnames); |     nkwargs = PyTuple_GET_SIZE(kwnames); | ||||||
|     for (i=0; i < nkwargs; i++) { |     for (i=0; i < nkwargs; i++) { | ||||||
|         PyObject *kwname = PyTuple_GET_ITEM(kwnames, i); |         PyObject *kwname = PyTuple_GET_ITEM(kwnames, i); | ||||||
|  | @ -1978,7 +1984,7 @@ find_keyword(PyObject *kwnames, PyObject **kwstack, PyObject *key) | ||||||
|         } |         } | ||||||
|         if (!PyUnicode_Check(kwname)) { |         if (!PyUnicode_Check(kwname)) { | ||||||
|             /* ignore non-string keyword keys:
 |             /* ignore non-string keyword keys:
 | ||||||
|                an error will be raised above */ |                an error will be raised below */ | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|         if (_PyUnicode_EQ(kwname, key)) { |         if (_PyUnicode_EQ(kwname, key)) { | ||||||
|  | @ -2012,8 +2018,7 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, | ||||||
|     freelist.entries_malloced = 0; |     freelist.entries_malloced = 0; | ||||||
| 
 | 
 | ||||||
|     assert(kwargs == NULL || PyDict_Check(kwargs)); |     assert(kwargs == NULL || PyDict_Check(kwargs)); | ||||||
|     assert((kwargs != NULL || kwnames != NULL) |     assert(kwargs == NULL || kwnames == NULL); | ||||||
|            || (kwargs == NULL && kwnames == NULL)); |  | ||||||
|     assert(p_va != NULL); |     assert(p_va != NULL); | ||||||
| 
 | 
 | ||||||
|     if (parser == NULL) { |     if (parser == NULL) { | ||||||
|  | @ -2074,7 +2079,6 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, | ||||||
|     format = parser->format; |     format = parser->format; | ||||||
|     /* convert tuple args and keyword args in same loop, using kwtuple to drive process */ |     /* convert tuple args and keyword args in same loop, using kwtuple to drive process */ | ||||||
|     for (i = 0; i < len; i++) { |     for (i = 0; i < len; i++) { | ||||||
|         keyword = (i >= pos) ? PyTuple_GET_ITEM(kwtuple, i - pos) : NULL; |  | ||||||
|         if (*format == '|') { |         if (*format == '|') { | ||||||
|             format++; |             format++; | ||||||
|         } |         } | ||||||
|  | @ -2083,32 +2087,18 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, | ||||||
|         } |         } | ||||||
|         assert(!IS_END_OF_FORMAT(*format)); |         assert(!IS_END_OF_FORMAT(*format)); | ||||||
| 
 | 
 | ||||||
|         current_arg = NULL; |         if (i < nargs) { | ||||||
|         if (nkwargs && i >= pos) { |  | ||||||
|             if (kwargs != NULL) { |  | ||||||
|                 current_arg = PyDict_GetItem(kwargs, keyword); |  | ||||||
|                 if (!current_arg && PyErr_Occurred()) { |  | ||||||
|                     return cleanreturn(0, &freelist); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else { |  | ||||||
|                 current_arg = find_keyword(kwnames, kwstack, keyword); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         if (current_arg) { |  | ||||||
|             --nkwargs; |  | ||||||
|             if (i < nargs) { |  | ||||||
|                 /* arg present in tuple and in dict */ |  | ||||||
|                 PyErr_Format(PyExc_TypeError, |  | ||||||
|                              "Argument given by name ('%U') " |  | ||||||
|                              "and position (%d)", |  | ||||||
|                              keyword, i+1); |  | ||||||
|                 return cleanreturn(0, &freelist); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         else if (i < nargs) { |  | ||||||
|             current_arg = args[i]; |             current_arg = args[i]; | ||||||
|         } |         } | ||||||
|  |         else if (nkwargs && i >= pos) { | ||||||
|  |             keyword = PyTuple_GET_ITEM(kwtuple, i - pos); | ||||||
|  |             current_arg = find_keyword(kwargs, kwnames, kwstack, keyword); | ||||||
|  |             if (current_arg) | ||||||
|  |                 --nkwargs; | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             current_arg = NULL; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         if (current_arg) { |         if (current_arg) { | ||||||
|             msg = convertitem(current_arg, &format, p_va, flags, |             msg = convertitem(current_arg, &format, p_va, flags, | ||||||
|  | @ -2123,13 +2113,15 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, | ||||||
|         if (i < parser->min) { |         if (i < parser->min) { | ||||||
|             /* Less arguments than required */ |             /* Less arguments than required */ | ||||||
|             if (i < pos) { |             if (i < pos) { | ||||||
|  |                 Py_ssize_t min = Py_MIN(pos, parser->min); | ||||||
|                 PyErr_Format(PyExc_TypeError, |                 PyErr_Format(PyExc_TypeError, | ||||||
|                              "Function takes %s %d positional arguments" |                              "Function takes %s %d positional arguments" | ||||||
|                              " (%d given)", |                              " (%d given)", | ||||||
|                              (Py_MIN(pos, parser->min) < parser->max) ? "at least" : "exactly", |                              min < parser->max ? "at least" : "exactly", | ||||||
|                              Py_MIN(pos, parser->min), nargs); |                              min, nargs); | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|  |                 keyword = PyTuple_GET_ITEM(kwtuple, i - pos); | ||||||
|                 PyErr_Format(PyExc_TypeError, "Required argument " |                 PyErr_Format(PyExc_TypeError, "Required argument " | ||||||
|                              "'%U' (pos %d) not found", |                              "'%U' (pos %d) not found", | ||||||
|                              keyword, i+1); |                              keyword, i+1); | ||||||
|  | @ -2152,54 +2144,50 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, | ||||||
| 
 | 
 | ||||||
|     assert(IS_END_OF_FORMAT(*format) || (*format == '|') || (*format == '$')); |     assert(IS_END_OF_FORMAT(*format) || (*format == '|') || (*format == '$')); | ||||||
| 
 | 
 | ||||||
|     /* make sure there are no extraneous keyword arguments */ |  | ||||||
|     if (nkwargs > 0) { |     if (nkwargs > 0) { | ||||||
|         if (kwargs != NULL) { |         Py_ssize_t j; | ||||||
|             PyObject *key, *value; |         /* make sure there are no arguments given by name and position */ | ||||||
|             Py_ssize_t pos = 0; |         for (i = pos; i < nargs; i++) { | ||||||
|             while (PyDict_Next(kwargs, &pos, &key, &value)) { |             keyword = PyTuple_GET_ITEM(kwtuple, i - pos); | ||||||
|                 int match; |             current_arg = find_keyword(kwargs, kwnames, kwstack, keyword); | ||||||
|                 if (!PyUnicode_Check(key)) { |             if (current_arg) { | ||||||
|                     PyErr_SetString(PyExc_TypeError, |                 /* arg present in tuple and in dict */ | ||||||
|                                     "keywords must be strings"); |                 PyErr_Format(PyExc_TypeError, | ||||||
|                     return cleanreturn(0, &freelist); |                              "Argument given by name ('%U') " | ||||||
|                 } |                              "and position (%d)", | ||||||
|                 match = PySequence_Contains(kwtuple, key); |                              keyword, i+1); | ||||||
|                 if (match <= 0) { |                 return cleanreturn(0, &freelist); | ||||||
|                     if (!match) { |  | ||||||
|                         PyErr_Format(PyExc_TypeError, |  | ||||||
|                                      "'%U' is an invalid keyword " |  | ||||||
|                                      "argument for this function", |  | ||||||
|                                      key); |  | ||||||
|                     } |  | ||||||
|                     return cleanreturn(0, &freelist); |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         else { |         /* make sure there are no extraneous keyword arguments */ | ||||||
|             Py_ssize_t j, nkwargs; |         j = 0; | ||||||
|  |         while (1) { | ||||||
|  |             int match; | ||||||
|  |             if (kwargs != NULL) { | ||||||
|  |                 if (!PyDict_Next(kwargs, &j, &keyword, NULL)) | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 if (j >= PyTuple_GET_SIZE(kwnames)) | ||||||
|  |                     break; | ||||||
|  |                 keyword = PyTuple_GET_ITEM(kwnames, j); | ||||||
|  |                 j++; | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             nkwargs = PyTuple_GET_SIZE(kwnames); |             if (!PyUnicode_Check(keyword)) { | ||||||
|             for (j=0; j < nkwargs; j++) { |                 PyErr_SetString(PyExc_TypeError, | ||||||
|                 PyObject *key = PyTuple_GET_ITEM(kwnames, j); |                                 "keywords must be strings"); | ||||||
|                 int match; |                 return cleanreturn(0, &freelist); | ||||||
| 
 |             } | ||||||
|                 if (!PyUnicode_Check(key)) { |             match = PySequence_Contains(kwtuple, keyword); | ||||||
|                     PyErr_SetString(PyExc_TypeError, |             if (match <= 0) { | ||||||
|                                     "keywords must be strings"); |                 if (!match) { | ||||||
|                     return cleanreturn(0, &freelist); |                     PyErr_Format(PyExc_TypeError, | ||||||
|                 } |                                  "'%U' is an invalid keyword " | ||||||
| 
 |                                  "argument for this function", | ||||||
|                 match = PySequence_Contains(kwtuple, key); |                                  keyword); | ||||||
|                 if (match <= 0) { |  | ||||||
|                     if (!match) { |  | ||||||
|                         PyErr_Format(PyExc_TypeError, |  | ||||||
|                                      "'%U' is an invalid keyword " |  | ||||||
|                                      "argument for this function", |  | ||||||
|                                      key); |  | ||||||
|                     } |  | ||||||
|                     return cleanreturn(0, &freelist); |  | ||||||
|                 } |                 } | ||||||
|  |                 return cleanreturn(0, &freelist); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Serhiy Storchaka
						Serhiy Storchaka