views:

22

answers:

1

Is it possible to modify the fields definition of the ctypes.Structure after it's been imported?

Something like:

from ctypes import *

class A_STRUCT(Structure):
     _fields_ = [("one",c_int)]

A_STRUCT._fields_.append(("two",c_int))

x = A_STRUCT()
print x.one
print x.two

Not surprisingly this fails with:

0
Traceback (most recent call last):
  File "structEnumTest.py", line 10, in <module>
    print x.two
AttributeError: 'A_STRUCT' object has no attribute 'two'

EDITS

My use case is that I have two version of A_STRUCT. Version 2 is the same with additional fields appended to the end of version one. I was hoping to avoid having something like this. I do not know which version of the struct is needed until run-time.

class A_STRUCT_V1(Structure):
     _fields_ = [("one",c_int)]

class A_STRUCT_V2(Structure):
     _fields_ = [("one",c_int),("two",c_int)]
+2  A: 

No, as you can see in the sources, PyCStructType_Type is a custom metaclass (see lines 327ff in the C code I just pointed to) and Structure (lines 4136ff) uses it (as exposed in 5532ff). The class statement (specifically when the __new__ from the custom metaclass gets called to make the new class which inherits from Structure) is when all C-accessible fields are actually defined (and it's nice of ctypes to make other "smuggled in" fields inaccessible from Python as well to avoid accidents;-).

What problem, exactly, are you trying to solve, that you couldn't solve by rebuilding A_STRUCT from scratch at the time you learn about the extra fields? For example, if your issue is that there are instances of the "old" A_STRUCT already around, well, obviously, those instances don't have the new fields you've just learned about, so modifying the class, even if through some incredible contortion it was feasible, wouldn't be all that useful;-).

Alex Martelli
@Alex, thanks for the response. See edits above for my use case. Could you elaborate on what you mean by "rebuilding A_STRUCT from scratch"?
Mark
@Mark, by "rebuilding A_STRUCT from scratch" I mean exactly that -- with either a `class` statement or a direct call to Structure's metaclass, build a "new and improved" version on the fly (and bind it to the same name A_STRUCT if you wish). If you don't know the fields "until runtime", say the list of fields comes from a function `def foo():...`, do `class A_STRUCT(Structure): __fields__ = foo()` -- no need to build A_STRUCT first and then rebuild it later, just build it (execute its `class` statement) at the time you **know** how it needs to be.
Alex Martelli
@Alex, thanks for all your help. One more question, if I may. When you say "execute its class statement", this happens on import correct? Would I need to resort to something like this is pass the foo: foo = lambda: [("one",ctypes.c_int)]; execfile("structTest.py",globals(),locals())?
Mark
@Mark, the class statement, like every other, is executed when control reaches it -- that's "on import" only if you put it as one of a module's top-level statements, which is usually done if there are no special reason to do otherwise. Just put it in a function, called when you finally have the info: that function need only contain the `class` statement and then a `return A_STRUCT`, and you can call it as `A_STRUCT = thefunction()` (or with arguments of course, or add a `global A_STRUCT` in the function or in its caller if you need to, and so forth).
Alex Martelli
@Alex, thanks again. Looks like I was trying to make it overly complicated. Rolling it into a function is perfect.
Mark
@Mark, you're welcome!
Alex Martelli