|
1 | 1 | from ctypes import * |
2 | | -import unittest, sys |
| 2 | +import contextlib |
| 3 | +from test import support |
| 4 | +import unittest |
| 5 | +import sys |
| 6 | + |
3 | 7 |
|
4 | 8 | def callback_func(arg): |
5 | 9 | 42 / arg |
@@ -34,41 +38,40 @@ class CallbackTracbackTestCase(unittest.TestCase): |
34 | 38 | # created, then a full traceback printed. When SystemExit is |
35 | 39 | # raised in a callback function, the interpreter exits. |
36 | 40 |
|
37 | | - def capture_stderr(self, func, *args, **kw): |
38 | | - # helper - call function 'func', and return the captured stderr |
39 | | - import io |
40 | | - old_stderr = sys.stderr |
41 | | - logger = sys.stderr = io.StringIO() |
42 | | - try: |
43 | | - func(*args, **kw) |
44 | | - finally: |
45 | | - sys.stderr = old_stderr |
46 | | - return logger.getvalue() |
| 41 | + @contextlib.contextmanager |
| 42 | + def expect_unraisable(self, exc_type, exc_msg=None): |
| 43 | + with support.catch_unraisable_exception() as cm: |
| 44 | + yield |
| 45 | + |
| 46 | + self.assertIsInstance(cm.unraisable.exc_value, exc_type) |
| 47 | + if exc_msg is not None: |
| 48 | + self.assertEqual(str(cm.unraisable.exc_value), exc_msg) |
| 49 | + self.assertEqual(cm.unraisable.err_msg, |
| 50 | + "Exception ignored on calling ctypes " |
| 51 | + "callback function") |
| 52 | + self.assertIs(cm.unraisable.object, callback_func) |
47 | 53 |
|
48 | 54 | def test_ValueError(self): |
49 | 55 | cb = CFUNCTYPE(c_int, c_int)(callback_func) |
50 | | - out = self.capture_stderr(cb, 42) |
51 | | - self.assertEqual(out.splitlines()[-1], |
52 | | - "ValueError: 42") |
| 56 | + with self.expect_unraisable(ValueError, '42'): |
| 57 | + cb(42) |
53 | 58 |
|
54 | 59 | def test_IntegerDivisionError(self): |
55 | 60 | cb = CFUNCTYPE(c_int, c_int)(callback_func) |
56 | | - out = self.capture_stderr(cb, 0) |
57 | | - self.assertEqual(out.splitlines()[-1][:19], |
58 | | - "ZeroDivisionError: ") |
| 61 | + with self.expect_unraisable(ZeroDivisionError): |
| 62 | + cb(0) |
59 | 63 |
|
60 | 64 | def test_FloatDivisionError(self): |
61 | 65 | cb = CFUNCTYPE(c_int, c_double)(callback_func) |
62 | | - out = self.capture_stderr(cb, 0.0) |
63 | | - self.assertEqual(out.splitlines()[-1][:19], |
64 | | - "ZeroDivisionError: ") |
| 66 | + with self.expect_unraisable(ZeroDivisionError): |
| 67 | + cb(0.0) |
65 | 68 |
|
66 | 69 | def test_TypeErrorDivisionError(self): |
67 | 70 | cb = CFUNCTYPE(c_int, c_char_p)(callback_func) |
68 | | - out = self.capture_stderr(cb, b"spam") |
69 | | - self.assertEqual(out.splitlines()[-1], |
70 | | - "TypeError: " |
71 | | - "unsupported operand type(s) for /: 'int' and 'bytes'") |
| 71 | + err_msg = "unsupported operand type(s) for /: 'int' and 'bytes'" |
| 72 | + with self.expect_unraisable(TypeError, err_msg): |
| 73 | + cb(b"spam") |
| 74 | + |
72 | 75 |
|
73 | 76 | if __name__ == '__main__': |
74 | 77 | unittest.main() |
0 commit comments