bpo-34272: Move argument parsing tests from test_capi to test_getargs2. (GH-8567)
(cherry picked from commit 8f7bb100d0
)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
parent
e39fb207f2
commit
a1ff5f9031
|
@ -6,7 +6,6 @@ import os
|
||||||
import pickle
|
import pickle
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
import string
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import sysconfig
|
import sysconfig
|
||||||
|
@ -414,174 +413,6 @@ class SubinterpreterTest(unittest.TestCase):
|
||||||
self.assertNotEqual(pickle.load(f), id(builtins))
|
self.assertNotEqual(pickle.load(f), id(builtins))
|
||||||
|
|
||||||
|
|
||||||
# Bug #6012
|
|
||||||
class Test6012(unittest.TestCase):
|
|
||||||
def test(self):
|
|
||||||
self.assertEqual(_testcapi.argparsing("Hello", "World"), 1)
|
|
||||||
|
|
||||||
|
|
||||||
class SkipitemTest(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_skipitem(self):
|
|
||||||
"""
|
|
||||||
If this test failed, you probably added a new "format unit"
|
|
||||||
in Python/getargs.c, but neglected to update our poor friend
|
|
||||||
skipitem() in the same file. (If so, shame on you!)
|
|
||||||
|
|
||||||
With a few exceptions**, this function brute-force tests all
|
|
||||||
printable ASCII*** characters (32 to 126 inclusive) as format units,
|
|
||||||
checking to see that PyArg_ParseTupleAndKeywords() return consistent
|
|
||||||
errors both when the unit is attempted to be used and when it is
|
|
||||||
skipped. If the format unit doesn't exist, we'll get one of two
|
|
||||||
specific error messages (one for used, one for skipped); if it does
|
|
||||||
exist we *won't* get that error--we'll get either no error or some
|
|
||||||
other error. If we get the specific "does not exist" error for one
|
|
||||||
test and not for the other, there's a mismatch, and the test fails.
|
|
||||||
|
|
||||||
** Some format units have special funny semantics and it would
|
|
||||||
be difficult to accommodate them here. Since these are all
|
|
||||||
well-established and properly skipped in skipitem() we can
|
|
||||||
get away with not testing them--this test is really intended
|
|
||||||
to catch *new* format units.
|
|
||||||
|
|
||||||
*** Python C source files must be ASCII. Therefore it's impossible
|
|
||||||
to have non-ASCII format units.
|
|
||||||
|
|
||||||
"""
|
|
||||||
empty_tuple = ()
|
|
||||||
tuple_1 = (0,)
|
|
||||||
dict_b = {'b':1}
|
|
||||||
keywords = ["a", "b"]
|
|
||||||
|
|
||||||
for i in range(32, 127):
|
|
||||||
c = chr(i)
|
|
||||||
|
|
||||||
# skip parentheses, the error reporting is inconsistent about them
|
|
||||||
# skip 'e', it's always a two-character code
|
|
||||||
# skip '|' and '$', they don't represent arguments anyway
|
|
||||||
if c in '()e|$':
|
|
||||||
continue
|
|
||||||
|
|
||||||
# test the format unit when not skipped
|
|
||||||
format = c + "i"
|
|
||||||
try:
|
|
||||||
_testcapi.parse_tuple_and_keywords(tuple_1, dict_b,
|
|
||||||
format, keywords)
|
|
||||||
when_not_skipped = False
|
|
||||||
except SystemError as e:
|
|
||||||
s = "argument 1 (impossible<bad format char>)"
|
|
||||||
when_not_skipped = (str(e) == s)
|
|
||||||
except TypeError:
|
|
||||||
when_not_skipped = False
|
|
||||||
|
|
||||||
# test the format unit when skipped
|
|
||||||
optional_format = "|" + format
|
|
||||||
try:
|
|
||||||
_testcapi.parse_tuple_and_keywords(empty_tuple, dict_b,
|
|
||||||
optional_format, keywords)
|
|
||||||
when_skipped = False
|
|
||||||
except SystemError as e:
|
|
||||||
s = "impossible<bad format char>: '{}'".format(format)
|
|
||||||
when_skipped = (str(e) == s)
|
|
||||||
|
|
||||||
message = ("test_skipitem_parity: "
|
|
||||||
"detected mismatch between convertsimple and skipitem "
|
|
||||||
"for format unit '{}' ({}), not skipped {}, skipped {}".format(
|
|
||||||
c, i, when_skipped, when_not_skipped))
|
|
||||||
self.assertIs(when_skipped, when_not_skipped, message)
|
|
||||||
|
|
||||||
def test_skipitem_with_suffix(self):
|
|
||||||
parse = _testcapi.parse_tuple_and_keywords
|
|
||||||
empty_tuple = ()
|
|
||||||
tuple_1 = (0,)
|
|
||||||
dict_b = {'b':1}
|
|
||||||
keywords = ["a", "b"]
|
|
||||||
|
|
||||||
supported = ('s#', 's*', 'z#', 'z*', 'u#', 'Z#', 'y#', 'y*', 'w#', 'w*')
|
|
||||||
for c in string.ascii_letters:
|
|
||||||
for c2 in '#*':
|
|
||||||
f = c + c2
|
|
||||||
with self.subTest(format=f):
|
|
||||||
optional_format = "|" + f + "i"
|
|
||||||
if f in supported:
|
|
||||||
parse(empty_tuple, dict_b, optional_format, keywords)
|
|
||||||
else:
|
|
||||||
with self.assertRaisesRegex(SystemError,
|
|
||||||
'impossible<bad format char>'):
|
|
||||||
parse(empty_tuple, dict_b, optional_format, keywords)
|
|
||||||
|
|
||||||
for c in map(chr, range(32, 128)):
|
|
||||||
f = 'e' + c
|
|
||||||
optional_format = "|" + f + "i"
|
|
||||||
with self.subTest(format=f):
|
|
||||||
if c in 'st':
|
|
||||||
parse(empty_tuple, dict_b, optional_format, keywords)
|
|
||||||
else:
|
|
||||||
with self.assertRaisesRegex(SystemError,
|
|
||||||
'impossible<bad format char>'):
|
|
||||||
parse(empty_tuple, dict_b, optional_format, keywords)
|
|
||||||
|
|
||||||
def test_parse_tuple_and_keywords(self):
|
|
||||||
# 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,
|
|
||||||
(), {}, '', 42)
|
|
||||||
self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
|
|
||||||
(), {}, '', [''] * 42)
|
|
||||||
self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
|
|
||||||
(), {}, '', [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), {}, '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}, 'OOO', ['', '', 'a'])
|
|
||||||
parse((1,), {}, 'O|OO', ['', '', 'a'])
|
|
||||||
with self.assertRaisesRegex(TypeError,
|
|
||||||
r'function takes at least 1 positional arguments \(0 given\)'):
|
|
||||||
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}, 'OO$O', ['', '', 'a'])
|
|
||||||
parse((1,), {}, 'O|O$O', ['', '', 'a'])
|
|
||||||
with self.assertRaisesRegex(TypeError,
|
|
||||||
r'function takes at least 1 positional arguments \(0 given\)'):
|
|
||||||
parse((), {}, 'O|O$O', ['', '', 'a'])
|
|
||||||
with self.assertRaisesRegex(SystemError, r'Empty parameter name after \$'):
|
|
||||||
parse((1,), {}, 'O|$OO', ['', '', 'a'])
|
|
||||||
with self.assertRaisesRegex(SystemError, 'Empty keyword'):
|
|
||||||
parse((1,), {}, 'O|OO', ['', 'a', ''])
|
|
||||||
|
|
||||||
|
|
||||||
class TestThreadState(unittest.TestCase):
|
class TestThreadState(unittest.TestCase):
|
||||||
|
|
||||||
@support.reap_threads
|
@support.reap_threads
|
||||||
|
@ -607,17 +438,9 @@ class TestThreadState(unittest.TestCase):
|
||||||
|
|
||||||
|
|
||||||
class Test_testcapi(unittest.TestCase):
|
class Test_testcapi(unittest.TestCase):
|
||||||
def test__testcapi(self):
|
locals().update((name, getattr(_testcapi, name))
|
||||||
if support.verbose:
|
for name in dir(_testcapi)
|
||||||
print()
|
if name.startswith('test_') and not name.endswith('_code'))
|
||||||
for name in dir(_testcapi):
|
|
||||||
if not name.startswith('test_'):
|
|
||||||
continue
|
|
||||||
with self.subTest("internal", name=name):
|
|
||||||
if support.verbose:
|
|
||||||
print(f" {name}", flush=True)
|
|
||||||
test = getattr(_testcapi, name)
|
|
||||||
test()
|
|
||||||
|
|
||||||
|
|
||||||
class PyMemDebugTests(unittest.TestCase):
|
class PyMemDebugTests(unittest.TestCase):
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import unittest
|
import unittest
|
||||||
import math
|
import math
|
||||||
|
import string
|
||||||
import sys
|
import sys
|
||||||
from test import support
|
from test import support
|
||||||
# Skip this test if the _testcapi module isn't available.
|
# Skip this test if the _testcapi module isn't available.
|
||||||
support.import_module('_testcapi')
|
_testcapi = support.import_module('_testcapi')
|
||||||
from _testcapi import getargs_keywords, getargs_keyword_only
|
from _testcapi import getargs_keywords, getargs_keyword_only
|
||||||
|
|
||||||
# > How about the following counterproposal. This also changes some of
|
# > How about the following counterproposal. This also changes some of
|
||||||
|
@ -956,5 +957,182 @@ class Object_TestCase(unittest.TestCase):
|
||||||
self.assertRaises(TypeError, getargs_U, None)
|
self.assertRaises(TypeError, getargs_U, None)
|
||||||
|
|
||||||
|
|
||||||
|
# Bug #6012
|
||||||
|
class Test6012(unittest.TestCase):
|
||||||
|
def test(self):
|
||||||
|
self.assertEqual(_testcapi.argparsing("Hello", "World"), 1)
|
||||||
|
|
||||||
|
|
||||||
|
class SkipitemTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_skipitem(self):
|
||||||
|
"""
|
||||||
|
If this test failed, you probably added a new "format unit"
|
||||||
|
in Python/getargs.c, but neglected to update our poor friend
|
||||||
|
skipitem() in the same file. (If so, shame on you!)
|
||||||
|
|
||||||
|
With a few exceptions**, this function brute-force tests all
|
||||||
|
printable ASCII*** characters (32 to 126 inclusive) as format units,
|
||||||
|
checking to see that PyArg_ParseTupleAndKeywords() return consistent
|
||||||
|
errors both when the unit is attempted to be used and when it is
|
||||||
|
skipped. If the format unit doesn't exist, we'll get one of two
|
||||||
|
specific error messages (one for used, one for skipped); if it does
|
||||||
|
exist we *won't* get that error--we'll get either no error or some
|
||||||
|
other error. If we get the specific "does not exist" error for one
|
||||||
|
test and not for the other, there's a mismatch, and the test fails.
|
||||||
|
|
||||||
|
** Some format units have special funny semantics and it would
|
||||||
|
be difficult to accommodate them here. Since these are all
|
||||||
|
well-established and properly skipped in skipitem() we can
|
||||||
|
get away with not testing them--this test is really intended
|
||||||
|
to catch *new* format units.
|
||||||
|
|
||||||
|
*** Python C source files must be ASCII. Therefore it's impossible
|
||||||
|
to have non-ASCII format units.
|
||||||
|
|
||||||
|
"""
|
||||||
|
empty_tuple = ()
|
||||||
|
tuple_1 = (0,)
|
||||||
|
dict_b = {'b':1}
|
||||||
|
keywords = ["a", "b"]
|
||||||
|
|
||||||
|
for i in range(32, 127):
|
||||||
|
c = chr(i)
|
||||||
|
|
||||||
|
# skip parentheses, the error reporting is inconsistent about them
|
||||||
|
# skip 'e', it's always a two-character code
|
||||||
|
# skip '|' and '$', they don't represent arguments anyway
|
||||||
|
if c in '()e|$':
|
||||||
|
continue
|
||||||
|
|
||||||
|
# test the format unit when not skipped
|
||||||
|
format = c + "i"
|
||||||
|
try:
|
||||||
|
_testcapi.parse_tuple_and_keywords(tuple_1, dict_b,
|
||||||
|
format, keywords)
|
||||||
|
when_not_skipped = False
|
||||||
|
except SystemError as e:
|
||||||
|
s = "argument 1 (impossible<bad format char>)"
|
||||||
|
when_not_skipped = (str(e) == s)
|
||||||
|
except TypeError:
|
||||||
|
when_not_skipped = False
|
||||||
|
|
||||||
|
# test the format unit when skipped
|
||||||
|
optional_format = "|" + format
|
||||||
|
try:
|
||||||
|
_testcapi.parse_tuple_and_keywords(empty_tuple, dict_b,
|
||||||
|
optional_format, keywords)
|
||||||
|
when_skipped = False
|
||||||
|
except SystemError as e:
|
||||||
|
s = "impossible<bad format char>: '{}'".format(format)
|
||||||
|
when_skipped = (str(e) == s)
|
||||||
|
|
||||||
|
message = ("test_skipitem_parity: "
|
||||||
|
"detected mismatch between convertsimple and skipitem "
|
||||||
|
"for format unit '{}' ({}), not skipped {}, skipped {}".format(
|
||||||
|
c, i, when_skipped, when_not_skipped))
|
||||||
|
self.assertIs(when_skipped, when_not_skipped, message)
|
||||||
|
|
||||||
|
def test_skipitem_with_suffix(self):
|
||||||
|
parse = _testcapi.parse_tuple_and_keywords
|
||||||
|
empty_tuple = ()
|
||||||
|
tuple_1 = (0,)
|
||||||
|
dict_b = {'b':1}
|
||||||
|
keywords = ["a", "b"]
|
||||||
|
|
||||||
|
supported = ('s#', 's*', 'z#', 'z*', 'u#', 'Z#', 'y#', 'y*', 'w#', 'w*')
|
||||||
|
for c in string.ascii_letters:
|
||||||
|
for c2 in '#*':
|
||||||
|
f = c + c2
|
||||||
|
with self.subTest(format=f):
|
||||||
|
optional_format = "|" + f + "i"
|
||||||
|
if f in supported:
|
||||||
|
parse(empty_tuple, dict_b, optional_format, keywords)
|
||||||
|
else:
|
||||||
|
with self.assertRaisesRegex(SystemError,
|
||||||
|
'impossible<bad format char>'):
|
||||||
|
parse(empty_tuple, dict_b, optional_format, keywords)
|
||||||
|
|
||||||
|
for c in map(chr, range(32, 128)):
|
||||||
|
f = 'e' + c
|
||||||
|
optional_format = "|" + f + "i"
|
||||||
|
with self.subTest(format=f):
|
||||||
|
if c in 'st':
|
||||||
|
parse(empty_tuple, dict_b, optional_format, keywords)
|
||||||
|
else:
|
||||||
|
with self.assertRaisesRegex(SystemError,
|
||||||
|
'impossible<bad format char>'):
|
||||||
|
parse(empty_tuple, dict_b, optional_format, keywords)
|
||||||
|
|
||||||
|
|
||||||
|
class ParseTupleAndKeywords_Test(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_parse_tuple_and_keywords(self):
|
||||||
|
# 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,
|
||||||
|
(), {}, '', 42)
|
||||||
|
self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
|
||||||
|
(), {}, '', [''] * 42)
|
||||||
|
self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
|
||||||
|
(), {}, '', [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), {}, '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}, 'OOO', ['', '', 'a'])
|
||||||
|
parse((1,), {}, 'O|OO', ['', '', 'a'])
|
||||||
|
with self.assertRaisesRegex(TypeError,
|
||||||
|
r'function takes at least 1 positional arguments \(0 given\)'):
|
||||||
|
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}, 'OO$O', ['', '', 'a'])
|
||||||
|
parse((1,), {}, 'O|O$O', ['', '', 'a'])
|
||||||
|
with self.assertRaisesRegex(TypeError,
|
||||||
|
r'function takes at least 1 positional arguments \(0 given\)'):
|
||||||
|
parse((), {}, 'O|O$O', ['', '', 'a'])
|
||||||
|
with self.assertRaisesRegex(SystemError, r'Empty parameter name after \$'):
|
||||||
|
parse((1,), {}, 'O|$OO', ['', '', 'a'])
|
||||||
|
with self.assertRaisesRegex(SystemError, 'Empty keyword'):
|
||||||
|
parse((1,), {}, 'O|OO', ['', 'a', ''])
|
||||||
|
|
||||||
|
|
||||||
|
class Test_testcapi(unittest.TestCase):
|
||||||
|
locals().update((name, getattr(_testcapi, name))
|
||||||
|
for name in dir(_testcapi)
|
||||||
|
if name.startswith('test_') and name.endswith('_code'))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Loading…
Reference in New Issue