Skip to content

Commit 8f7bb10

Browse files
bpo-34272: Move argument parsing tests from test_capi to test_getargs2. (GH-8567)
1 parent 28c7f8c commit 8f7bb10

File tree

2 files changed

+182
-181
lines changed

2 files changed

+182
-181
lines changed

Lib/test/test_capi.py

Lines changed: 3 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import pickle
77
import random
88
import re
9-
import string
109
import subprocess
1110
import sys
1211
import sysconfig
@@ -414,174 +413,6 @@ def test_subinterps(self):
414413
self.assertNotEqual(pickle.load(f), id(builtins))
415414

416415

417-
# Bug #6012
418-
class Test6012(unittest.TestCase):
419-
def test(self):
420-
self.assertEqual(_testcapi.argparsing("Hello", "World"), 1)
421-
422-
423-
class SkipitemTest(unittest.TestCase):
424-
425-
def test_skipitem(self):
426-
"""
427-
If this test failed, you probably added a new "format unit"
428-
in Python/getargs.c, but neglected to update our poor friend
429-
skipitem() in the same file. (If so, shame on you!)
430-
431-
With a few exceptions**, this function brute-force tests all
432-
printable ASCII*** characters (32 to 126 inclusive) as format units,
433-
checking to see that PyArg_ParseTupleAndKeywords() return consistent
434-
errors both when the unit is attempted to be used and when it is
435-
skipped. If the format unit doesn't exist, we'll get one of two
436-
specific error messages (one for used, one for skipped); if it does
437-
exist we *won't* get that error--we'll get either no error or some
438-
other error. If we get the specific "does not exist" error for one
439-
test and not for the other, there's a mismatch, and the test fails.
440-
441-
** Some format units have special funny semantics and it would
442-
be difficult to accommodate them here. Since these are all
443-
well-established and properly skipped in skipitem() we can
444-
get away with not testing them--this test is really intended
445-
to catch *new* format units.
446-
447-
*** Python C source files must be ASCII. Therefore it's impossible
448-
to have non-ASCII format units.
449-
450-
"""
451-
empty_tuple = ()
452-
tuple_1 = (0,)
453-
dict_b = {'b':1}
454-
keywords = ["a", "b"]
455-
456-
for i in range(32, 127):
457-
c = chr(i)
458-
459-
# skip parentheses, the error reporting is inconsistent about them
460-
# skip 'e', it's always a two-character code
461-
# skip '|' and '$', they don't represent arguments anyway
462-
if c in '()e|$':
463-
continue
464-
465-
# test the format unit when not skipped
466-
format = c + "i"
467-
try:
468-
_testcapi.parse_tuple_and_keywords(tuple_1, dict_b,
469-
format, keywords)
470-
when_not_skipped = False
471-
except SystemError as e:
472-
s = "argument 1 (impossible<bad format char>)"
473-
when_not_skipped = (str(e) == s)
474-
except TypeError:
475-
when_not_skipped = False
476-
477-
# test the format unit when skipped
478-
optional_format = "|" + format
479-
try:
480-
_testcapi.parse_tuple_and_keywords(empty_tuple, dict_b,
481-
optional_format, keywords)
482-
when_skipped = False
483-
except SystemError as e:
484-
s = "impossible<bad format char>: '{}'".format(format)
485-
when_skipped = (str(e) == s)
486-
487-
message = ("test_skipitem_parity: "
488-
"detected mismatch between convertsimple and skipitem "
489-
"for format unit '{}' ({}), not skipped {}, skipped {}".format(
490-
c, i, when_skipped, when_not_skipped))
491-
self.assertIs(when_skipped, when_not_skipped, message)
492-
493-
def test_skipitem_with_suffix(self):
494-
parse = _testcapi.parse_tuple_and_keywords
495-
empty_tuple = ()
496-
tuple_1 = (0,)
497-
dict_b = {'b':1}
498-
keywords = ["a", "b"]
499-
500-
supported = ('s#', 's*', 'z#', 'z*', 'u#', 'Z#', 'y#', 'y*', 'w#', 'w*')
501-
for c in string.ascii_letters:
502-
for c2 in '#*':
503-
f = c + c2
504-
with self.subTest(format=f):
505-
optional_format = "|" + f + "i"
506-
if f in supported:
507-
parse(empty_tuple, dict_b, optional_format, keywords)
508-
else:
509-
with self.assertRaisesRegex(SystemError,
510-
'impossible<bad format char>'):
511-
parse(empty_tuple, dict_b, optional_format, keywords)
512-
513-
for c in map(chr, range(32, 128)):
514-
f = 'e' + c
515-
optional_format = "|" + f + "i"
516-
with self.subTest(format=f):
517-
if c in 'st':
518-
parse(empty_tuple, dict_b, optional_format, keywords)
519-
else:
520-
with self.assertRaisesRegex(SystemError,
521-
'impossible<bad format char>'):
522-
parse(empty_tuple, dict_b, optional_format, keywords)
523-
524-
def test_parse_tuple_and_keywords(self):
525-
# Test handling errors in the parse_tuple_and_keywords helper itself
526-
self.assertRaises(TypeError, _testcapi.parse_tuple_and_keywords,
527-
(), {}, 42, [])
528-
self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
529-
(), {}, '', 42)
530-
self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
531-
(), {}, '', [''] * 42)
532-
self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
533-
(), {}, '', [42])
534-
535-
def test_bad_use(self):
536-
# Test handling invalid format and keywords in
537-
# PyArg_ParseTupleAndKeywords()
538-
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
539-
(1,), {}, '||O', ['a'])
540-
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
541-
(1, 2), {}, '|O|O', ['a', 'b'])
542-
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
543-
(), {'a': 1}, '$$O', ['a'])
544-
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
545-
(), {'a': 1, 'b': 2}, '$O$O', ['a', 'b'])
546-
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
547-
(), {'a': 1}, '$|O', ['a'])
548-
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
549-
(), {'a': 1, 'b': 2}, '$O|O', ['a', 'b'])
550-
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
551-
(1,), {}, '|O', ['a', 'b'])
552-
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
553-
(1,), {}, '|OO', ['a'])
554-
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
555-
(), {}, '|$O', [''])
556-
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
557-
(), {}, '|OO', ['a', ''])
558-
559-
def test_positional_only(self):
560-
parse = _testcapi.parse_tuple_and_keywords
561-
562-
parse((1, 2, 3), {}, 'OOO', ['', '', 'a'])
563-
parse((1, 2), {'a': 3}, 'OOO', ['', '', 'a'])
564-
with self.assertRaisesRegex(TypeError,
565-
r'function takes at least 2 positional arguments \(1 given\)'):
566-
parse((1,), {'a': 3}, 'OOO', ['', '', 'a'])
567-
parse((1,), {}, 'O|OO', ['', '', 'a'])
568-
with self.assertRaisesRegex(TypeError,
569-
r'function takes at least 1 positional arguments \(0 given\)'):
570-
parse((), {}, 'O|OO', ['', '', 'a'])
571-
parse((1, 2), {'a': 3}, 'OO$O', ['', '', 'a'])
572-
with self.assertRaisesRegex(TypeError,
573-
r'function takes exactly 2 positional arguments \(1 given\)'):
574-
parse((1,), {'a': 3}, 'OO$O', ['', '', 'a'])
575-
parse((1,), {}, 'O|O$O', ['', '', 'a'])
576-
with self.assertRaisesRegex(TypeError,
577-
r'function takes at least 1 positional arguments \(0 given\)'):
578-
parse((), {}, 'O|O$O', ['', '', 'a'])
579-
with self.assertRaisesRegex(SystemError, r'Empty parameter name after \$'):
580-
parse((1,), {}, 'O|$OO', ['', '', 'a'])
581-
with self.assertRaisesRegex(SystemError, 'Empty keyword'):
582-
parse((1,), {}, 'O|OO', ['', 'a', ''])
583-
584-
585416
class TestThreadState(unittest.TestCase):
586417

