I don’t claim to be a particularly good Python programmer at all. I’m probably one of the… less useful people to program Python - I much prefer Go (and to be rather honest, C).
But I think I’ve encountered the nastiest piece of library I’ve used with
It’s kind of like the psuedo
C package for Go - it allows direct access to
C functions and structures and all those nice things, and lets you use C
libraries in your Python program.
I thought, “Well, this sounds nice! Let’s write Python bindings for
can’t be too hard!” I was wrong.
I’ve learned two major annoyances:
Calling a function is hard.
The documentation claims that that this is possible:
from ctypes import * import atexit dpx = CDLL('libdpx.so') def cleanup(): dpx.dpx_cleanup() dpx.dpx_init() atexit.Register(cleanup) peer = dpx.dpx_peer_new() # do stuff with peer here
And it’d be all nice and dandy, right? NOPE. Nowhere in the documentation does ctypes seem to know what to do when you get a pointer back unless you explicitly say it. It does say that return values would “assume to be int”. So technically doing the following:
peer = c_void_p(dpx.dpx_peer_new())
should work, right? Because c_void_p takes an integer argument, which comes out of the function thanks to ctypes. And that integer argument is probably a pointer, given the C return value.
But nooo. segfaults everywhere. I had to do this:
dpn = dpx.dpx_peer_new dpn.argtypes =  dpn.restype = c_void_p # now call it peer = dpn()
For every single function.
I apparently cannot work with pointers nicely with ctypes.
Because, well.. Python seems to suck at pointer support. It’s understandable, so I’m not sure what I was expecting, but it’s still annoying when I do the following:
dfn = dpx.dpx_frame_new dfn.argtypes = [c_void_p] dfn.restype = POINTER(CFRAME) # POINTER(CFRAME) means the type is a pointer to a CFRAME # CFRAME was a class derived from "Structure" that mimics dpx_frame in dpx.h new_frame = dfn(None) new_frame.payloadSize = 10 new_frame.payload = payload channel.send(new_frame) # Sending frame: 0 bytes...
Only to realise that payloadSize that channel gets is size 0. Why?
It’s because the object
new_frame is a pointer object. It’s like any other
Python object, so I was actually setting the attributes ‘payloadSize’ and
‘payload’ on the object itself.
So then, how to get to the actual object?
You can’t do
new_frame.contents like the docs say, because the docs also say
that Python will make a copy of the object. So that’s useless.
Insted, I have to treat it like an array:
actual_new_frame = new_frame actual_new_frame.payloadSize = 10 actual_new_frame.payload = payload channel.send(new_frame) # Sending frame: 10 bytes...
So those problems are solved, at least.
So you’re happy now?
Nope. See that cover image? Crashes everywhere.
The same code that worked for the most core test case is now failing for the
ctypes is passing invalid pointers (or I am).
I think I’m going to try my hand at making a Python extension instead. At least I can code that in C…
EDIT: I’ve legitimately failed. It’s segfaulting because I’m cleaning up structures prematurely. So therefore pointers that used to point to valid stuff no longer do.
I think I need to take shots.
Where is your code so I can laugh at it
By all means, please do! #ctypestruggles
It’s living in a branch of duplex right now. Feel free to check it out.