1 import unittest 2 import ctypes 3 import gc 4 5 MyCallback = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int) 6 OtherCallback = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_ulonglong) 7 8 import _ctypes_test 9 dll = ctypes.CDLL(_ctypes_test.__file__) 10 11 class RefcountTestCase(unittest.TestCase): 12 13 def test_1(self): 14 from sys import getrefcount as grc 15 16 f = dll._testfunc_callback_i_if 17 f.restype = ctypes.c_int 18 f.argtypes = [ctypes.c_int, MyCallback] 19 20 def callback(value): 21 #print "called back with", value 22 return value 23 24 self.assertEqual(grc(callback), 2) 25 cb = MyCallback(callback) 26 27 self.assertTrue(grc(callback) > 2) 28 result = f(-10, cb) 29 self.assertEqual(result, -18) 30 cb = None 31 32 gc.collect() 33 34 self.assertEqual(grc(callback), 2) 35 36 37 def test_refcount(self): 38 from sys import getrefcount as grc 39 def func(*args): 40 pass 41 # this is the standard refcount for func 42 self.assertEqual(grc(func), 2) 43 44 # the CFuncPtr instance holds atr least one refcount on func: 45 f = OtherCallback(func) 46 self.assertTrue(grc(func) > 2) 47 48 # and may release it again 49 del f 50 self.assertTrue(grc(func) >= 2) 51 52 # but now it must be gone 53 gc.collect() 54 self.assertTrue(grc(func) == 2) 55 56 class X(ctypes.Structure): 57 _fields_ = [("a", OtherCallback)] 58 x = X() 59 x.a = OtherCallback(func) 60 61 # the CFuncPtr instance holds atr least one refcount on func: 62 self.assertTrue(grc(func) > 2) 63 64 # and may release it again 65 del x 66 self.assertTrue(grc(func) >= 2) 67 68 # and now it must be gone again 69 gc.collect() 70 self.assertEqual(grc(func), 2) 71 72 f = OtherCallback(func) 73 74 # the CFuncPtr instance holds atr least one refcount on func: 75 self.assertTrue(grc(func) > 2) 76 77 # create a cycle 78 f.cycle = f 79 80 del f 81 gc.collect() 82 self.assertEqual(grc(func), 2) 83 84 class AnotherLeak(unittest.TestCase): 85 def test_callback(self): 86 import sys 87 88 proto = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int) 89 def func(a, b): 90 return a * b * 2 91 f = proto(func) 92 93 a = sys.getrefcount(ctypes.c_int) 94 f(1, 2) 95 self.assertEqual(sys.getrefcount(ctypes.c_int), a) 96 97 if __name__ == '__main__': 98 unittest.main() 99