|
1 |
| -import builtins |
2 | 1 | from collections import namedtuple
|
3 | 2 | import contextlib
|
4 | 3 | import itertools
|
@@ -867,11 +866,10 @@ def assert_run_failed(self, exctype, msg=None):
|
867 | 866 | yield
|
868 | 867 | if msg is None:
|
869 | 868 | self.assertEqual(str(caught.exception).split(':')[0],
|
870 |
| - exctype.__name__) |
| 869 | + str(exctype)) |
871 | 870 | else:
|
872 | 871 | self.assertEqual(str(caught.exception),
|
873 |
| - "{}: {}".format(exctype.__name__, msg)) |
874 |
| - self.assertIsInstance(caught.exception.__cause__, exctype) |
| 872 | + "{}: {}".format(exctype, msg)) |
875 | 873 |
|
876 | 874 | def test_invalid_syntax(self):
|
877 | 875 | with self.assert_run_failed(SyntaxError):
|
@@ -1062,301 +1060,6 @@ def f():
|
1062 | 1060 | self.assertEqual(retcode, 0)
|
1063 | 1061 |
|
1064 | 1062 |
|
1065 |
| -def build_exception(exctype, /, *args, **kwargs): |
1066 |
| - # XXX Use __qualname__? |
1067 |
| - name = exctype.__name__ |
1068 |
| - argreprs = [repr(a) for a in args] |
1069 |
| - if kwargs: |
1070 |
| - kwargreprs = [f'{k}={v!r}' for k, v in kwargs.items()] |
1071 |
| - script = f'{name}({", ".join(argreprs)}, {", ".join(kwargreprs)})' |
1072 |
| - else: |
1073 |
| - script = f'{name}({", ".join(argreprs)})' |
1074 |
| - expected = exctype(*args, **kwargs) |
1075 |
| - return script, expected |
1076 |
| - |
1077 |
| - |
1078 |
| -def build_exceptions(self, *exctypes, default=None, custom=None, bases=True): |
1079 |
| - if not exctypes: |
1080 |
| - raise NotImplementedError |
1081 |
| - if not default: |
1082 |
| - default = ((), {}) |
1083 |
| - elif isinstance(default, str): |
1084 |
| - default = ((default,), {}) |
1085 |
| - elif type(default) is not tuple: |
1086 |
| - raise NotImplementedError |
1087 |
| - elif len(default) != 2: |
1088 |
| - default = (default, {}) |
1089 |
| - elif type(default[0]) is not tuple: |
1090 |
| - default = (default, {}) |
1091 |
| - elif type(default[1]) is not dict: |
1092 |
| - default = (default, {}) |
1093 |
| - # else leave it alone |
1094 |
| - |
1095 |
| - for exctype in exctypes: |
1096 |
| - customtype = None |
1097 |
| - values = default |
1098 |
| - if custom: |
1099 |
| - if exctype in custom: |
1100 |
| - customtype = exctype |
1101 |
| - elif bases: |
1102 |
| - for customtype in custom: |
1103 |
| - if issubclass(exctype, customtype): |
1104 |
| - break |
1105 |
| - else: |
1106 |
| - customtype = None |
1107 |
| - if customtype is not None: |
1108 |
| - values = custom[customtype] |
1109 |
| - if values is None: |
1110 |
| - continue |
1111 |
| - args, kwargs = values |
1112 |
| - script, expected = build_exception(exctype, *args, **kwargs) |
1113 |
| - yield exctype, customtype, script, expected |
1114 |
| - |
1115 |
| - |
1116 |
| -try: |
1117 |
| - raise Exception |
1118 |
| -except Exception as exc: |
1119 |
| - assert exc.__traceback__ is not None |
1120 |
| - Traceback = type(exc.__traceback__) |
1121 |
| - |
1122 |
| - |
1123 |
| -class RunFailedTests(TestBase): |
1124 |
| - |
1125 |
| - BUILTINS = [v |
1126 |
| - for v in vars(builtins).values() |
1127 |
| - if (type(v) is type |
1128 |
| - and issubclass(v, Exception) |
1129 |
| - #and issubclass(v, BaseException) |
1130 |
| - ) |
1131 |
| - ] |
1132 |
| - BUILTINS_SPECIAL = [ |
1133 |
| - # These all have extra attributes (i.e. args/kwargs) |
1134 |
| - SyntaxError, |
1135 |
| - ImportError, |
1136 |
| - UnicodeError, |
1137 |
| - OSError, |
1138 |
| - SystemExit, |
1139 |
| - StopIteration, |
1140 |
| - ] |
1141 |
| - |
1142 |
| - @classmethod |
1143 |
| - def build_exceptions(cls, exctypes=None, default=(), custom=None): |
1144 |
| - if exctypes is None: |
1145 |
| - exctypes = cls.BUILTINS |
1146 |
| - if custom is None: |
1147 |
| - # Skip the "special" ones. |
1148 |
| - custom = {et: None for et in cls.BUILTINS_SPECIAL} |
1149 |
| - yield from build_exceptions(*exctypes, default=default, custom=custom) |
1150 |
| - |
1151 |
| - def assertExceptionsEqual(self, exc, expected, *, chained=True): |
1152 |
| - if type(expected) is type: |
1153 |
| - self.assertIs(type(exc), expected) |
1154 |
| - return |
1155 |
| - elif not isinstance(exc, Exception): |
1156 |
| - self.assertEqual(exc, expected) |
1157 |
| - elif not isinstance(expected, Exception): |
1158 |
| - self.assertEqual(exc, expected) |
1159 |
| - else: |
1160 |
| - # Plain equality doesn't work, so we have to compare manually. |
1161 |
| - self.assertIs(type(exc), type(expected)) |
1162 |
| - self.assertEqual(exc.args, expected.args) |
1163 |
| - self.assertEqual(exc.__reduce__(), expected.__reduce__()) |
1164 |
| - if chained: |
1165 |
| - self.assertExceptionsEqual(exc.__context__, |
1166 |
| - expected.__context__) |
1167 |
| - self.assertExceptionsEqual(exc.__cause__, |
1168 |
| - expected.__cause__) |
1169 |
| - self.assertEqual(exc.__suppress_context__, |
1170 |
| - expected.__suppress_context__) |
1171 |
| - |
1172 |
| - def assertTracebacksEqual(self, tb, expected): |
1173 |
| - if not isinstance(tb, Traceback): |
1174 |
| - self.assertEqual(tb, expected) |
1175 |
| - elif not isinstance(expected, Traceback): |
1176 |
| - self.assertEqual(tb, expected) |
1177 |
| - else: |
1178 |
| - self.assertEqual(tb.tb_frame.f_code.co_name, |
1179 |
| - expected.tb_frame.f_code.co_name) |
1180 |
| - self.assertEqual(tb.tb_frame.f_code.co_filename, |
1181 |
| - expected.tb_frame.f_code.co_filename) |
1182 |
| - self.assertEqual(tb.tb_lineno, expected.tb_lineno) |
1183 |
| - self.assertTracebacksEqual(tb.tb_next, expected.tb_next) |
1184 |
| - |
1185 |
| - # XXX Move this to TestBase? |
1186 |
| - @contextlib.contextmanager |
1187 |
| - def expected_run_failure(self, expected): |
1188 |
| - exctype = expected if type(expected) is type else type(expected) |
1189 |
| - |
1190 |
| - with self.assertRaises(interpreters.RunFailedError) as caught: |
1191 |
| - yield caught |
1192 |
| - exc = caught.exception |
1193 |
| - |
1194 |
| - modname = exctype.__module__ |
1195 |
| - if modname == 'builtins' or modname == '__main__': |
1196 |
| - exctypename = exctype.__name__ |
1197 |
| - else: |
1198 |
| - exctypename = f'{modname}.{exctype.__name__}' |
1199 |
| - if exctype is expected: |
1200 |
| - self.assertEqual(str(exc).split(':')[0], exctypename) |
1201 |
| - else: |
1202 |
| - self.assertEqual(str(exc), f'{exctypename}: {expected}') |
1203 |
| - self.assertExceptionsEqual(exc.__cause__, expected) |
1204 |
| - if exc.__cause__ is not None: |
1205 |
| - self.assertIsNotNone(exc.__cause__.__traceback__) |
1206 |
| - |
1207 |
| - def test_builtin_exceptions(self): |
1208 |
| - interpid = interpreters.create() |
1209 |
| - msg = '<a message>' |
1210 |
| - for i, info in enumerate(self.build_exceptions( |
1211 |
| - default=msg, |
1212 |
| - custom={ |
1213 |
| - SyntaxError: ((msg, '<stdin>', 1, 3, 'a +?'), {}), |
1214 |
| - ImportError: ((msg,), {'name': 'spam', 'path': '/x/spam.py'}), |
1215 |
| - UnicodeError: None, |
1216 |
| - #UnicodeError: ((), {}), |
1217 |
| - #OSError: ((), {}), |
1218 |
| - SystemExit: ((1,), {}), |
1219 |
| - StopIteration: (('<a value>',), {}), |
1220 |
| - }, |
1221 |
| - )): |
1222 |
| - exctype, _, script, expected = info |
1223 |
| - testname = f'{i+1} - {script}' |
1224 |
| - script = f'raise {script}' |
1225 |
| - |
1226 |
| - with self.subTest(testname): |
1227 |
| - with self.expected_run_failure(expected): |
1228 |
| - interpreters.run_string(interpid, script) |
1229 |
| - |
1230 |
| - def test_custom_exception_from___main__(self): |
1231 |
| - script = dedent(""" |
1232 |
| - class SpamError(Exception): |
1233 |
| - def __init__(self, q): |
1234 |
| - super().__init__(f'got {q}') |
1235 |
| - self.q = q |
1236 |
| - raise SpamError('eggs') |
1237 |
| - """) |
1238 |
| - expected = Exception(f'SpamError: got {"eggs"}') |
1239 |
| - |
1240 |
| - interpid = interpreters.create() |
1241 |
| - with self.assertRaises(interpreters.RunFailedError) as caught: |
1242 |
| - interpreters.run_string(interpid, script) |
1243 |
| - cause = caught.exception.__cause__ |
1244 |
| - |
1245 |
| - self.assertExceptionsEqual(cause, expected) |
1246 |
| - |
1247 |
| - class SpamError(Exception): |
1248 |
| - # The normal Exception.__reduce__() produces a funny result |
1249 |
| - # here. So we have to use a custom __new__(). |
1250 |
| - def __new__(cls, q): |
1251 |
| - if type(q) is SpamError: |
1252 |
| - return q |
1253 |
| - return super().__new__(cls, q) |
1254 |
| - def __init__(self, q): |
1255 |
| - super().__init__(f'got {q}') |
1256 |
| - self.q = q |
1257 |
| - |
1258 |
| - def test_custom_exception(self): |
1259 |
| - script = dedent(""" |
1260 |
| - import test.test__xxsubinterpreters |
1261 |
| - SpamError = test.test__xxsubinterpreters.RunFailedTests.SpamError |
1262 |
| - raise SpamError('eggs') |
1263 |
| - """) |
1264 |
| - try: |
1265 |
| - ns = {} |
1266 |
| - exec(script, ns, ns) |
1267 |
| - except Exception as exc: |
1268 |
| - expected = exc |
1269 |
| - |
1270 |
| - interpid = interpreters.create() |
1271 |
| - with self.expected_run_failure(expected): |
1272 |
| - interpreters.run_string(interpid, script) |
1273 |
| - |
1274 |
| - class SpamReducedError(Exception): |
1275 |
| - def __init__(self, q): |
1276 |
| - super().__init__(f'got {q}') |
1277 |
| - self.q = q |
1278 |
| - def __reduce__(self): |
1279 |
| - return (type(self), (self.q,), {}) |
1280 |
| - |
1281 |
| - def test_custom___reduce__(self): |
1282 |
| - script = dedent(""" |
1283 |
| - import test.test__xxsubinterpreters |
1284 |
| - SpamError = test.test__xxsubinterpreters.RunFailedTests.SpamReducedError |
1285 |
| - raise SpamError('eggs') |
1286 |
| - """) |
1287 |
| - try: |
1288 |
| - exec(script, (ns := {'__name__': '__main__'}), ns) |
1289 |
| - except Exception as exc: |
1290 |
| - expected = exc |
1291 |
| - |
1292 |
| - interpid = interpreters.create() |
1293 |
| - with self.expected_run_failure(expected): |
1294 |
| - interpreters.run_string(interpid, script) |
1295 |
| - |
1296 |
| - def test_traceback_propagated(self): |
1297 |
| - script = dedent(""" |
1298 |
| - def do_spam(): |
1299 |
| - raise Exception('uh-oh') |
1300 |
| - def do_eggs(): |
1301 |
| - return do_spam() |
1302 |
| - class Spam: |
1303 |
| - def do(self): |
1304 |
| - return do_eggs() |
1305 |
| - def get_handler(): |
1306 |
| - def handler(): |
1307 |
| - return Spam().do() |
1308 |
| - return handler |
1309 |
| - go = (lambda: get_handler()()) |
1310 |
| - def iter_all(): |
1311 |
| - yield from (go() for _ in [True]) |
1312 |
| - yield None |
1313 |
| - def main(): |
1314 |
| - for v in iter_all(): |
1315 |
| - pass |
1316 |
| - main() |
1317 |
| - """) |
1318 |
| - try: |
1319 |
| - ns = {} |
1320 |
| - exec(script, ns, ns) |
1321 |
| - except Exception as exc: |
1322 |
| - expected = exc |
1323 |
| - expectedtb = exc.__traceback__.tb_next |
1324 |
| - |
1325 |
| - interpid = interpreters.create() |
1326 |
| - with self.expected_run_failure(expected) as caught: |
1327 |
| - interpreters.run_string(interpid, script) |
1328 |
| - exc = caught.exception |
1329 |
| - |
1330 |
| - self.assertTracebacksEqual(exc.__cause__.__traceback__, |
1331 |
| - expectedtb) |
1332 |
| - |
1333 |
| - def test_chained_exceptions(self): |
1334 |
| - script = dedent(""" |
1335 |
| - try: |
1336 |
| - raise ValueError('msg 1') |
1337 |
| - except Exception as exc1: |
1338 |
| - try: |
1339 |
| - raise TypeError('msg 2') |
1340 |
| - except Exception as exc2: |
1341 |
| - try: |
1342 |
| - raise IndexError('msg 3') from exc2 |
1343 |
| - except Exception: |
1344 |
| - raise AttributeError('msg 4') |
1345 |
| - """) |
1346 |
| - try: |
1347 |
| - exec(script, {}, {}) |
1348 |
| - except Exception as exc: |
1349 |
| - expected = exc |
1350 |
| - |
1351 |
| - interpid = interpreters.create() |
1352 |
| - with self.expected_run_failure(expected) as caught: |
1353 |
| - interpreters.run_string(interpid, script) |
1354 |
| - exc = caught.exception |
1355 |
| - |
1356 |
| - # ...just to be sure. |
1357 |
| - self.assertIs(type(exc.__cause__), AttributeError) |
1358 |
| - |
1359 |
| - |
1360 | 1063 | ##################################
|
1361 | 1064 | # channel tests
|
1362 | 1065 |
|
|
0 commit comments