587418
@support.reap_threads
@@ -607,17 +438,9 @@ def callback():
607438

608439

609440
class Test_testcapi(unittest.TestCase):
610-
def test__testcapi(self):
611-
if support.verbose:
612-
print()
613-
for name in dir(_testcapi):
614-
if not name.startswith('test_'):
615-
continue
616-
with self.subTest("internal", name=name):
617-
if support.verbose:
618-
print(f" {name}", flush=True)
619-
test = getattr(_testcapi, name)
620-
test()
441+
locals().update((name, getattr(_testcapi, name))
442+
for name in dir(_testcapi)
443+
if name.startswith('test_') and not name.endswith('_code'))
621444

622445

623446
class PyMemDebugTests(unittest.TestCase):

Lib/test/test_getargs2.py

Lines changed: 179 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import unittest
22
import math
3+
import string
34
import sys
45
from test import support
56
# Skip this test if the _testcapi module isn't available.
6-
support.import_module('_testcapi')
7+
_testcapi = support.import_module('_testcapi')
78
from _testcapi import getargs_keywords, getargs_keyword_only
89

910
# > How about the following counterproposal. This also changes some of
@@ -956,5 +957,182 @@ def test_U(self):
956957
self.assertRaises(TypeError, getargs_U, None)
957958

958959

