ctypes is an advanced ffi (Foreign Function Interface) package for Python 2.3 and higher. In Python 2.5 it is already included.
ctypes allows to call functions in dlls/shared libraries and has extensive facilities to create, access and manipulate simple and complicated C data types in Python - in other words: wrap
ctypes works on Windows, Windows CE, Mac OS X, Linux, Solaris, FreeBSD, OpenBSD. It may also run on other systems, provided that libffi supports this platform.
ctypes is licensed under the MIT License.
>>> from ctypes import *
>>> libc = cdll.msvcrt
Just access libraries as attributes of cdll
>>> from ctypes import *
>>> libc = CDLL("libc.so.6")
Must specify full filename, so can't use attribute access
>>> libc.printf("Hello, world!\n")
Hello, world!
14
In Linux, the full file name, including extension, must be specified to load a library, so attribute access does not work. An alternative to using the CDLL constructor as described above is to use the LoadLibrary method of the dll loader:
>>> libc = cdll.LoadLibrary("libc.so.6")
cdll loads libraries which export functions using the standard cdecl calling convention. On Windows, there is also windll and oledll, which both call functions using the stdcall calling convention. oledll assumes that functions return a Windows HRESULT error code, which is used to automatically raise WindowsError exceptions when the call fails.
>>> c_int()
c_long(0)
>>> c_int(10)
c_long(10)
>>> c_char()
c_char('\x00')
>>> b = c_ubyte(); b.value
0
>>> b.value -= 1; b
c_ubyte(255)
>>> libc.rand
<_FuncPtr object at 0xb7e02984>
>>> libc.rand()
719885386
>>> libc.abs(-10)
10
>>> libc.strlen('penguins')
8
>>> libm.fabs(c_double(-5.5))
8
Note that you need to load libm in the same way that libc was loaded. On Linux:
>>> libm = CDLL("libm.so.6")
On Windows, the math functions are also in msvcrt, so this will suffice:
>>> libm = libc
You can also access extern variables in libraries by using the in_dll method on a ctypes type. For example:
>>> c_int.in_dll(libc, 'errno')
c_long(0)
>>> libm.fabs.restype = c_double
>>> libm.fabs(c_double(-5.5))
5.5
>>> libm.fabs.argtypes = (c_double,)
>>> libm.fabs(-5.5)
5.5
>>> i = c_int(42); i
c_long(42)
>>> pi = pointer(i); pi
<ctypes.LP_c_long object at 0x009D98A0>
>>> pi.contents
c_long(42)
>>> i = c_int(5); pi.contents = i; pi.contents
c_long(5)
>>> pi[0]
5
>>> pi[0] = 10; pi.contents
c_long(10)
>>> i.value
10
>>> i.value = 15; pi[0]
15
Just as in C, you need to be careful when accessing pointer indexes other than 0, as you can access arbitrary memory locations!
pointer() has to create pointer types first>>> intp = POINTER(c_int); intp
<class 'ctypes.LP_c_long'>
>>> pi = intp(i); pi
<ctypes.LP_c_long object at 0x009D99E0>
>>> libc.sscanf('9', '%d', pi)
1
>>> pi.contents
c_long(9)
byref() if you don't need the pointer object:>>> libc.sscanf('3', '%d', byref(i))
1
>>> i.value
3
>>> int3 = c_int * 3; int3
<class 'ctypes._endian.c_long_Array_3'>
>>> i3 = int3(); [i for i in i3]
[0, 0, 0]
>>> i3 = int3(1, 2, 3); [i for i in i3]
[1, 2, 3]
>>> s = (c_char * 9)()
>>> libc.sprintf(s, '%dmonkeys', 5)
>>> "".join(s)
'5monkeys\x00'
value attribute which yields the string up to NULL and raw attribute to access the raw buffercreate_string_buffer(len)create_string_buffer() can also accept an initial string as the first parameter. If no size is specified, the size of the string is used.
>>> libc.tmpnam = POINTER(c_char)
>>> s = libc.tmpnam(None); s
<ctypes.LP_c_char object at 0xb7d2f8e4>
>>> s[0:5]
'/tmp/'
c_char_p is a much smarter string pointer:>>> libc.tmpnam.restype = c_char_p
>>> libc.tmpnam(None)
'/tmp/filevZ6H67'
Note the use of None here to indicate NULL.
errcheck attribute can be used to do error checking:>>> def errcheck(res, func, args):
... if not res:
... raise IOError('Error opening file')
... return res
...
>>> libc.fopen.errcheck = errcheck
>>> libc.fopen('notexist', 'r')
Traceback (most recent call last):
...
IOError: Error opening file
>>> libc.fopen('/bin/ls', 'r')
135958576
errcheck if no exception is raised is the object returned to the user.errcheck could do a lot more! For example, it could call strerror() to obtain the error message.libc.fopen.restype to c_void_p, as fopen()/code> returns a pointer.Structure and setting the _fields_ attribute:>>> class tm(Structure):
... _fields_ = (('tm_sec', c_int), ('tm_min', c_int), ('tm_hour', c_int),
... ('tm_day', c_int), ('tm_mon', c_int), ('tm_year', c_int),
... ('tm_wday', c_int), ('tm_yday', c_int), ('tm_isdst', c_int))
...
>>> t = c_int(); libc.time(byref(t))
1185921393
>>> libc.localtime.restype = POINTER(tm)
>>> lt = libc.localtime(byref(t)).contents
>>> "%d/%02d/%02d" % (lt.tm_year + 1900, lt.tm_mon, lt.tm_day)
'2007/07/01'
_fields_ after class definition if a struct must refer to itself; e.g. pointer to next item in linked listUnion in a similar wayqsort()qsort() requires a callback function to compare array items>>> cmpfunc = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))>>> @cmpfunc
... def mycmp(a, b):
... return cmp(a[0], b[0])
...
>>> mycmp
<CFunctionType object at 0x7ff7348c>
CFUNCTYPE is a factory function which creates types for C functions. The first argument is the result type and subsequent arguments are the types for the arguments expected by the function.
>>> ia = (c_int * 4)(3, 5, 2, 1)
>>> [i for i in ia]
[3, 5, 2, 1]
>>> libc.qsort.restype = None
>>> libc.qsort(ia, len(ia), sizeof(c_int), mycmp)
>>> [i for i in ia]
[1, 2, 3, 5]
CFUNCTYPE creates types for functions using the cdecl calling convention; WINFUNCTYPE is for the Windows stdcall calling conventionFailing to keep a reference to C function objects may result in them being garbage collected. This will cause a crash when a callback is made.