Skip to content

Commit 5c3a129

Browse files
gh-76785: Clean up the Failure-Related _xxsubinterpreters Tests (gh-112322)
1 parent 118522b commit 5c3a129

File tree

1 file changed

+157
-37
lines changed

1 file changed

+157
-37
lines changed

Lib/test/test__xxsubinterpreters.py

Lines changed: 157 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import _testinternalcapi
1111
from test import support
1212
from test.support import import_helper
13+
from test.support import os_helper
1314
from test.support import script_helper
1415

1516

@@ -759,43 +760,6 @@ def test_bytes_for_script(self):
759760
with self.assertRaises(TypeError):
760761
interpreters.run_string(self.id, b'print("spam")')
761762

762-
@contextlib.contextmanager
763-
def assert_run_failed(self, exctype, msg=None):
764-
with self.assertRaises(interpreters.RunFailedError) as caught:
765-
yield
766-
if msg is None:
767-
self.assertEqual(str(caught.exception).split(':')[0],
768-
exctype.__name__)
769-
else:
770-
self.assertEqual(str(caught.exception),
771-
"{}: {}".format(exctype.__name__, msg))
772-
773-
def test_invalid_syntax(self):
774-
with self.assert_run_failed(SyntaxError):
775-
# missing close paren
776-
interpreters.run_string(self.id, 'print("spam"')
777-
778-
def test_failure(self):
779-
with self.assert_run_failed(Exception, 'spam'):
780-
interpreters.run_string(self.id, 'raise Exception("spam")')
781-
782-
def test_SystemExit(self):
783-
with self.assert_run_failed(SystemExit, '42'):
784-
interpreters.run_string(self.id, 'raise SystemExit(42)')
785-
786-
def test_sys_exit(self):
787-
with self.assert_run_failed(SystemExit):
788-
interpreters.run_string(self.id, dedent("""
789-
import sys
790-
sys.exit()
791-
"""))
792-
793-
with self.assert_run_failed(SystemExit, '42'):
794-
interpreters.run_string(self.id, dedent("""
795-
import sys
796-
sys.exit(42)
797-
"""))
798-
799763
def test_with_shared(self):
800764
r, w = os.pipe()
801765

@@ -959,6 +923,162 @@ def f():
959923
self.assertEqual(retcode, 0)
960924

961925

926+
class RunFailedTests(TestBase):
927+
928+
def setUp(self):
929+
super().setUp()
930+
self.id = interpreters.create()
931+
932+
def add_module(self, modname, text):
933+
import tempfile
934+
tempdir = tempfile.mkdtemp()
935+
self.addCleanup(lambda: os_helper.rmtree(tempdir))
936+
interpreters.run_string(self.id, dedent(f"""
937+
import sys
938+
sys.path.insert(0, {tempdir!r})
939+
"""))
940+
return script_helper.make_script(tempdir, modname, text)
941+
942+
def run_script(self, text, *, fails=False):
943+
excwrapper = interpreters.RunFailedError
944+
r, w = os.pipe()
945+
try:
946+
script = dedent(f"""
947+
import os, sys
948+
os.write({w}, b'0')
949+
950+
# This raises an exception:
951+
{{}}
952+
953+
# Nothing from here down should ever run.
954+
os.write({w}, b'1')
955+
class NeverError(Exception): pass
956+
raise NeverError # never raised
957+
""").format(dedent(text))
958+
if fails:
959+
with self.assertRaises(excwrapper) as caught:
960+
interpreters.run_string(self.id, script)
961+
return caught.exception
962+
else:
963+
interpreters.run_string(self.id, script)
964+
return None
965+
except:
966+
raise # re-raise
967+
else:
968+
msg = os.read(r, 100)
969+
self.assertEqual(msg, b'0')
970+
finally:
971+
os.close(r)
972+
os.close(w)
973+
974+
def _assert_run_failed(self, exctype, msg, script):
975+
if isinstance(exctype, str):
976+
exctype_name = exctype
977+
exctype = None
978+
else:
979+
exctype_name = exctype.__name__
980+
981+
# Run the script.
982+
exc = self.run_script(script, fails=True)
983+
984+
# Check the wrapper exception.
985+
if msg is None:
986+
self.assertEqual(str(exc).split(':')[0],
987+
exctype_name)
988+
else:
989+
self.assertEqual(str(exc),
990+
'{}: {}'.format(exctype_name, msg))
991+
992+
return exc
993+
994+
def assert_run_failed(self, exctype, script):
995+
self._assert_run_failed(exctype, None, script)
996+
997+
def assert_run_failed_msg(self, exctype, msg, script):
998+
self._assert_run_failed(exctype, msg, script)
999+
1000+
def test_exit(self):
1001+
with self.subTest('sys.exit(0)'):
1002+
# XXX Should an unhandled SystemExit(0) be handled as not-an-error?
1003+
self.assert_run_failed(SystemExit, """
1004+
sys.exit(0)
1005+
""")
1006+
1007+
with self.subTest('sys.exit()'):
1008+
self.assert_run_failed(SystemExit, """
1009+
import sys
1010+
sys.exit()
1011+
""")
1012+
1013+
with self.subTest('sys.exit(42)'):
1014+
self.assert_run_failed_msg(SystemExit, '42', """
1015+
import sys
1016+
sys.exit(42)
1017+
""")
1018+
1019+
with self.subTest('SystemExit'):
1020+
self.assert_run_failed_msg(SystemExit, '42', """
1021+
raise SystemExit(42)
1022+
""")
1023+
1024+
# XXX Also check os._exit() (via a subprocess)?
1025+
1026+
def test_plain_exception(self):
1027+
self.assert_run_failed_msg(Exception, 'spam', """
1028+
raise Exception("spam")
1029+
""")
1030+
1031+
def test_invalid_syntax(self):
1032+
script = dedent("""
1033+
x = 1 + 2
1034+
y = 2 + 4
1035+
z = 4 + 8
1036+
1037+
# missing close paren
1038+
print("spam"
1039+
1040+
if x + y + z < 20:
1041+
...
1042+
""")
1043+
1044+
with self.subTest('script'):
1045+
self.assert_run_failed(SyntaxError, script)
1046+
1047+
with self.subTest('module'):
1048+
modname = 'spam_spam_spam'
1049+
filename = self.add_module(modname, script)
1050+
self.assert_run_failed(SyntaxError, f"""
1051+
import {modname}
1052+
""")
1053+
1054+
def test_NameError(self):
1055+
self.assert_run_failed(NameError, """
1056+
res = spam + eggs
1057+
""")
1058+
# XXX check preserved suggestions
1059+
1060+
def test_AttributeError(self):
1061+
self.assert_run_failed(AttributeError, """
1062+
object().spam
1063+
""")
1064+
# XXX check preserved suggestions
1065+
1066+
def test_ExceptionGroup(self):
1067+
self.assert_run_failed(ExceptionGroup, """
1068+
raise ExceptionGroup('exceptions', [
1069+
Exception('spam'),
1070+
ImportError('eggs'),
1071+
])
1072+
""")
1073+
1074+
def test_user_defined_exception(self):
1075+
self.assert_run_failed_msg('MyError', 'spam', """
1076+
class MyError(Exception):
1077+
pass
1078+
raise MyError('spam')
1079+
""")
1080+
1081+
9621082
class RunFuncTests(TestBase):
9631083

9641084
def setUp(self):

0 commit comments

Comments
 (0)