I’ve been searching around the web with no luck. I have the following Python code:
class LED(Structure):
_fields_ = [
('color', c_char_p),
('id', c_uint32)
]
class LEDConfiguration(Structure):
_fields_ = [
('daemon_user', c_char_p),
('leds', POINTER(LED)),
('num_leds', c_uint32)
]
Here is a simplified example function that uses these structures and returns an LEDConfiguration.
def parseLedConfiguration(path, board):
lc = LEDConfiguration()
for config in configs:
if( config.attributes['ID'].value.lstrip().rstrip() == board ):
lc.daemon_user = c_char_p('some_name')
leds = []
#Imagine this in a loop
ld = LED()
ld.color = c_char_p('red')
ld.id = int(0)
leds.append(ld)
#end imagined loop
lc.num_leds = len(leds)
lc.leds = (LED * len(leds))(*leds)
return lc
Now this the C code I am using (I’ve stripped out everything involved with setting up python/calling the “parseLedConfiguration” function/etc but I can add it in if it is helpful).
/*Calling the python function "parseLedConfiguration"
pValue is the returned "LEDConfiguration" python Structure*/
pValue = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
if (pValue != NULL)
{
int i, num_leds;
PyObject *obj = PyObject_GetAttr(pValue, PyString_FromString("daemon_user"));
daemon_user = PyString_AsString(obj);
Py_DECREF(obj);
obj = PyObject_GetAttr(pValue, PyString_FromString("num_leds"));
num_leds = PyInt_AsLong(obj);
Py_DECREF(obj);
obj = PyObject_GetAttr(pValue, PyString_FromString("leds"));
PyObject_Print(obj, stdout, 0);
My problem is figuring out how to access what is returned to the final “obj”. The “PyObject_Print” on the “obj” shows this output:
<ConfigurationParser.LP_LED object at 0x7f678a06fcb0>
I want to get into a state where I can access that LP_LED object in the same way I’m accessing the above “LEDConfiguration” object.
EDIT 1
I guess another maybe more important question, is my python code correct? Is that how I should be storing a list or array of “Structure” inside another “Structure” so it can be accessed from the Python C API?
Thanks!
Since your EDIT 1 clarifies the underlying question, let me put that at top:
No, that’s how you should be storing an array of
Structureinside anotherStructureso it can be accessed fromnon-Python-C-APIC code. If you want it to be accessed from the Python C API, just use a Pythonlist.In general, if you’re writing code in both Python and C, only one side has to bend over backward to work with the other one. The point of using
ctypesStructures andPOINTERs and the like in Python is to allow them to work directly in C, without having to go through the C API. Conversely, the point of using functions likePyList_GetItemis to allow you to use normal Python code, notctypesPython code.So, if you want to store a
listinside aStructureto be accessed via the Python C API, just store a Pythonlist—and you really don’t need theStructurein the first place; use a normal Python class (possibly with__slots__). You can write this code without importingctypesat all.Conversely, if you want to store structures that can be used directly in C, you can do that with
ctypes; then, in the C code, once you’ve gotten intoStructureguts of thePyObject *you don’t need the Python API anymore, because the structure is all C. This is usually the way you go when you have existing C code, and want to interface with it from Python, rather than when you’re designing the C code from scratch, but there’s no rule that says you can’t use it the other way.Meanwhile, if this is your first attempt at writing C and Python code that talk to each other, I’d suggest you use
Cython. Then, once you’re comfortable with that, if you want to learnctypes, do a different project that uses Python withctypesto talk to C code that knows nothing at all about Python. And then, a third project that uses the C API to talk to Python code that knows nothing aboutctypes(like most C extension modules). Once you’re familiar with all three, you’ll be able to pick the right one for most projects in the future.Now, to answer the specific problem:
First, when
PyList_GetItem(or most other functions in the C API) returns NULL, this means there’s an exception, so you should check the exception and log it. Trying to debug NULL return values in the C API without looking at the set exception is like trying to debug Python code without looking at the tracebacks.Anyway, there are a few obvious reasons this function could fail: Maybe you’re calling it with an out-of-bounds index, or maybe you’re calling it on something that isn’t a
listat all.In fact, the second one seems pretty obvious here. If printing out
objgives you this:<ConfigurationParser.LP_LED object at 0x7f678a06fcb0>Then you’ve got a (pointer to an)
LEDobject, andLEDobjects aren’tlists.And if you look at your code, you don’t seem to have a
listofLEDobjects anywhere, at least not in the code you show us. You do have aPOINTER(LED), which could hold a C-array-decayed C array ofLEDs, but that’s not the same thing as a Pythonlistof them. It’s just a C array, which you use C array syntax to dereference: