Skip to content

Commit efc985a

Browse files
gh-93649: Split exception tests from _testcapimodule.c (GH-102173)
Automerge-Triggered-By: GH:erlend-aasland
1 parent e07b304 commit efc985a

File tree

8 files changed

+434
-416
lines changed

8 files changed

+434
-416
lines changed

Lib/test/test_capi/test_exceptions.py

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import re
2+
import sys
3+
import unittest
4+
5+
from test import support
6+
from test.support import import_helper
7+
from test.support.script_helper import assert_python_failure
8+
9+
from .test_misc import decode_stderr
10+
11+
# Skip this test if the _testcapi module isn't available.
12+
_testcapi = import_helper.import_module('_testcapi')
13+
14+
class Test_Exceptions(unittest.TestCase):
15+
16+
def test_exception(self):
17+
raised_exception = ValueError("5")
18+
new_exc = TypeError("TEST")
19+
try:
20+
raise raised_exception
21+
except ValueError as e:
22+
orig_sys_exception = sys.exception()
23+
orig_exception = _testcapi.set_exception(new_exc)
24+
new_sys_exception = sys.exception()
25+
new_exception = _testcapi.set_exception(orig_exception)
26+
reset_sys_exception = sys.exception()
27+
28+
self.assertEqual(orig_exception, e)
29+
30+
self.assertEqual(orig_exception, raised_exception)
31+
self.assertEqual(orig_sys_exception, orig_exception)
32+
self.assertEqual(reset_sys_exception, orig_exception)
33+
self.assertEqual(new_exception, new_exc)
34+
self.assertEqual(new_sys_exception, new_exception)
35+
else:
36+
self.fail("Exception not raised")
37+
38+
def test_exc_info(self):
39+
raised_exception = ValueError("5")
40+
new_exc = TypeError("TEST")
41+
try:
42+
raise raised_exception
43+
except ValueError as e:
44+
tb = e.__traceback__
45+
orig_sys_exc_info = sys.exc_info()
46+
orig_exc_info = _testcapi.set_exc_info(new_exc.__class__, new_exc, None)
47+
new_sys_exc_info = sys.exc_info()
48+
new_exc_info = _testcapi.set_exc_info(*orig_exc_info)
49+
reset_sys_exc_info = sys.exc_info()
50+
51+
self.assertEqual(orig_exc_info[1], e)
52+
53+
self.assertSequenceEqual(orig_exc_info, (raised_exception.__class__, raised_exception, tb))
54+
self.assertSequenceEqual(orig_sys_exc_info, orig_exc_info)
55+
self.assertSequenceEqual(reset_sys_exc_info, orig_exc_info)
56+
self.assertSequenceEqual(new_exc_info, (new_exc.__class__, new_exc, None))
57+
self.assertSequenceEqual(new_sys_exc_info, new_exc_info)
58+
else:
59+
self.assertTrue(False)
60+
61+
62+
class Test_FatalError(unittest.TestCase):
63+
64+
def check_fatal_error(self, code, expected, not_expected=()):
65+
with support.SuppressCrashReport():
66+
rc, out, err = assert_python_failure('-sSI', '-c', code)
67+
68+
err = decode_stderr(err)
69+
self.assertIn('Fatal Python error: test_fatal_error: MESSAGE\n',
70+
err)
71+
72+
match = re.search(r'^Extension modules:(.*) \(total: ([0-9]+)\)$',
73+
err, re.MULTILINE)
74+
if not match:
75+
self.fail(f"Cannot find 'Extension modules:' in {err!r}")
76+
modules = set(match.group(1).strip().split(', '))
77+
total = int(match.group(2))
78+
79+
for name in expected:
80+
self.assertIn(name, modules)
81+
for name in not_expected:
82+
self.assertNotIn(name, modules)
83+
self.assertEqual(len(modules), total)
84+
85+
@support.requires_subprocess()
86+
def test_fatal_error(self):
87+
# By default, stdlib extension modules are ignored,
88+
# but not test modules.
89+
expected = ('_testcapi',)
90+
not_expected = ('sys',)
91+
code = 'import _testcapi, sys; _testcapi.fatal_error(b"MESSAGE")'
92+
self.check_fatal_error(code, expected, not_expected)
93+
94+
# Mark _testcapi as stdlib module, but not sys
95+
expected = ('sys',)
96+
not_expected = ('_testcapi',)
97+
code = """if True:
98+
import _testcapi, sys
99+
sys.stdlib_module_names = frozenset({"_testcapi"})
100+
_testcapi.fatal_error(b"MESSAGE")
101+
"""
102+
self.check_fatal_error(code, expected)
103+
104+
105+
class Test_ErrSetAndRestore(unittest.TestCase):
106+
107+
def test_err_set_raised(self):
108+
with self.assertRaises(ValueError):
109+
_testcapi.err_set_raised(ValueError())
110+
v = ValueError()
111+
try:
112+
_testcapi.err_set_raised(v)
113+
except ValueError as ex:
114+
self.assertIs(v, ex)
115+
116+
def test_err_restore(self):
117+
with self.assertRaises(ValueError):
118+
_testcapi.err_restore(ValueError)
119+
with self.assertRaises(ValueError):
120+
_testcapi.err_restore(ValueError, 1)
121+
with self.assertRaises(ValueError):
122+
_testcapi.err_restore(ValueError, 1, None)
123+
with self.assertRaises(ValueError):
124+
_testcapi.err_restore(ValueError, ValueError())
125+
try:
126+
_testcapi.err_restore(KeyError, "hi")
127+
except KeyError as k:
128+
self.assertEqual("hi", k.args[0])
129+
try:
130+
1/0
131+
except Exception as e:
132+
tb = e.__traceback__
133+
with self.assertRaises(ValueError):
134+
_testcapi.err_restore(ValueError, 1, tb)
135+
with self.assertRaises(TypeError):
136+
_testcapi.err_restore(ValueError, 1, 0)
137+
try:
138+
_testcapi.err_restore(ValueError, 1, tb)
139+
except ValueError as v:
140+
self.assertEqual(1, v.args[0])
141+
self.assertIs(tb, v.__traceback__.tb_next)
142+
143+
144+
if __name__ == "__main__":
145+
unittest.main()