960+
# Bug #6012
961+
class Test6012(unittest.TestCase):
962+
def test(self):
963+
self.assertEqual(_testcapi.argparsing("Hello", "World"), 1)
964+
965+
966+
class SkipitemTest(unittest.TestCase):
967+
968+
def test_skipitem(self):
969+
"""
970+
If this test failed, you probably added a new "format unit"
971+
in Python/getargs.c, but neglected to update our poor friend
972+
skipitem() in the same file. (If so, shame on you!)
973+
974+
With a few exceptions**, this function brute-force tests all
975+
printable ASCII*** characters (32 to 126 inclusive) as format units,
976+
checking to see that PyArg_ParseTupleAndKeywords() return consistent
977+
errors both when the unit is attempted to be used and when it is
978+
skipped. If the format unit doesn't exist, we'll get one of two
979+
specific error messages (one for used, one for skipped); if it does
980+
exist we *won't* get that error--we'll get either no error or some
981+
other error. If we get the specific "does not exist" error for one
982+
test and not for the other, there's a mismatch, and the test fails.
983+
984+
** Some format units have special funny semantics and it would
985+
be difficult to accommodate them here. Since these are all
986+
well-established and properly skipped in skipitem() we can
987+
get away with not testing them--this test is really intended
988+
to catch *new* format units.
989+
990+
*** Python C source files must be ASCII. Therefore it's impossible
991+
to have non-ASCII format units.
992+
993+
"""
994+
empty_tuple = ()
995+
tuple_1 = (0,)
996+
dict_b = {'b':1}
997+
keywords = ["a", "b"]
998+
999+
for i in range(32, 127):
1000+
c = chr(i)
1001+
1002+
# skip parentheses, the error reporting is inconsistent about them
1003+
# skip 'e', it's always a two-character code
1004+
# skip '|' and '$', they don't represent arguments anyway
1005+
if c in '()e|$':
1006+
continue
1007+
1008+
# test the format unit when not skipped
1009+
format = c + "i"
1010+
try:
1011+
_testcapi.parse_tuple_and_keywords(tuple_1, dict_b,
1012+
format, keywords)
1013+
when_not_skipped = False
1014+
except SystemError as e:
1015+
s = "argument 1 (impossible<bad format char>)"
1016+
when_not_skipped = (str(e) == s)
1017+
except TypeError:
1018+
when_not_skipped = False
1019+
1020+
# test the format unit when skipped
1021+
optional_format = "|" + format
1022+
try:
1023+
_testcapi.parse_tuple_and_keywords(empty_tuple, dict_b,
1024+
optional_format, keywords)
1025+
when_skipped = False
1026+
except SystemError as e:
1027+
s = "impossible<bad format char>: '{}'".format(format)
1028+
when_skipped = (str(e) == s)
1029+
1030+
message = ("test_skipitem_parity: "
1031+
"detected mismatch between convertsimple and skipitem "
1032+
"for format unit '{}' ({}), not skipped {}, skipped {}".format(
1033+
c, i, when_skipped, when_not_skipped))
1034+
self.assertIs(when_skipped, when_not_skipped, message)
1035+
1036+
def test_skipitem_with_suffix(self):
1037+
parse = _testcapi.parse_tuple_and_keywords
1038+
empty_tuple = ()
1039+
tuple_1 = (0,)
1040+
dict_b = {'b':1}
1041+
keywords = ["a", "b"]
1042+
1043+
supported = ('s#', 's*', 'z#', 'z*', 'u#', 'Z#', 'y#', 'y*', 'w#', 'w*')
1044+
for c in string.ascii_letters:
1045+
for c2 in '#*':
1046+
f = c + c2
1047+
with self.subTest(format=f):
1048+
optional_format = "|" + f + "i"
1049+
if f in supported:
1050+
parse(empty_tuple, dict_b, optional_format, keywords)
1051+
else:
1052+
with self.assertRaisesRegex(SystemError,
1053+
'impossible<bad format char>'):
1054+
parse(empty_tuple, dict_b, optional_format, keywords)
1055+
1056+
for c in map(chr, range(32, 128)):
1057+
f = 'e' + c
1058+
optional_format = "|" + f + "i"
1059+
with self.subTest(format=f):
1060+
if c in 'st':
1061+
parse(empty_tuple, dict_b, optional_format, keywords)
1062+
else:
1063+
with self.assertRaisesRegex(SystemError,
1064+
'impossible<bad format char>'):
1065+
parse(empty_tuple, dict_b, optional_format, keywords)
1066+
1067+
1068+
class ParseTupleAndKeywords_Test(unittest.TestCase):
1069+
1070+
def test_parse_tuple_and_keywords(self):
1071+
# Test handling errors in the parse_tuple_and_keywords helper itself
1072+
self.assertRaises(TypeError, _testcapi.parse_tuple_and_keywords,
1073+
(), {}, 42, [])
1074+
self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
1075+
(), {}, '', 42)
1076+
self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
1077+
(), {}, '', [''] * 42)
1078+
self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
1079+
(), {}, '', [42])
1080+
1081+
def test_bad_use(self):
1082+
# Test handling invalid format and keywords in
1083+
# PyArg_ParseTupleAndKeywords()
1084+
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
1085+
(1,), {}, '||O', ['a'])
1086+
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
1087+
(1, 2), {}, '|O|O', ['a', 'b'])
1088+
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
1089+
(), {'a': 1}, '$$O', ['a'])
1090+
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
1091+
(), {'a': 1, 'b': 2}, '$O$O', ['a', 'b'])
1092+
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
1093+
(), {'a': 1}, '$|O', ['a'])
1094+
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
1095+
(), {'a': 1, 'b': 2}, '$O|O', ['a', 'b'])
1096+
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
1097+
(1,), {}, '|O', ['a', 'b'])
1098+
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
1099+
(1,), {}, '|OO', ['a'])
1100+
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
1101+
(), {}, '|$O', [''])
1102+
self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords,
1103+
(), {}, '|OO', ['a', ''])
1104+
1105+
def test_positional_only(self):
1106+
parse = _testcapi.parse_tuple_and_keywords
1107+
1108+
parse((1, 2, 3), {}, 'OOO', ['', '', 'a'])
1109+
parse((1, 2), {'a': 3}, 'OOO', ['', '', 'a'])
1110+
with self.assertRaisesRegex(TypeError,
1111+
r'function takes at least 2 positional arguments \(1 given\)'):
1112+
parse((1,), {'a': 3}, 'OOO', ['', '', 'a'])
1113+
parse((1,), {}, 'O|OO', ['', '', 'a'])
1114+
with self.assertRaisesRegex(TypeError,
1115+
r'function takes at least 1 positional arguments \(0 given\)'):
1116+
parse((), {}, 'O|OO', ['', '', 'a'])
1117+
parse((1, 2), {'a': 3}, 'OO$O', ['', '', 'a'])
1118+
with self.assertRaisesRegex(TypeError,
1119+
r'function takes exactly 2 positional arguments \(1 given\)'):
1120+
parse((1,), {'a': 3}, 'OO$O', ['', '', 'a'])
1121+
parse((1,), {}, 'O|O$O', ['', '', 'a'])
1122+
with self.assertRaisesRegex(TypeError,
1123+
r'function takes at least 1 positional arguments \(0 given\)'):
1124+
parse((), {}, 'O|O$O', ['', '', 'a'])
1125+
with self.assertRaisesRegex(SystemError, r'Empty parameter name after \$'):
1126+
parse((1,), {}, 'O|$OO', ['', '', 'a'])
1127+
with self.assertRaisesRegex(SystemError, 'Empty keyword'):
1128+
parse((1,), {}, 'O|OO', ['', 'a', ''])
1129+
1130+
1131+
class Test_testcapi(unittest.TestCase):
1132+
locals().update((name, getattr(_testcapi, name))
1133+
for name in dir(_testcapi)
1134+
if name.startswith('test_') and name.endswith('_code'))
1135+
1136+
9591137
if __name__ == "__main__":
9601138
unittest.main()

0 commit comments

Comments
 (0)