| import unittest |
| from ctypes import * |
| import _ctypes_test |
| |
| class Callbacks(unittest.TestCase): |
| functype = CFUNCTYPE |
| |
| ## def tearDown(self): |
| ## import gc |
| ## gc.collect() |
| |
| def callback(self, *args): |
| self.got_args = args |
| return args[-1] |
| |
| def check_type(self, typ, arg): |
| PROTO = self.functype.im_func(typ, typ) |
| result = PROTO(self.callback)(arg) |
| if typ == c_float: |
| self.assertAlmostEqual(result, arg, places=5) |
| else: |
| self.assertEqual(self.got_args, (arg,)) |
| self.assertEqual(result, arg) |
| |
| PROTO = self.functype.im_func(typ, c_byte, typ) |
| result = PROTO(self.callback)(-3, arg) |
| if typ == c_float: |
| self.assertAlmostEqual(result, arg, places=5) |
| else: |
| self.assertEqual(self.got_args, (-3, arg)) |
| self.assertEqual(result, arg) |
| |
| ################ |
| |
| def test_byte(self): |
| self.check_type(c_byte, 42) |
| self.check_type(c_byte, -42) |
| |
| def test_ubyte(self): |
| self.check_type(c_ubyte, 42) |
| |
| def test_short(self): |
| self.check_type(c_short, 42) |
| self.check_type(c_short, -42) |
| |
| def test_ushort(self): |
| self.check_type(c_ushort, 42) |
| |
| def test_int(self): |
| self.check_type(c_int, 42) |
| self.check_type(c_int, -42) |
| |
| def test_uint(self): |
| self.check_type(c_uint, 42) |
| |
| def test_long(self): |
| self.check_type(c_long, 42) |
| self.check_type(c_long, -42) |
| |
| def test_ulong(self): |
| self.check_type(c_ulong, 42) |
| |
| def test_longlong(self): |
| # test some 64-bit values, positive and negative |
| self.check_type(c_longlong, 5948291757245277467) |
| self.check_type(c_longlong, -5229388909784190580) |
| self.check_type(c_longlong, 42) |
| self.check_type(c_longlong, -42) |
| |
| def test_ulonglong(self): |
| # test some 64-bit values, with and without msb set. |
| self.check_type(c_ulonglong, 10955412242170339782) |
| self.check_type(c_ulonglong, 3665885499841167458) |
| self.check_type(c_ulonglong, 42) |
| |
| def test_float(self): |
| # only almost equal: double -> float -> double |
| import math |
| self.check_type(c_float, math.e) |
| self.check_type(c_float, -math.e) |
| |
| def test_double(self): |
| self.check_type(c_double, 3.14) |
| self.check_type(c_double, -3.14) |
| |
| def test_longdouble(self): |
| self.check_type(c_longdouble, 3.14) |
| self.check_type(c_longdouble, -3.14) |
| |
| def test_char(self): |
| self.check_type(c_char, "x") |
| self.check_type(c_char, "a") |
| |
| # disabled: would now (correctly) raise a RuntimeWarning about |
| # a memory leak. A callback function cannot return a non-integral |
| # C type without causing a memory leak. |
| ## def test_char_p(self): |
| ## self.check_type(c_char_p, "abc") |
| ## self.check_type(c_char_p, "def") |
| |
| def test_pyobject(self): |
| o = () |
| from sys import getrefcount as grc |
| for o in (), [], object(): |
| initial = grc(o) |
| # This call leaks a reference to 'o'... |
| self.check_type(py_object, o) |
| before = grc(o) |
| # ...but this call doesn't leak any more. Where is the refcount? |
| self.check_type(py_object, o) |
| after = grc(o) |
| self.assertEqual((after, o), (before, o)) |
| |
| def test_unsupported_restype_1(self): |
| # Only "fundamental" result types are supported for callback |
| # functions, the type must have a non-NULL stgdict->setfunc. |
| # POINTER(c_double), for example, is not supported. |
| |
| prototype = self.functype.im_func(POINTER(c_double)) |
| # The type is checked when the prototype is called |
| self.assertRaises(TypeError, prototype, lambda: None) |
| |
| def test_unsupported_restype_2(self): |
| prototype = self.functype.im_func(object) |
| self.assertRaises(TypeError, prototype, lambda: None) |
| |
| def test_issue_7959(self): |
| proto = self.functype.im_func(None) |
| |
| class X(object): |
| def func(self): pass |
| def __init__(self): |
| self.v = proto(self.func) |
| |
| import gc |
| for i in range(32): |
| X() |
| gc.collect() |
| live = [x for x in gc.get_objects() |
| if isinstance(x, X)] |
| self.assertEqual(len(live), 0) |
| |
| def test_issue12483(self): |
| import gc |
| class Nasty: |
| def __del__(self): |
| gc.collect() |
| CFUNCTYPE(None)(lambda x=Nasty(): None) |
| |
| |
| try: |
| WINFUNCTYPE |
| except NameError: |
| pass |
| else: |
| class StdcallCallbacks(Callbacks): |
| functype = WINFUNCTYPE |
| |
| ################################################################ |
| |
| class SampleCallbacksTestCase(unittest.TestCase): |
| |
| def test_integrate(self): |
| # Derived from some then non-working code, posted by David Foster |
| dll = CDLL(_ctypes_test.__file__) |
| |
| # The function prototype called by 'integrate': double func(double); |
| CALLBACK = CFUNCTYPE(c_double, c_double) |
| |
| # The integrate function itself, exposed from the _ctypes_test dll |
| integrate = dll.integrate |
| integrate.argtypes = (c_double, c_double, CALLBACK, c_long) |
| integrate.restype = c_double |
| |
| def func(x): |
| return x**2 |
| |
| result = integrate(0.0, 1.0, CALLBACK(func), 10) |
| diff = abs(result - 1./3.) |
| |
| self.assertLess(diff, 0.01, "%s not less than 0.01" % diff) |
| |
| def test_issue_8959_a(self): |
| from ctypes.util import find_library |
| libc_path = find_library("c") |
| if not libc_path: |
| return # cannot test |
| libc = CDLL(libc_path) |
| |
| @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int)) |
| def cmp_func(a, b): |
| return a[0] - b[0] |
| |
| array = (c_int * 5)(5, 1, 99, 7, 33) |
| |
| libc.qsort(array, len(array), sizeof(c_int), cmp_func) |
| self.assertEqual(array[:], [1, 5, 7, 33, 99]) |
| |
| try: |
| WINFUNCTYPE |
| except NameError: |
| pass |
| else: |
| def test_issue_8959_b(self): |
| from ctypes.wintypes import BOOL, HWND, LPARAM |
| global windowCount |
| windowCount = 0 |
| |
| @WINFUNCTYPE(BOOL, HWND, LPARAM) |
| def EnumWindowsCallbackFunc(hwnd, lParam): |
| global windowCount |
| windowCount += 1 |
| return True #Allow windows to keep enumerating |
| |
| windll.user32.EnumWindows(EnumWindowsCallbackFunc, 0) |
| |
| def test_callback_register_int(self): |
| # Issue #8275: buggy handling of callback args under Win64 |
| # NOTE: should be run on release builds as well |
| dll = CDLL(_ctypes_test.__file__) |
| CALLBACK = CFUNCTYPE(c_int, c_int, c_int, c_int, c_int, c_int) |
| # All this function does is call the callback with its args squared |
| func = dll._testfunc_cbk_reg_int |
| func.argtypes = (c_int, c_int, c_int, c_int, c_int, CALLBACK) |
| func.restype = c_int |
| |
| def callback(a, b, c, d, e): |
| return a + b + c + d + e |
| |
| result = func(2, 3, 4, 5, 6, CALLBACK(callback)) |
| self.assertEqual(result, callback(2*2, 3*3, 4*4, 5*5, 6*6)) |
| |
| def test_callback_register_double(self): |
| # Issue #8275: buggy handling of callback args under Win64 |
| # NOTE: should be run on release builds as well |
| dll = CDLL(_ctypes_test.__file__) |
| CALLBACK = CFUNCTYPE(c_double, c_double, c_double, c_double, |
| c_double, c_double) |
| # All this function does is call the callback with its args squared |
| func = dll._testfunc_cbk_reg_double |
| func.argtypes = (c_double, c_double, c_double, |
| c_double, c_double, CALLBACK) |
| func.restype = c_double |
| |
| def callback(a, b, c, d, e): |
| return a + b + c + d + e |
| |
| result = func(1.1, 2.2, 3.3, 4.4, 5.5, CALLBACK(callback)) |
| self.assertEqual(result, |
| callback(1.1*1.1, 2.2*2.2, 3.3*3.3, 4.4*4.4, 5.5*5.5)) |
| |
| |
| ################################################################ |
| |
| if __name__ == '__main__': |
| unittest.main() |