|
10 | 10 | import _testinternalcapi
|
11 | 11 | from test import support
|
12 | 12 | from test.support import import_helper
|
| 13 | +from test.support import os_helper |
13 | 14 | from test.support import script_helper
|
14 | 15 |
|
15 | 16 |
|
@@ -759,43 +760,6 @@ def test_bytes_for_script(self):
|
759 | 760 | with self.assertRaises(TypeError):
|
760 | 761 | interpreters.run_string(self.id, b'print("spam")')
|
761 | 762 |
|
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 |
| - |
799 | 763 | def test_with_shared(self):
|
800 | 764 | r, w = os.pipe()
|
801 | 765 |
|
@@ -959,6 +923,162 @@ def f():
|
959 | 923 | self.assertEqual(retcode, 0)
|
960 | 924 |
|
961 | 925 |
|
| 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 | + |
962 | 1082 | class RunFuncTests(TestBase):
|
963 | 1083 |
|
964 | 1084 | def setUp(self):
|
|
0 commit comments