summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorEric Snow <ericsnowcurrently@gmail.com>2023-11-22 21:48:45 (GMT)
committerGitHub <noreply@github.com>2023-11-22 21:48:45 (GMT)
commit5c3a129ecfbaac107bccf3083533276ee0ccc036 (patch)
treeac21a757b1ea83c51e89d9c7af70b58d20ae1025 /Lib
parent118522b972c0f94c981cf45facd776e7f309f9f5 (diff)
downloadcpython-5c3a129ecfbaac107bccf3083533276ee0ccc036.zip
cpython-5c3a129ecfbaac107bccf3083533276ee0ccc036.tar.gz
cpython-5c3a129ecfbaac107bccf3083533276ee0ccc036.tar.bz2
gh-76785: Clean up the Failure-Related _xxsubinterpreters Tests (gh-112322)
Diffstat (limited to 'Lib')
-rw-r--r--Lib/test/test__xxsubinterpreters.py194
1 files changed, 157 insertions, 37 deletions
diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py
index 97314dd..d2056c9 100644
--- a/Lib/test/test__xxsubinterpreters.py
+++ b/Lib/test/test__xxsubinterpreters.py
@@ -10,6 +10,7 @@ import unittest
import _testinternalcapi
from test import support
from test.support import import_helper
+from test.support import os_helper
from test.support import script_helper
@@ -759,43 +760,6 @@ class RunStringTests(TestBase):
with self.assertRaises(TypeError):
interpreters.run_string(self.id, b'print("spam")')
- @contextlib.contextmanager
- def assert_run_failed(self, exctype, msg=None):
- with self.assertRaises(interpreters.RunFailedError) as caught:
- yield
- if msg is None:
- self.assertEqual(str(caught.exception).split(':')[0],
- exctype.__name__)
- else:
- self.assertEqual(str(caught.exception),
- "{}: {}".format(exctype.__name__, msg))
-
- def test_invalid_syntax(self):
- with self.assert_run_failed(SyntaxError):
- # missing close paren
- interpreters.run_string(self.id, 'print("spam"')
-
- def test_failure(self):
- with self.assert_run_failed(Exception, 'spam'):
- interpreters.run_string(self.id, 'raise Exception("spam")')
-
- def test_SystemExit(self):
- with self.assert_run_failed(SystemExit, '42'):
- interpreters.run_string(self.id, 'raise SystemExit(42)')
-
- def test_sys_exit(self):
- with self.assert_run_failed(SystemExit):
- interpreters.run_string(self.id, dedent("""
- import sys
- sys.exit()
- """))
-
- with self.assert_run_failed(SystemExit, '42'):
- interpreters.run_string(self.id, dedent("""
- import sys
- sys.exit(42)
- """))
-
def test_with_shared(self):
r, w = os.pipe()
@@ -959,6 +923,162 @@ class RunStringTests(TestBase):
self.assertEqual(retcode, 0)
+class RunFailedTests(TestBase):
+
+ def setUp(self):
+ super().setUp()
+ self.id = interpreters.create()
+
+ def add_module(self, modname, text):
+ import tempfile
+ tempdir = tempfile.mkdtemp()
+ self.addCleanup(lambda: os_helper.rmtree(tempdir))
+ interpreters.run_string(self.id, dedent(f"""
+ import sys
+ sys.path.insert(0, {tempdir!r})
+ """))
+ return script_helper.make_script(tempdir, modname, text)
+
+ def run_script(self, text, *, fails=False):
+ excwrapper = interpreters.RunFailedError
+ r, w = os.pipe()
+ try:
+ script = dedent(f"""
+ import os, sys
+ os.write({w}, b'0')
+
+ # This raises an exception:
+ {{}}
+
+ # Nothing from here down should ever run.
+ os.write({w}, b'1')
+ class NeverError(Exception): pass
+ raise NeverError # never raised
+ """).format(dedent(text))
+ if fails:
+ with self.assertRaises(excwrapper) as caught:
+ interpreters.run_string(self.id, script)
+ return caught.exception
+ else:
+ interpreters.run_string(self.id, script)
+ return None
+ except:
+ raise # re-raise
+ else:
+ msg = os.read(r, 100)
+ self.assertEqual(msg, b'0')
+ finally:
+ os.close(r)
+ os.close(w)
+
+ def _assert_run_failed(self, exctype, msg, script):
+ if isinstance(exctype, str):
+ exctype_name = exctype
+ exctype = None
+ else:
+ exctype_name = exctype.__name__
+
+ # Run the script.
+ exc = self.run_script(script, fails=True)
+
+ # Check the wrapper exception.
+ if msg is None:
+ self.assertEqual(str(exc).split(':')[0],
+ exctype_name)
+ else:
+ self.assertEqual(str(exc),
+ '{}: {}'.format(exctype_name, msg))
+
+ return exc
+
+ def assert_run_failed(self, exctype, script):
+ self._assert_run_failed(exctype, None, script)
+
+ def assert_run_failed_msg(self, exctype, msg, script):
+ self._assert_run_failed(exctype, msg, script)
+
+ def test_exit(self):
+ with self.subTest('sys.exit(0)'):
+ # XXX Should an unhandled SystemExit(0) be handled as not-an-error?
+ self.assert_run_failed(SystemExit, """
+ sys.exit(0)
+ """)
+
+ with self.subTest('sys.exit()'):
+ self.assert_run_failed(SystemExit, """
+ import sys
+ sys.exit()
+ """)
+
+ with self.subTest('sys.exit(42)'):
+ self.assert_run_failed_msg(SystemExit, '42', """
+ import sys
+ sys.exit(42)
+ """)
+
+ with self.subTest('SystemExit'):
+ self.assert_run_failed_msg(SystemExit, '42', """
+ raise SystemExit(42)
+ """)
+
+ # XXX Also check os._exit() (via a subprocess)?
+
+ def test_plain_exception(self):
+ self.assert_run_failed_msg(Exception, 'spam', """
+ raise Exception("spam")
+ """)
+
+ def test_invalid_syntax(self):
+ script = dedent("""
+ x = 1 + 2
+ y = 2 + 4
+ z = 4 + 8
+
+ # missing close paren
+ print("spam"
+
+ if x + y + z < 20:
+ ...
+ """)
+
+ with self.subTest('script'):
+ self.assert_run_failed(SyntaxError, script)
+
+ with self.subTest('module'):
+ modname = 'spam_spam_spam'
+ filename = self.add_module(modname, script)
+ self.assert_run_failed(SyntaxError, f"""
+ import {modname}
+ """)
+
+ def test_NameError(self):
+ self.assert_run_failed(NameError, """
+ res = spam + eggs
+ """)
+ # XXX check preserved suggestions
+
+ def test_AttributeError(self):
+ self.assert_run_failed(AttributeError, """
+ object().spam
+ """)
+ # XXX check preserved suggestions
+
+ def test_ExceptionGroup(self):
+ self.assert_run_failed(ExceptionGroup, """
+ raise ExceptionGroup('exceptions', [
+ Exception('spam'),
+ ImportError('eggs'),
+ ])
+ """)
+
+ def test_user_defined_exception(self):
+ self.assert_run_failed_msg('MyError', 'spam', """
+ class MyError(Exception):
+ pass
+ raise MyError('spam')
+ """)
+
+
class RunFuncTests(TestBase):
def setUp(self):