]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-76785: Clean up the Failure-Related _xxsubinterpreters Tests (gh-112322)
authorEric Snow <ericsnowcurrently@gmail.com>
Wed, 22 Nov 2023 21:48:45 +0000 (14:48 -0700)
committerGitHub <noreply@github.com>
Wed, 22 Nov 2023 21:48:45 +0000 (14:48 -0700)
Lib/test/test__xxsubinterpreters.py

index 97314ddbb55ec8364a0b10c7cbe69a2ddbfd7b3d..d2056c9f52287b43715339e849655035262fce45 100644 (file)
@@ -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):