1 import unittest 2 from ctypes import * 3 import _ctypes_test 4 5 class Callbacks(unittest.TestCase): 6 functype = CFUNCTYPE 7 8 ## def tearDown(self): 9 ## import gc 10 ## gc.collect() 11 12 def callback(self, *args): 13 self.got_args = args 14 return args[-1] 15 16 def check_type(self, typ, arg): 17 PROTO = self.functype.im_func(typ, typ) 18 result = PROTO(self.callback)(arg) 19 if typ == c_float: 20 self.assertAlmostEqual(result, arg, places=5) 21 else: 22 self.assertEqual(self.got_args, (arg,)) 23 self.assertEqual(result, arg) 24 25 PROTO = self.functype.im_func(typ, c_byte, typ) 26 result = PROTO(self.callback)(-3, arg) 27 if typ == c_float: 28 self.assertAlmostEqual(result, arg, places=5) 29 else: 30 self.assertEqual(self.got_args, (-3, arg)) 31 self.assertEqual(result, arg) 32 33 ################ 34 35 def test_byte(self): 36 self.check_type(c_byte, 42) 37 self.check_type(c_byte, -42) 38 39 def test_ubyte(self): 40 self.check_type(c_ubyte, 42) 41 42 def test_short(self): 43 self.check_type(c_short, 42) 44 self.check_type(c_short, -42) 45 46 def test_ushort(self): 47 self.check_type(c_ushort, 42) 48 49 def test_int(self): 50 self.check_type(c_int, 42) 51 self.check_type(c_int, -42) 52 53 def test_uint(self): 54 self.check_type(c_uint, 42) 55 56 def test_long(self): 57 self.check_type(c_long, 42) 58 self.check_type(c_long, -42) 59 60 def test_ulong(self): 61 self.check_type(c_ulong, 42) 62 63 def test_longlong(self): 64 # test some 64-bit values, positive and negative 65 self.check_type(c_longlong, 5948291757245277467) 66 self.check_type(c_longlong, -5229388909784190580) 67 self.check_type(c_longlong, 42) 68 self.check_type(c_longlong, -42) 69 70 def test_ulonglong(self): 71 # test some 64-bit values, with and without msb set. 72 self.check_type(c_ulonglong, 10955412242170339782) 73 self.check_type(c_ulonglong, 3665885499841167458) 74 self.check_type(c_ulonglong, 42) 75 76 def test_float(self): 77 # only almost equal: double -> float -> double 78 import math 79 self.check_type(c_float, math.e) 80 self.check_type(c_float, -math.e) 81 82 def test_double(self): 83 self.check_type(c_double, 3.14) 84 self.check_type(c_double, -3.14) 85 86 def test_longdouble(self): 87 self.check_type(c_longdouble, 3.14) 88 self.check_type(c_longdouble, -3.14) 89 90 def test_char(self): 91 self.check_type(c_char, "x") 92 self.check_type(c_char, "a") 93 94 # disabled: would now (correctly) raise a RuntimeWarning about 95 # a memory leak. A callback function cannot return a non-integral 96 # C type without causing a memory leak. 97 ## def test_char_p(self): 98 ## self.check_type(c_char_p, "abc") 99 ## self.check_type(c_char_p, "def") 100 101 def test_pyobject(self): 102 o = () 103 from sys import getrefcount as grc 104 for o in (), [], object(): 105 initial = grc(o) 106 # This call leaks a reference to 'o'... 107 self.check_type(py_object, o) 108 before = grc(o) 109 # ...but this call doesn't leak any more. Where is the refcount? 110 self.check_type(py_object, o) 111 after = grc(o) 112 self.assertEqual((after, o), (before, o)) 113 114 def test_unsupported_restype_1(self): 115 # Only "fundamental" result types are supported for callback 116 # functions, the type must have a non-NULL stgdict->setfunc. 117 # POINTER(c_double), for example, is not supported. 118 119 prototype = self.functype.im_func(POINTER(c_double)) 120 # The type is checked when the prototype is called 121 self.assertRaises(TypeError, prototype, lambda: None) 122 123 def test_unsupported_restype_2(self): 124 prototype = self.functype.im_func(object) 125 self.assertRaises(TypeError, prototype, lambda: None) 126 127 def test_issue_7959(self): 128 proto = self.functype.im_func(None) 129 130 class X(object): 131 def func(self): pass 132 def __init__(self): 133 self.v = proto(self.func) 134 135 import gc 136 for i in range(32): 137 X() 138 gc.collect() 139 live = [x for x in gc.get_objects() 140 if isinstance(x, X)] 141 self.assertEqual(len(live), 0) 142 143 def test_issue12483(self): 144 import gc 145 class Nasty: 146 def __del__(self): 147 gc.collect() 148 CFUNCTYPE(None)(lambda x=Nasty(): None) 149 150 151 try: 152 WINFUNCTYPE 153 except NameError: 154 pass 155 else: 156 class StdcallCallbacks(Callbacks): 157 functype = WINFUNCTYPE 158 159 ################################################################ 160 161 class SampleCallbacksTestCase(unittest.TestCase): 162 163 def test_integrate(self): 164 # Derived from some then non-working code, posted by David Foster 165 dll = CDLL(_ctypes_test.__file__) 166 167 # The function prototype called by 'integrate': double func(double); 168 CALLBACK = CFUNCTYPE(c_double, c_double) 169 170 # The integrate function itself, exposed from the _ctypes_test dll 171 integrate = dll.integrate 172 integrate.argtypes = (c_double, c_double, CALLBACK, c_long) 173 integrate.restype = c_double 174 175 def func(x): 176 return x**2 177 178 result = integrate(0.0, 1.0, CALLBACK(func), 10) 179 diff = abs(result - 1./3.) 180 181 self.assertLess(diff, 0.01, "%s not less than 0.01" % diff) 182 183 def test_issue_8959_a(self): 184 from ctypes.util import find_library 185 libc_path = find_library("c") 186 if not libc_path: 187 return # cannot test 188 libc = CDLL(libc_path) 189 190 @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int)) 191 def cmp_func(a, b): 192 return a[0] - b[0] 193 194 array = (c_int * 5)(5, 1, 99, 7, 33) 195 196 libc.qsort(array, len(array), sizeof(c_int), cmp_func) 197 self.assertEqual(array[:], [1, 5, 7, 33, 99]) 198 199 try: 200 WINFUNCTYPE 201 except NameError: 202 pass 203 else: 204 def test_issue_8959_b(self): 205 from ctypes.wintypes import BOOL, HWND, LPARAM 206 global windowCount 207 windowCount = 0 208 209 @WINFUNCTYPE(BOOL, HWND, LPARAM) 210 def EnumWindowsCallbackFunc(hwnd, lParam): 211 global windowCount 212 windowCount += 1 213 return True #Allow windows to keep enumerating 214 215 windll.user32.EnumWindows(EnumWindowsCallbackFunc, 0) 216 217 def test_callback_register_int(self): 218 # Issue #8275: buggy handling of callback args under Win64 219 # NOTE: should be run on release builds as well 220 dll = CDLL(_ctypes_test.__file__) 221 CALLBACK = CFUNCTYPE(c_int, c_int, c_int, c_int, c_int, c_int) 222 # All this function does is call the callback with its args squared 223 func = dll._testfunc_cbk_reg_int 224 func.argtypes = (c_int, c_int, c_int, c_int, c_int, CALLBACK) 225 func.restype = c_int 226 227 def callback(a, b, c, d, e): 228 return a + b + c + d + e 229 230 result = func(2, 3, 4, 5, 6, CALLBACK(callback)) 231 self.assertEqual(result, callback(2*2, 3*3, 4*4, 5*5, 6*6)) 232 233 def test_callback_register_double(self): 234 # Issue #8275: buggy handling of callback args under Win64 235 # NOTE: should be run on release builds as well 236 dll = CDLL(_ctypes_test.__file__) 237 CALLBACK = CFUNCTYPE(c_double, c_double, c_double, c_double, 238 c_double, c_double) 239 # All this function does is call the callback with its args squared 240 func = dll._testfunc_cbk_reg_double 241 func.argtypes = (c_double, c_double, c_double, 242 c_double, c_double, CALLBACK) 243 func.restype = c_double 244 245 def callback(a, b, c, d, e): 246 return a + b + c + d + e 247 248 result = func(1.1, 2.2, 3.3, 4.4, 5.5, CALLBACK(callback)) 249 self.assertEqual(result, 250 callback(1.1*1.1, 2.2*2.2, 3.3*3.3, 4.4*4.4, 5.5*5.5)) 251 252 253 ################################################################ 254 255 if __name__ == '__main__': 256 unittest.main() 257