Skip to content

Commit ea83793

Browse files
committed
Patch #1691070 from Roger Upole: Speed up PyArg_ParseTupleAndKeywords() and improve error msg
My tests don't show the promised speed up of 10%. The code is as fast as the old code for simple cases and slightly faster for complex cases with several of args and kwargs. But the patch simplifies the code, too.
1 parent ca37661 commit ea83793

3 files changed

Lines changed: 177 additions & 155 deletions

File tree

Lib/test/test_getargs2.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import unittest
22
from test import test_support
3+
from _testcapi import getargs_keywords
34

45
import warnings
56
warnings.filterwarnings("ignore",
@@ -248,9 +249,57 @@ def __getitem__(self, n):
248249
raise ValueError
249250
self.assertRaises(TypeError, getargs_tuple, 1, seq())
250251

252+
class Keywords_TestCase(unittest.TestCase):
253+
def test_positional_args(self):
254+
# using all positional args
255+
self.assertEquals(
256+
getargs_keywords((1,2), 3, (4,(5,6)), (7,8,9), 10),
257+
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
258+
)
259+
def test_mixed_args(self):
260+
# positional and keyword args
261+
self.assertEquals(
262+
getargs_keywords((1,2), 3, (4,(5,6)), arg4=(7,8,9), arg5=10),
263+
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
264+
)
265+
def test_keyword_args(self):
266+
# all keywords
267+
self.assertEquals(
268+
getargs_keywords(arg1=(1,2), arg2=3, arg3=(4,(5,6)), arg4=(7,8,9), arg5=10),
269+
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
270+
)
271+
def test_optional_args(self):
272+
# missing optional keyword args, skipping tuples
273+
self.assertEquals(
274+
getargs_keywords(arg1=(1,2), arg2=3, arg5=10),
275+
(1, 2, 3, -1, -1, -1, -1, -1, -1, 10)
276+
)
277+
def test_required_args(self):
278+
# required arg missing
279+
try:
280+
getargs_keywords(arg1=(1,2))
281+
except TypeError, err:
282+
self.assertEquals(str(err), "Required argument 'arg2' (pos 2) not found")
283+
else:
284+
self.fail('TypeError should have been raised')
285+
def test_too_many_args(self):
286+
try:
287+
getargs_keywords((1,2),3,(4,(5,6)),(7,8,9),10,111)
288+
except TypeError, err:
289+
self.assertEquals(str(err), "function takes at most 5 arguments (6 given)")
290+
else:
291+
self.fail('TypeError should have been raised')
292+
def test_invalid_keyword(self):
293+
# extraneous keyword arg
294+
try:
295+
getargs_keywords((1,2),3,arg5=10,arg666=666)
296+
except TypeError, err:
297+
self.assertEquals(str(err), "'arg666' is an invalid keyword argument for this function")
298+
else:
299+
self.fail('TypeError should have been raised')
251300

252301
def test_main():
253-
tests = [Signed_TestCase, Unsigned_TestCase, Tuple_TestCase]
302+
tests = [Signed_TestCase, Unsigned_TestCase, Tuple_TestCase, Keywords_TestCase]
254303
try:
255304
from _testcapi import getargs_L, getargs_K
256305
except ImportError:

Modules/_testcapimodule.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,22 @@ getargs_tuple(PyObject *self, PyObject *args)
306306
return Py_BuildValue("iii", a, b, c);
307307
}
308308

309+
/* test PyArg_ParseTupleAndKeywords */
310+
static PyObject *getargs_keywords(PyObject *self, PyObject *args, PyObject *kwargs)
311+
{
312+
static char *keywords[] = {"arg1","arg2","arg3","arg4","arg5", NULL};
313+
static char *fmt="(ii)i|(i(ii))(iii)i";
314+
int int_args[10]={-1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
315+
316+
if (!PyArg_ParseTupleAndKeywords(args, kwargs, fmt, keywords,
317+
&int_args[0], &int_args[1], &int_args[2], &int_args[3], &int_args[4],
318+
&int_args[5], &int_args[6], &int_args[7], &int_args[8], &int_args[9]))
319+
return NULL;
320+
return Py_BuildValue("iiiiiiiiii",
321+
int_args[0], int_args[1], int_args[2], int_args[3], int_args[4],
322+
int_args[5], int_args[6], int_args[7], int_args[8], int_args[9]);
323+
}
324+
309325
/* Functions to call PyArg_ParseTuple with integer format codes,
310326
and return the result.
311327
*/
@@ -732,6 +748,8 @@ static PyMethodDef TestMethods[] = {
732748
PyDoc_STR("This is a pretty normal docstring.")},
733749

734750
{"getargs_tuple", getargs_tuple, METH_VARARGS},
751+
{"getargs_keywords", (PyCFunction)getargs_keywords,
752+
METH_VARARGS|METH_KEYWORDS},
735753
{"getargs_b", getargs_b, METH_VARARGS},
736754
{"getargs_B", getargs_B, METH_VARARGS},
737755
{"getargs_H", getargs_H, METH_VARARGS},

0 commit comments

Comments
 (0)