Aug 5, 2009
Using Python’s ctypes to Call Into C Libraries
The ctypes module makes loading and calling into a dynamic library incredibly easy:
>>> from ctypes import CDLL
>>> libc = CDLL('libc.so.6')
>>> print libc.strlen('abcde')
5
As with everything else in python, it gets even better when you scratch the surface. In the example above, CDLL returns an object that represents the dynamic library. You can access the functions in that library by attribute access (”libc.strlen”) or item access (”libc['strlen']“). Both access mechanisms return a callable object.
This callable object has an “errcheck” attribute that can be assigned a callable. We can use this for error-checking our calls into the library. Let’s write a simple version of the “kill” command that uses the kill(2) system call.
import sys
from ctypes import *
# Load the library.
libc = CDLL('libc.so.6')
# Our error checking function. This will receive the
# return value of the library function, the function that
# was called, and the arguments passed to the function as a
# tuple.
def kill_errcheck(retval, func, funcargs):
'''Check for error -- retval == -1.'''
if retval < 0:
raise Exception('kill%s failed' % (funcargs, ))
return True
# Get the kill function from the standard library.
kill = libc.kill
# Set the error checker for kill().
kill.errcheck = kill_errcheck
# Pass the command line argument as a pid to kill, with
# SIGSEGV (11).
pid = int(sys.argv[1])
kill(pid, 11)
Save this as kill.py. Then, in your shell, try something like this:
# Notice that the 3401 is the pid of the process
# we're putting into the background. Yours will
# be different.
bash$ sleep 120&
[1] 3401
bash$ python kill.py 3401
[1]+ Segmentation Fault sleep 120
bash$ python kill.py 3401
Traceback (most recent call last):
File "kill.py", line 17, in
kill(pid, 11)
File "kill.py", line 10, in kill_errcheck
raise Exception('kill%s failed' % (funcargs, ))
Exception: kill(3401, 11) failed
At line 4 of the output we run sleep in the background. At line 5 we learn the pid of this process. At line 6 we run our kill program, giving it the pid we just spawned, and we see the notification from bash that the process was killed (with signal 11, segmentation fault). At line 8 we run our kill program again on pid 3401, but it doesn’t exist, the kill system call returns -1, and our error checker raises an exception when it detects the system call failure.
But wait, there’s more… I’m working on a follow up post that combines ctypes.Structure with calls into a linux system call.
Related posts:
- Python Exception Handling: Cleanup and Reraise I’ve had this code around for a while and...
- Five Days to a Django Web App: Day Three, Coding Thanks for coming back for Day Three! [Note: Sorry...
- Jesse Noller on Python Jesse Noller has been republishing articles he wrote for...
- Yet Another Python Enum Module I didn’t like the existing enum recipes, so I...
- Five Days to a Django Web App: Day Four, Deployment Thanks for your patience, and for coming back for...