summaryrefslogtreecommitdiffstats
path: root/Lib/ctypes/test
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2020-05-27 22:38:12 (GMT)
committerGitHub <noreply@github.com>2020-05-27 22:38:12 (GMT)
commit10228bad0452d94e66c964b625a0b61befa08e59 (patch)
tree26a37fc4fd1292e754aff8e7d546365bf9af43a7 /Lib/ctypes/test
parente80697d687b610bd7fb9104af905dec8f0bc55a7 (diff)
downloadcpython-10228bad0452d94e66c964b625a0b61befa08e59.zip
cpython-10228bad0452d94e66c964b625a0b61befa08e59.tar.gz
cpython-10228bad0452d94e66c964b625a0b61befa08e59.tar.bz2
bpo-40795: ctypes calls unraisablehook with an exception (GH-20452)
If ctypes fails to convert the result of a callback or if a ctypes callback function raises an exception, sys.unraisablehook is now called with an exception set. Previously, the error was logged into stderr by PyErr_Print().
Diffstat (limited to 'Lib/ctypes/test')
-rw-r--r--Lib/ctypes/test/test_callbacks.py18
-rw-r--r--Lib/ctypes/test/test_random_things.py51
-rw-r--r--Lib/ctypes/test/test_unaligned_structures.py2
3 files changed, 44 insertions, 27 deletions
diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py
index 937a06d..d8e9c5a 100644
--- a/Lib/ctypes/test/test_callbacks.py
+++ b/Lib/ctypes/test/test_callbacks.py
@@ -1,5 +1,7 @@
import functools
import unittest
+from test import support
+
from ctypes import *
from ctypes.test import need_symbol
import _ctypes_test
@@ -301,8 +303,22 @@ class SampleCallbacksTestCase(unittest.TestCase):
with self.assertRaises(ArgumentError):
cb(*args2)
+ def test_convert_result_error(self):
+ def func():
+ return ("tuple",)
+
+ proto = CFUNCTYPE(c_int)
+ ctypes_func = proto(func)
+ with support.catch_unraisable_exception() as cm:
+ # don't test the result since it is an uninitialized value
+ result = ctypes_func()
+
+ self.assertIsInstance(cm.unraisable.exc_value, TypeError)
+ self.assertEqual(cm.unraisable.err_msg,
+ "Exception ignored on converting result "
+ "of ctypes callback function")
+ self.assertIs(cm.unraisable.object, func)
-################################################################
if __name__ == '__main__':
unittest.main()
diff --git a/Lib/ctypes/test/test_random_things.py b/Lib/ctypes/test/test_random_things.py
index ee5b212..2988e27 100644
--- a/Lib/ctypes/test/test_random_things.py
+++ b/Lib/ctypes/test/test_random_things.py
@@ -1,5 +1,9 @@
from ctypes import *
-import unittest, sys
+import contextlib
+from test import support
+import unittest
+import sys
+
def callback_func(arg):
42 / arg
@@ -34,41 +38,40 @@ class CallbackTracbackTestCase(unittest.TestCase):
# created, then a full traceback printed. When SystemExit is
# raised in a callback function, the interpreter exits.
- def capture_stderr(self, func, *args, **kw):
- # helper - call function 'func', and return the captured stderr
- import io
- old_stderr = sys.stderr
- logger = sys.stderr = io.StringIO()
- try:
- func(*args, **kw)
- finally:
- sys.stderr = old_stderr
- return logger.getvalue()
+ @contextlib.contextmanager
+ def expect_unraisable(self, exc_type, exc_msg=None):
+ with support.catch_unraisable_exception() as cm:
+ yield
+
+ self.assertIsInstance(cm.unraisable.exc_value, exc_type)
+ if exc_msg is not None:
+ self.assertEqual(str(cm.unraisable.exc_value), exc_msg)
+ self.assertEqual(cm.unraisable.err_msg,
+ "Exception ignored on calling ctypes "
+ "callback function")
+ self.assertIs(cm.unraisable.object, callback_func)
def test_ValueError(self):
cb = CFUNCTYPE(c_int, c_int)(callback_func)
- out = self.capture_stderr(cb, 42)
- self.assertEqual(out.splitlines()[-1],
- "ValueError: 42")
+ with self.expect_unraisable(ValueError, '42'):
+ cb(42)
def test_IntegerDivisionError(self):
cb = CFUNCTYPE(c_int, c_int)(callback_func)
- out = self.capture_stderr(cb, 0)
- self.assertEqual(out.splitlines()[-1][:19],
- "ZeroDivisionError: ")
+ with self.expect_unraisable(ZeroDivisionError):
+ cb(0)
def test_FloatDivisionError(self):
cb = CFUNCTYPE(c_int, c_double)(callback_func)
- out = self.capture_stderr(cb, 0.0)
- self.assertEqual(out.splitlines()[-1][:19],
- "ZeroDivisionError: ")
+ with self.expect_unraisable(ZeroDivisionError):
+ cb(0.0)
def test_TypeErrorDivisionError(self):
cb = CFUNCTYPE(c_int, c_char_p)(callback_func)
- out = self.capture_stderr(cb, b"spam")
- self.assertEqual(out.splitlines()[-1],
- "TypeError: "
- "unsupported operand type(s) for /: 'int' and 'bytes'")
+ err_msg = "unsupported operand type(s) for /: 'int' and 'bytes'"
+ with self.expect_unraisable(TypeError, err_msg):
+ cb(b"spam")
+
if __name__ == '__main__':
unittest.main()
diff --git a/Lib/ctypes/test/test_unaligned_structures.py b/Lib/ctypes/test/test_unaligned_structures.py
index bcacfc8..ee7fb45 100644
--- a/Lib/ctypes/test/test_unaligned_structures.py
+++ b/Lib/ctypes/test/test_unaligned_structures.py
@@ -27,7 +27,6 @@ for typ in [c_short, c_int, c_long, c_longlong,
class TestStructures(unittest.TestCase):
def test_native(self):
for typ in structures:
-## print typ.value
self.assertEqual(typ.value.offset, 1)
o = typ()
o.value = 4
@@ -35,7 +34,6 @@ class TestStructures(unittest.TestCase):
def test_swapped(self):
for typ in byteswapped_structures:
-## print >> sys.stderr, typ.value
self.assertEqual(typ.value.offset, 1)
o = typ()
o.value = 4