Home | History | Annotate | Download | only in test
      1 from ctypes import *
      2 import unittest, sys
      3 
      4 def callback_func(arg):
      5     42 / arg
      6     raise ValueError(arg)
      7 
      8 @unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
      9 class call_function_TestCase(unittest.TestCase):
     10     # _ctypes.call_function is deprecated and private, but used by
     11     # Gary Bishp's readline module.  If we have it, we must test it as well.
     12 
     13     def test(self):
     14         from _ctypes import call_function
     15         windll.kernel32.LoadLibraryA.restype = c_void_p
     16         windll.kernel32.GetProcAddress.argtypes = c_void_p, c_char_p
     17         windll.kernel32.GetProcAddress.restype = c_void_p
     18 
     19         hdll = windll.kernel32.LoadLibraryA(b"kernel32")
     20         funcaddr = windll.kernel32.GetProcAddress(hdll, b"GetModuleHandleA")
     21 
     22         self.assertEqual(call_function(funcaddr, (None,)),
     23                              windll.kernel32.GetModuleHandleA(None))
     24 
     25 class CallbackTracbackTestCase(unittest.TestCase):
     26     # When an exception is raised in a ctypes callback function, the C
     27     # code prints a traceback.
     28     #
     29     # This test makes sure the exception types *and* the exception
     30     # value is printed correctly.
     31     #
     32     # Changed in 0.9.3: No longer is '(in callback)' prepended to the
     33     # error message - instead an additional frame for the C code is
     34     # created, then a full traceback printed.  When SystemExit is
     35     # raised in a callback function, the interpreter exits.
     36 
     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()
     47 
     48     def test_ValueError(self):
     49         cb = CFUNCTYPE(c_int, c_int)(callback_func)
     50         out = self.capture_stderr(cb, 42)
     51         self.assertEqual(out.splitlines()[-1],
     52                              "ValueError: 42")
     53 
     54     def test_IntegerDivisionError(self):
     55         cb = CFUNCTYPE(c_int, c_int)(callback_func)
     56         out = self.capture_stderr(cb, 0)
     57         self.assertEqual(out.splitlines()[-1][:19],
     58                              "ZeroDivisionError: ")
     59 
     60     def test_FloatDivisionError(self):
     61         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: ")
     65 
     66     def test_TypeErrorDivisionError(self):
     67         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'")
     72 
     73 if __name__ == '__main__':
     74     unittest.main()
     75