1import unittest
2from ctypes.test import need_symbol
3import test.support
4
5class SimpleTypesTestCase(unittest.TestCase):
6
7    def setUp(self):
8        import ctypes
9        try:
10            from _ctypes import set_conversion_mode
11        except ImportError:
12            pass
13        else:
14            self.prev_conv_mode = set_conversion_mode("ascii", "strict")
15
16    def tearDown(self):
17        try:
18            from _ctypes import set_conversion_mode
19        except ImportError:
20            pass
21        else:
22            set_conversion_mode(*self.prev_conv_mode)
23
24    def test_subclasses(self):
25        from ctypes import c_void_p, c_char_p
26        # ctypes 0.9.5 and before did overwrite from_param in SimpleType_new
27        class CVOIDP(c_void_p):
28            def from_param(cls, value):
29                return value * 2
30            from_param = classmethod(from_param)
31
32        class CCHARP(c_char_p):
33            def from_param(cls, value):
34                return value * 4
35            from_param = classmethod(from_param)
36
37        self.assertEqual(CVOIDP.from_param("abc"), "abcabc")
38        self.assertEqual(CCHARP.from_param("abc"), "abcabcabcabc")
39
40    @need_symbol('c_wchar_p')
41    def test_subclasses_c_wchar_p(self):
42        from ctypes import c_wchar_p
43
44        class CWCHARP(c_wchar_p):
45            def from_param(cls, value):
46                return value * 3
47            from_param = classmethod(from_param)
48
49        self.assertEqual(CWCHARP.from_param("abc"), "abcabcabc")
50
51    # XXX Replace by c_char_p tests
52    def test_cstrings(self):
53        from ctypes import c_char_p
54
55        # c_char_p.from_param on a Python String packs the string
56        # into a cparam object
57        s = b"123"
58        self.assertIs(c_char_p.from_param(s)._obj, s)
59
60        # new in 0.9.1: convert (encode) unicode to ascii
61        self.assertEqual(c_char_p.from_param(b"123")._obj, b"123")
62        self.assertRaises(TypeError, c_char_p.from_param, "123\377")
63        self.assertRaises(TypeError, c_char_p.from_param, 42)
64
65        # calling c_char_p.from_param with a c_char_p instance
66        # returns the argument itself:
67        a = c_char_p(b"123")
68        self.assertIs(c_char_p.from_param(a), a)
69
70    @need_symbol('c_wchar_p')
71    def test_cw_strings(self):
72        from ctypes import c_wchar_p
73
74        c_wchar_p.from_param("123")
75
76        self.assertRaises(TypeError, c_wchar_p.from_param, 42)
77        self.assertRaises(TypeError, c_wchar_p.from_param, b"123\377")
78
79        pa = c_wchar_p.from_param(c_wchar_p("123"))
80        self.assertEqual(type(pa), c_wchar_p)
81
82    def test_int_pointers(self):
83        from ctypes import c_short, c_uint, c_int, c_long, POINTER, pointer
84        LPINT = POINTER(c_int)
85
86##        p = pointer(c_int(42))
87##        x = LPINT.from_param(p)
88        x = LPINT.from_param(pointer(c_int(42)))
89        self.assertEqual(x.contents.value, 42)
90        self.assertEqual(LPINT(c_int(42)).contents.value, 42)
91
92        self.assertEqual(LPINT.from_param(None), None)
93
94        if c_int != c_long:
95            self.assertRaises(TypeError, LPINT.from_param, pointer(c_long(42)))
96        self.assertRaises(TypeError, LPINT.from_param, pointer(c_uint(42)))
97        self.assertRaises(TypeError, LPINT.from_param, pointer(c_short(42)))
98
99    def test_byref_pointer(self):
100        # The from_param class method of POINTER(typ) classes accepts what is
101        # returned by byref(obj), it type(obj) == typ
102        from ctypes import c_short, c_uint, c_int, c_long, POINTER, byref
103        LPINT = POINTER(c_int)
104
105        LPINT.from_param(byref(c_int(42)))
106
107        self.assertRaises(TypeError, LPINT.from_param, byref(c_short(22)))
108        if c_int != c_long:
109            self.assertRaises(TypeError, LPINT.from_param, byref(c_long(22)))
110        self.assertRaises(TypeError, LPINT.from_param, byref(c_uint(22)))
111
112    def test_byref_pointerpointer(self):
113        # See above
114        from ctypes import c_short, c_uint, c_int, c_long, pointer, POINTER, byref
115
116        LPLPINT = POINTER(POINTER(c_int))
117        LPLPINT.from_param(byref(pointer(c_int(42))))
118
119        self.assertRaises(TypeError, LPLPINT.from_param, byref(pointer(c_short(22))))
120        if c_int != c_long:
121            self.assertRaises(TypeError, LPLPINT.from_param, byref(pointer(c_long(22))))
122        self.assertRaises(TypeError, LPLPINT.from_param, byref(pointer(c_uint(22))))
123
124    def test_array_pointers(self):
125        from ctypes import c_short, c_uint, c_int, c_long, POINTER
126        INTARRAY = c_int * 3
127        ia = INTARRAY()
128        self.assertEqual(len(ia), 3)
129        self.assertEqual([ia[i] for i in range(3)], [0, 0, 0])
130
131        # Pointers are only compatible with arrays containing items of
132        # the same type!
133        LPINT = POINTER(c_int)
134        LPINT.from_param((c_int*3)())
135        self.assertRaises(TypeError, LPINT.from_param, c_short*3)
136        self.assertRaises(TypeError, LPINT.from_param, c_long*3)
137        self.assertRaises(TypeError, LPINT.from_param, c_uint*3)
138
139    def test_noctypes_argtype(self):
140        import _ctypes_test
141        from ctypes import CDLL, c_void_p, ArgumentError
142
143        func = CDLL(_ctypes_test.__file__)._testfunc_p_p
144        func.restype = c_void_p
145        # TypeError: has no from_param method
146        self.assertRaises(TypeError, setattr, func, "argtypes", (object,))
147
148        class Adapter(object):
149            def from_param(cls, obj):
150                return None
151
152        func.argtypes = (Adapter(),)
153        self.assertEqual(func(None), None)
154        self.assertEqual(func(object()), None)
155
156        class Adapter(object):
157            def from_param(cls, obj):
158                return obj
159
160        func.argtypes = (Adapter(),)
161        # don't know how to convert parameter 1
162        self.assertRaises(ArgumentError, func, object())
163        self.assertEqual(func(c_void_p(42)), 42)
164
165        class Adapter(object):
166            def from_param(cls, obj):
167                raise ValueError(obj)
168
169        func.argtypes = (Adapter(),)
170        # ArgumentError: argument 1: ValueError: 99
171        self.assertRaises(ArgumentError, func, 99)
172
173    def test_abstract(self):
174        from ctypes import (Array, Structure, Union, _Pointer,
175                            _SimpleCData, _CFuncPtr)
176
177        self.assertRaises(TypeError, Array.from_param, 42)
178        self.assertRaises(TypeError, Structure.from_param, 42)
179        self.assertRaises(TypeError, Union.from_param, 42)
180        self.assertRaises(TypeError, _CFuncPtr.from_param, 42)
181        self.assertRaises(TypeError, _Pointer.from_param, 42)
182        self.assertRaises(TypeError, _SimpleCData.from_param, 42)
183
184    @test.support.cpython_only
185    def test_issue31311(self):
186        # __setstate__ should neither raise a SystemError nor crash in case
187        # of a bad __dict__.
188        from ctypes import Structure
189
190        class BadStruct(Structure):
191            @property
192            def __dict__(self):
193                pass
194        with self.assertRaises(TypeError):
195            BadStruct().__setstate__({}, b'foo')
196
197        class WorseStruct(Structure):
198            @property
199            def __dict__(self):
200                1/0
201        with self.assertRaises(ZeroDivisionError):
202            WorseStruct().__setstate__({}, b'foo')
203
204    def test_parameter_repr(self):
205        from ctypes import (
206            c_bool,
207            c_char,
208            c_wchar,
209            c_byte,
210            c_ubyte,
211            c_short,
212            c_ushort,
213            c_int,
214            c_uint,
215            c_long,
216            c_ulong,
217            c_longlong,
218            c_ulonglong,
219            c_float,
220            c_double,
221            c_longdouble,
222            c_char_p,
223            c_wchar_p,
224            c_void_p,
225        )
226        self.assertRegex(repr(c_bool.from_param(True)), r"^<cparam '\?' at 0x[A-Fa-f0-9]+>$")
227        self.assertEqual(repr(c_char.from_param(97)), "<cparam 'c' ('a')>")
228        self.assertRegex(repr(c_wchar.from_param('a')), r"^<cparam 'u' at 0x[A-Fa-f0-9]+>$")
229        self.assertEqual(repr(c_byte.from_param(98)), "<cparam 'b' (98)>")
230        self.assertEqual(repr(c_ubyte.from_param(98)), "<cparam 'B' (98)>")
231        self.assertEqual(repr(c_short.from_param(511)), "<cparam 'h' (511)>")
232        self.assertEqual(repr(c_ushort.from_param(511)), "<cparam 'H' (511)>")
233        self.assertRegex(repr(c_int.from_param(20000)), r"^<cparam '[li]' \(20000\)>$")
234        self.assertRegex(repr(c_uint.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$")
235        self.assertRegex(repr(c_long.from_param(20000)), r"^<cparam '[li]' \(20000\)>$")
236        self.assertRegex(repr(c_ulong.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$")
237        self.assertRegex(repr(c_longlong.from_param(20000)), r"^<cparam '[liq]' \(20000\)>$")
238        self.assertRegex(repr(c_ulonglong.from_param(20000)), r"^<cparam '[LIQ]' \(20000\)>$")
239        self.assertEqual(repr(c_float.from_param(1.5)), "<cparam 'f' (1.5)>")
240        self.assertEqual(repr(c_double.from_param(1.5)), "<cparam 'd' (1.5)>")
241        self.assertEqual(repr(c_double.from_param(1e300)), "<cparam 'd' (1e+300)>")
242        self.assertRegex(repr(c_longdouble.from_param(1.5)), r"^<cparam ('d' \(1.5\)|'g' at 0x[A-Fa-f0-9]+)>$")
243        self.assertRegex(repr(c_char_p.from_param(b'hihi')), r"^<cparam 'z' \(0x[A-Fa-f0-9]+\)>$")
244        self.assertRegex(repr(c_wchar_p.from_param('hihi')), r"^<cparam 'Z' \(0x[A-Fa-f0-9]+\)>$")
245        self.assertRegex(repr(c_void_p.from_param(0x12)), r"^<cparam 'P' \(0x0*12\)>$")
246
247################################################################
248
249if __name__ == '__main__':
250    unittest.main()
251