|
1 | 1 | import contextlib |
2 | 2 | import itertools |
3 | 3 | import sys |
| 4 | +import traceback |
4 | 5 | import types |
5 | 6 | import unittest |
6 | 7 | import warnings |
7 | 8 |
|
8 | | -from test.support import import_helper |
| 9 | +from test import support |
| 10 | +from test.support import import_helper, swap_attr |
9 | 11 |
|
10 | 12 | _testinternalcapi = import_helper.import_module('_testinternalcapi') |
11 | 13 | _interpreters = import_helper.import_module('_interpreters') |
@@ -1491,5 +1493,50 @@ def test_builtin_objects(self): |
1491 | 1493 | ]) |
1492 | 1494 |
|
1493 | 1495 |
|
| 1496 | +class CaptureExceptionTests(unittest.TestCase): |
| 1497 | + |
| 1498 | + # Prevent crashes with incompatible TracebackException.format(). |
| 1499 | + # Regression test for https://github.com/python/cpython/issues/143377. |
| 1500 | + |
| 1501 | + def capture_with_formatter(self, exc, formatter): |
| 1502 | + with swap_attr(traceback.TracebackException, "format", formatter): |
| 1503 | + return _interpreters.capture_exception(exc) |
| 1504 | + |
| 1505 | + def test_capture_exception(self): |
| 1506 | + captured = _interpreters.capture_exception(ValueError("hello")) |
| 1507 | + |
| 1508 | + self.assertEqual(captured.type.__name__, "ValueError") |
| 1509 | + self.assertEqual(captured.type.__qualname__, "ValueError") |
| 1510 | + self.assertEqual(captured.type.__module__, "builtins") |
| 1511 | + |
| 1512 | + self.assertEqual(captured.msg, "hello") |
| 1513 | + self.assertEqual(captured.formatted, "ValueError: hello") |
| 1514 | + |
| 1515 | + def test_capture_exception_custom_format(self): |
| 1516 | + exc = ValueError("good bye!") |
| 1517 | + formatter = lambda self: ["hello\n", "world\n"] |
| 1518 | + captured = self.capture_with_formatter(exc, formatter) |
| 1519 | + self.assertEqual(captured.msg, "good bye!") |
| 1520 | + self.assertEqual(captured.formatted, "ValueError: good bye!") |
| 1521 | + self.assertEqual(captured.errdisplay, "hello\nworld") |
| 1522 | + |
| 1523 | + @support.subTests("exc_lines", ([], ["x-no-nl"], ["x-no-nl", "y-no-nl"])) |
| 1524 | + def test_capture_exception_invalid_format(self, exc_lines): |
| 1525 | + formatter = lambda self: exc_lines |
| 1526 | + captured = self.capture_with_formatter(ValueError(), formatter) |
| 1527 | + self.assertEqual(captured.msg, "") |
| 1528 | + self.assertEqual(captured.formatted, "ValueError: ") |
| 1529 | + self.assertEqual(captured.errdisplay, "".join(exc_lines)) |
| 1530 | + |
| 1531 | + def test_capture_exception_unraisable_exception(self): |
| 1532 | + formatter = lambda self: 1 |
| 1533 | + with support.catch_unraisable_exception() as cm: |
| 1534 | + captured = self.capture_with_formatter(ValueError(), formatter) |
| 1535 | + self.assertNotHasAttr(captured, "errdisplay") |
| 1536 | + self.assertEqual(cm.unraisable.exc_type, TypeError) |
| 1537 | + self.assertEqual(str(cm.unraisable.exc_value), |
| 1538 | + "can only join an iterable") |
| 1539 | + |
| 1540 | + |
1494 | 1541 | if __name__ == '__main__': |
1495 | 1542 | unittest.main() |
0 commit comments