Lib/test/test_capi/test_misc.py

+3-128
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import os
99
import pickle
1010
import random
11-
import re
1211
import subprocess
1312
import sys
1413
import textwrap
@@ -91,51 +90,6 @@ def test_no_FatalError_infinite_loop(self):
9190
def test_memoryview_from_NULL_pointer(self):
9291
self.assertRaises(ValueError, _testcapi.make_memoryview_from_NULL_pointer)
9392

94-
def test_exception(self):
95-
raised_exception = ValueError("5")
96-
new_exc = TypeError("TEST")
97-
try:
98-
raise raised_exception
99-
except ValueError as e:
100-
orig_sys_exception = sys.exception()
101-
orig_exception = _testcapi.set_exception(new_exc)
102-
new_sys_exception = sys.exception()
103-
new_exception = _testcapi.set_exception(orig_exception)
104-
reset_sys_exception = sys.exception()
105-
106-
self.assertEqual(orig_exception, e)
107-
108-
self.assertEqual(orig_exception, raised_exception)
109-
self.assertEqual(orig_sys_exception, orig_exception)
110-
self.assertEqual(reset_sys_exception, orig_exception)
111-
self.assertEqual(new_exception, new_exc)
112-
self.assertEqual(new_sys_exception, new_exception)
113-
else:
114-
self.fail("Exception not raised")
115-
116-
def test_exc_info(self):
117-
raised_exception = ValueError("5")
118-
new_exc = TypeError("TEST")
119-
try:
120-
raise raised_exception
121-
except ValueError as e:
122-
tb = e.__traceback__
123-
orig_sys_exc_info = sys.exc_info()
124-
orig_exc_info = _testcapi.set_exc_info(new_exc.__class__, new_exc, None)
125-
new_sys_exc_info = sys.exc_info()
126-
new_exc_info = _testcapi.set_exc_info(*orig_exc_info)
127-
reset_sys_exc_info = sys.exc_info()
128-
129-
self.assertEqual(orig_exc_info[1], e)
130-
131-
self.assertSequenceEqual(orig_exc_info, (raised_exception.__class__, raised_exception, tb))
132-
self.assertSequenceEqual(orig_sys_exc_info, orig_exc_info)
133-
self.assertSequenceEqual(reset_sys_exc_info, orig_exc_info)
134-
self.assertSequenceEqual(new_exc_info, (new_exc.__class__, new_exc, None))
135-
self.assertSequenceEqual(new_sys_exc_info, new_exc_info)
136-
else:
137-
self.assertTrue(False)
138-
13993
@unittest.skipUnless(_posixsubprocess, '_posixsubprocess required for this test.')
14094
def test_seq_bytes_to_charp_array(self):
14195
# Issue #15732: crash in _PySequence_BytesToCharpArray()
@@ -837,46 +791,6 @@ def __index__(self):
837791
self.assertRaises(TypeError, pynumber_tobase, '123', 10)
838792
self.assertRaises(SystemError, pynumber_tobase, 123, 0)
839793

