1from ctypes import * 2import unittest 3import struct 4 5def valid_ranges(*types): 6 # given a sequence of numeric types, collect their _type_ 7 # attribute, which is a single format character compatible with 8 # the struct module, use the struct module to calculate the 9 # minimum and maximum value allowed for this format. 10 # Returns a list of (min, max) values. 11 result = [] 12 for t in types: 13 fmt = t._type_ 14 size = struct.calcsize(fmt) 15 a = struct.unpack(fmt, (b"\x00"*32)[:size])[0] 16 b = struct.unpack(fmt, (b"\xFF"*32)[:size])[0] 17 c = struct.unpack(fmt, (b"\x7F"+b"\x00"*32)[:size])[0] 18 d = struct.unpack(fmt, (b"\x80"+b"\xFF"*32)[:size])[0] 19 result.append((min(a, b, c, d), max(a, b, c, d))) 20 return result 21 22ArgType = type(byref(c_int(0))) 23 24unsigned_types = [c_ubyte, c_ushort, c_uint, c_ulong] 25signed_types = [c_byte, c_short, c_int, c_long, c_longlong] 26 27bool_types = [] 28 29float_types = [c_double, c_float] 30 31try: 32 c_ulonglong 33 c_longlong 34except NameError: 35 pass 36else: 37 unsigned_types.append(c_ulonglong) 38 signed_types.append(c_longlong) 39 40try: 41 c_bool 42except NameError: 43 pass 44else: 45 bool_types.append(c_bool) 46 47unsigned_ranges = valid_ranges(*unsigned_types) 48signed_ranges = valid_ranges(*signed_types) 49bool_values = [True, False, 0, 1, -1, 5000, 'test', [], [1]] 50 51################################################################ 52 53class NumberTestCase(unittest.TestCase): 54 55 def test_default_init(self): 56 # default values are set to zero 57 for t in signed_types + unsigned_types + float_types: 58 self.assertEqual(t().value, 0) 59 60 def test_unsigned_values(self): 61 # the value given to the constructor is available 62 # as the 'value' attribute 63 for t, (l, h) in zip(unsigned_types, unsigned_ranges): 64 self.assertEqual(t(l).value, l) 65 self.assertEqual(t(h).value, h) 66 67 def test_signed_values(self): 68 # see above 69 for t, (l, h) in zip(signed_types, signed_ranges): 70 self.assertEqual(t(l).value, l) 71 self.assertEqual(t(h).value, h) 72 73 def test_bool_values(self): 74 from operator import truth 75 for t, v in zip(bool_types, bool_values): 76 self.assertEqual(t(v).value, truth(v)) 77 78 def test_typeerror(self): 79 # Only numbers are allowed in the constructor, 80 # otherwise TypeError is raised 81 for t in signed_types + unsigned_types + float_types: 82 self.assertRaises(TypeError, t, "") 83 self.assertRaises(TypeError, t, None) 84 85 @unittest.skip('test disabled') 86 def test_valid_ranges(self): 87 # invalid values of the correct type 88 # raise ValueError (not OverflowError) 89 for t, (l, h) in zip(unsigned_types, unsigned_ranges): 90 self.assertRaises(ValueError, t, l-1) 91 self.assertRaises(ValueError, t, h+1) 92 93 def test_from_param(self): 94 # the from_param class method attribute always 95 # returns PyCArgObject instances 96 for t in signed_types + unsigned_types + float_types: 97 self.assertEqual(ArgType, type(t.from_param(0))) 98 99 def test_byref(self): 100 # calling byref returns also a PyCArgObject instance 101 for t in signed_types + unsigned_types + float_types + bool_types: 102 parm = byref(t()) 103 self.assertEqual(ArgType, type(parm)) 104 105 106 def test_floats(self): 107 # c_float and c_double can be created from 108 # Python int and float 109 class FloatLike(object): 110 def __float__(self): 111 return 2.0 112 f = FloatLike() 113 for t in float_types: 114 self.assertEqual(t(2.0).value, 2.0) 115 self.assertEqual(t(2).value, 2.0) 116 self.assertEqual(t(2).value, 2.0) 117 self.assertEqual(t(f).value, 2.0) 118 119 def test_integers(self): 120 class FloatLike(object): 121 def __float__(self): 122 return 2.0 123 f = FloatLike() 124 class IntLike(object): 125 def __int__(self): 126 return 2 127 d = IntLike() 128 class IndexLike(object): 129 def __index__(self): 130 return 2 131 i = IndexLike() 132 # integers cannot be constructed from floats, 133 # but from integer-like objects 134 for t in signed_types + unsigned_types: 135 self.assertRaises(TypeError, t, 3.14) 136 self.assertRaises(TypeError, t, f) 137 self.assertRaises(TypeError, t, d) 138 self.assertEqual(t(i).value, 2) 139 140 def test_sizes(self): 141 for t in signed_types + unsigned_types + float_types + bool_types: 142 try: 143 size = struct.calcsize(t._type_) 144 except struct.error: 145 continue 146 # sizeof of the type... 147 self.assertEqual(sizeof(t), size) 148 # and sizeof of an instance 149 self.assertEqual(sizeof(t()), size) 150 151 def test_alignments(self): 152 for t in signed_types + unsigned_types + float_types: 153 code = t._type_ # the typecode 154 align = struct.calcsize("c%c" % code) - struct.calcsize(code) 155 156 # alignment of the type... 157 self.assertEqual((code, alignment(t)), 158 (code, align)) 159 # and alignment of an instance 160 self.assertEqual((code, alignment(t())), 161 (code, align)) 162 163 def test_int_from_address(self): 164 from array import array 165 for t in signed_types + unsigned_types: 166 # the array module doesn't support all format codes 167 # (no 'q' or 'Q') 168 try: 169 array(t._type_) 170 except ValueError: 171 continue 172 a = array(t._type_, [100]) 173 174 # v now is an integer at an 'external' memory location 175 v = t.from_address(a.buffer_info()[0]) 176 self.assertEqual(v.value, a[0]) 177 self.assertEqual(type(v), t) 178 179 # changing the value at the memory location changes v's value also 180 a[0] = 42 181 self.assertEqual(v.value, a[0]) 182 183 184 def test_float_from_address(self): 185 from array import array 186 for t in float_types: 187 a = array(t._type_, [3.14]) 188 v = t.from_address(a.buffer_info()[0]) 189 self.assertEqual(v.value, a[0]) 190 self.assertIs(type(v), t) 191 a[0] = 2.3456e17 192 self.assertEqual(v.value, a[0]) 193 self.assertIs(type(v), t) 194 195 def test_char_from_address(self): 196 from ctypes import c_char 197 from array import array 198 199 a = array('b', [0]) 200 a[0] = ord('x') 201 v = c_char.from_address(a.buffer_info()[0]) 202 self.assertEqual(v.value, b'x') 203 self.assertIs(type(v), c_char) 204 205 a[0] = ord('?') 206 self.assertEqual(v.value, b'?') 207 208 # array does not support c_bool / 't' 209 @unittest.skip('test disabled') 210 def test_bool_from_address(self): 211 from ctypes import c_bool 212 from array import array 213 a = array(c_bool._type_, [True]) 214 v = t.from_address(a.buffer_info()[0]) 215 self.assertEqual(v.value, a[0]) 216 self.assertEqual(type(v) is t) 217 a[0] = False 218 self.assertEqual(v.value, a[0]) 219 self.assertEqual(type(v) is t) 220 221 def test_init(self): 222 # c_int() can be initialized from Python's int, and c_int. 223 # Not from c_long or so, which seems strange, abc should 224 # probably be changed: 225 self.assertRaises(TypeError, c_int, c_long(42)) 226 227 def test_float_overflow(self): 228 import sys 229 big_int = int(sys.float_info.max) * 2 230 for t in float_types + [c_longdouble]: 231 self.assertRaises(OverflowError, t, big_int) 232 if (hasattr(t, "__ctype_be__")): 233 self.assertRaises(OverflowError, t.__ctype_be__, big_int) 234 if (hasattr(t, "__ctype_le__")): 235 self.assertRaises(OverflowError, t.__ctype_le__, big_int) 236 237 @unittest.skip('test disabled') 238 def test_perf(self): 239 check_perf() 240 241from ctypes import _SimpleCData 242class c_int_S(_SimpleCData): 243 _type_ = "i" 244 __slots__ = [] 245 246def run_test(rep, msg, func, arg=None): 247## items = [None] * rep 248 items = range(rep) 249 from time import perf_counter as clock 250 if arg is not None: 251 start = clock() 252 for i in items: 253 func(arg); func(arg); func(arg); func(arg); func(arg) 254 stop = clock() 255 else: 256 start = clock() 257 for i in items: 258 func(); func(); func(); func(); func() 259 stop = clock() 260 print("%15s: %.2f us" % (msg, ((stop-start)*1e6/5/rep))) 261 262def check_perf(): 263 # Construct 5 objects 264 from ctypes import c_int 265 266 REP = 200000 267 268 run_test(REP, "int()", int) 269 run_test(REP, "int(999)", int) 270 run_test(REP, "c_int()", c_int) 271 run_test(REP, "c_int(999)", c_int) 272 run_test(REP, "c_int_S()", c_int_S) 273 run_test(REP, "c_int_S(999)", c_int_S) 274 275# Python 2.3 -OO, win2k, P4 700 MHz: 276# 277# int(): 0.87 us 278# int(999): 0.87 us 279# c_int(): 3.35 us 280# c_int(999): 3.34 us 281# c_int_S(): 3.23 us 282# c_int_S(999): 3.24 us 283 284# Python 2.2 -OO, win2k, P4 700 MHz: 285# 286# int(): 0.89 us 287# int(999): 0.89 us 288# c_int(): 9.99 us 289# c_int(999): 10.02 us 290# c_int_S(): 9.87 us 291# c_int_S(999): 9.85 us 292 293if __name__ == '__main__': 294## check_perf() 295 unittest.main() 296