Skip to content

Commit

Permalink
bpo-30184: Add tests for invalid use of PyArg_ParseTupleAndKeywords. (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
serhiy-storchaka committed May 3, 2017
1 parent feec3dc commit 5f161fd
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 20 deletions.
59 changes: 41 additions & 18 deletions Lib/test/test_capi.py
Expand Up @@ -490,9 +490,8 @@ def test_skipitem(self):
# test the format unit when not skipped
format = c + "i"
try:
# (note: the format string must be bytes!)
_testcapi.parse_tuple_and_keywords(tuple_1, dict_b,
format.encode("ascii"), keywords)
format, keywords)
when_not_skipped = False
except SystemError as e:
s = "argument 1 (impossible<bad format char>)"
Expand All @@ -504,7 +503,7 @@ def test_skipitem(self):
optional_format = "|" + format
try:
_testcapi.parse_tuple_and_keywords(empty_tuple, dict_b,
optional_format.encode("ascii"), keywords)
optional_format, keywords)
when_skipped = False
except SystemError as e:
s = "impossible<bad format char>: '{}'".format(format)
Expand All @@ -517,40 +516,64 @@ def test_skipitem(self):
self.assertIs(when_skipped, when_not_skipped, message)

def test_parse_tuple_and_keywords(self):
# parse_tuple_and_keywords error handling tests
# Test handling errors in the parse_tuple_and_keywords helper itself
self.assertRaises(TypeError, _testcapi.parse_tuple_and_keywords,
(), {}, 42, [])
self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
(), {}, b'', 42)
(), {}, '', 42)
self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
(), {}, b'', [''] * 42)
(), {}, '', [''] * 42)
self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
(), {}, b'', [42])
(), {}, '', [42])

def test_bad_use(self):
# Test handling invalid format and keywords in
# PyArg_ParseTupleAndKeywords()
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
(1,), {}, '||O', ['a'])
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
(1, 2), {}, '|O|O', ['a', 'b'])
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
(), {'a': 1}, '$$O', ['a'])
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
(), {'a': 1, 'b': 2}, '$O$O', ['a', 'b'])
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
(), {'a': 1}, '$|O', ['a'])
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
(), {'a': 1, 'b': 2}, '$O|O', ['a', 'b'])
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
(1,), {}, '|O', ['a', 'b'])
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
(1,), {}, '|OO', ['a'])
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
(), {}, '|$O', [''])
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
(), {}, '|OO', ['a', ''])

def test_positional_only(self):
parse = _testcapi.parse_tuple_and_keywords

parse((1, 2, 3), {}, b'OOO', ['', '', 'a'])
parse((1, 2), {'a': 3}, b'OOO', ['', '', 'a'])
parse((1, 2, 3), {}, 'OOO', ['', '', 'a'])
parse((1, 2), {'a': 3}, 'OOO', ['', '', 'a'])
with self.assertRaisesRegex(TypeError,
r'function takes at least 2 positional arguments \(1 given\)'):
parse((1,), {'a': 3}, b'OOO', ['', '', 'a'])
parse((1,), {}, b'O|OO', ['', '', 'a'])
parse((1,), {'a': 3}, 'OOO', ['', '', 'a'])
parse((1,), {}, 'O|OO', ['', '', 'a'])
with self.assertRaisesRegex(TypeError,
r'function takes at least 1 positional arguments \(0 given\)'):
parse((), {}, b'O|OO', ['', '', 'a'])
parse((1, 2), {'a': 3}, b'OO$O', ['', '', 'a'])
parse((), {}, 'O|OO', ['', '', 'a'])
parse((1, 2), {'a': 3}, 'OO$O', ['', '', 'a'])
with self.assertRaisesRegex(TypeError,
r'function takes exactly 2 positional arguments \(1 given\)'):
parse((1,), {'a': 3}, b'OO$O', ['', '', 'a'])
parse((1,), {}, b'O|O$O', ['', '', 'a'])
parse((1,), {'a': 3}, 'OO$O', ['', '', 'a'])
parse((1,), {}, 'O|O$O', ['', '', 'a'])
with self.assertRaisesRegex(TypeError,
r'function takes at least 1 positional arguments \(0 given\)'):
parse((), {}, b'O|O$O', ['', '', 'a'])
parse((), {}, 'O|O$O', ['', '', 'a'])
with self.assertRaisesRegex(SystemError, r'Empty parameter name after \$'):
parse((1,), {}, b'O|$OO', ['', '', 'a'])
parse((1,), {}, 'O|$OO', ['', '', 'a'])
with self.assertRaisesRegex(SystemError, 'Empty keyword'):
parse((1,), {}, b'O|OO', ['', 'a', ''])
parse((1,), {}, 'O|OO', ['', 'a', ''])


@unittest.skipUnless(threading, 'Threading required for this test.')
Expand Down
4 changes: 2 additions & 2 deletions Modules/_testcapimodule.c
Expand Up @@ -1560,7 +1560,7 @@ parse_tuple_and_keywords(PyObject *self, PyObject *args)
{
PyObject *sub_args;
PyObject *sub_kwargs;
char *sub_format;
const char *sub_format;
PyObject *sub_keywords;

Py_ssize_t i, size;
Expand All @@ -1573,7 +1573,7 @@ parse_tuple_and_keywords(PyObject *self, PyObject *args)

double buffers[8][4]; /* double ensures alignment where necessary */

if (!PyArg_ParseTuple(args, "OOyO:parse_tuple_and_keywords",
if (!PyArg_ParseTuple(args, "OOsO:parse_tuple_and_keywords",
&sub_args, &sub_kwargs,
&sub_format, &sub_keywords))
return NULL;
Expand Down

0 comments on commit 5f161fd

Please sign in to comment.