840-
def check_fatal_error(self, code, expected, not_expected=()):
841-
with support.SuppressCrashReport():
842-
rc, out, err = assert_python_failure('-sSI', '-c', code)
843-
844-
err = decode_stderr(err)
845-
self.assertIn('Fatal Python error: test_fatal_error: MESSAGE\n',
846-
err)
847-
848-
match = re.search(r'^Extension modules:(.*) \(total: ([0-9]+)\)$',
849-
err, re.MULTILINE)
850-
if not match:
851-
self.fail(f"Cannot find 'Extension modules:' in {err!r}")
852-
modules = set(match.group(1).strip().split(', '))
853-
total = int(match.group(2))
854-
855-
for name in expected:
856-
self.assertIn(name, modules)
857-
for name in not_expected:
858-
self.assertNotIn(name, modules)
859-
self.assertEqual(len(modules), total)
860-
861-
@support.requires_subprocess()
862-
def test_fatal_error(self):
863-
# By default, stdlib extension modules are ignored,
864-
# but not test modules.
865-
expected = ('_testcapi',)
866-
not_expected = ('sys',)
867-
code = 'import _testcapi, sys; _testcapi.fatal_error(b"MESSAGE")'
868-
self.check_fatal_error(code, expected, not_expected)
869-
870-
# Mark _testcapi as stdlib module, but not sys
871-
expected = ('sys',)
872-
not_expected = ('_testcapi',)
873-
code = textwrap.dedent('''
874-
import _testcapi, sys
875-
sys.stdlib_module_names = frozenset({"_testcapi"})
876-
_testcapi.fatal_error(b"MESSAGE")
877-
''')
878-
self.check_fatal_error(code, expected)
879-
880794
def test_pyobject_repr_from_null(self):
881795
s = _testcapi.pyobject_repr_from_null()
882796
self.assertEqual(s, '<NULL>')
@@ -1214,9 +1128,9 @@ def test_pendingcalls_non_threaded(self):
12141128
self.pendingcalls_wait(l, n)
12151129

12161130
def test_gen_get_code(self):
1217-
def genf(): yield
1218-
gen = genf()
1219-
self.assertEqual(_testcapi.gen_get_code(gen), gen.gi_code)
1131+
def genf(): yield
1132+
gen = genf()
1133+
self.assertEqual(_testcapi.gen_get_code(gen), gen.gi_code)
12201134

12211135

12221136
class SubinterpreterTest(unittest.TestCase):
@@ -1641,44 +1555,5 @@ def func2(x=None):
16411555
self.do_test(func2)
16421556

16431557

1644-
class Test_ErrSetAndRestore(unittest.TestCase):
1645-
1646-
def test_err_set_raised(self):
1647-
with self.assertRaises(ValueError):
1648-
_testcapi.err_set_raised(ValueError())
1649-
v = ValueError()
1650-
try:
1651-
_testcapi.err_set_raised(v)
1652-
except ValueError as ex:
1653-
self.assertIs(v, ex)
1654-
1655-
def test_err_restore(self):
1656-
with self.assertRaises(ValueError):
1657-
_testcapi.err_restore(ValueError)
1658-
with self.assertRaises(ValueError):
1659-
_testcapi.err_restore(ValueError, 1)
1660-
with self.assertRaises(ValueError):
1661-
_testcapi.err_restore(ValueError, 1, None)
1662-
with self.assertRaises(ValueError):
1663-
_testcapi.err_restore(ValueError, ValueError())
1664-
try:
1665-
_testcapi.err_restore(KeyError, "hi")
1666-
except KeyError as k:
1667-
self.assertEqual("hi", k.args[0])
1668-
try:
1669-
1/0
1670-
except Exception as e:
1671-
tb = e.__traceback__
1672-
with self.assertRaises(ValueError):
1673-
_testcapi.err_restore(ValueError, 1, tb)
1674-
with self.assertRaises(TypeError):
1675-
_testcapi.err_restore(ValueError, 1, 0)
1676-
try:
1677-
_testcapi.err_restore(ValueError, 1, tb)
1678-
except ValueError as v:
1679-
self.assertEqual(1, v.args[0])
1680-
self.assertIs(tb, v.__traceback__.tb_next)
1681-
1682-
16831558
if __name__ == "__main__":
16841559
unittest.main()

Modules/Setup.stdlib.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@
169169
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
170170
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
171171
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c
172-
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c
172+
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c
173173
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
174174

175175
# Some testing modules MUST be built as shared libraries.

0 commit comments

Comments
 (0)