Dynamic properties with SWIG
Sep. 14th, 2007 05:34 pm
I've got an object called a Request. This represents a single instance of job in the batch system. Each Request has properties such as queue and state which change depending on what the batch system is doing and which need to be kept up to date. Rather than simply caching the static data in a struct I've chosen to solve the problem by creating a routine that queries the batch server every time a __getattr__ is called on the object.
Essentially, I set up a very simple structure with a pair of buffers:
typedef struct {
int int_buffer;
char *char_buffer;
} Request;
I then wrote a simple routine to query the state of a particular property via the API and write the results to either int_buffer or char_buffer in the structure:
void Request_query(Request *r, int property);I integrated this into python using the following bit of SWIG:
%extend Request {
void query(int property);
}
So far, so simple. Now I needed to construct some code to intercept the __getattr__ calls so that they call query(). This can be done with a %pythoncode section in the SWIG interface file as follows:
%pythoncode {
get_attrs = { "queue": ( QUEUE, "char_buffer"),
"state": ( STATE, "int_buffer") }
def _Request_getattr(self, class_type, name):
try:
self.query(get_attrs[name][0])
name = get_attrs[name][1]
except KeyError:
pass
return _swig_getattr(self, class_type, name)
Request.__getattr__ = lambda self, name: \
_Request_getattr(self, Request, name)
}
The upshot of this is that any attempt to get a property will result in the dictionary get_attrs being checked for an entry. If an entry is found, the appropriate buffers are updated using query() and the value of name is changed to point to the buffer. Once this work has been done, the builtin _swig_getattr() routine is called as normal to extract a value from the object — either the buffer set by query() or, in the case of a simple static value which lacks a dictionary entry, the contents of the variable in the struct.
A similar method can be used to reverse the process, allowing changes to properties to be passed back to the API by replacing __setattr__. For example:
%pythoncode {
def _Request_setattr(self, class_type, name, value):
if name == "queue":
self.setQueue(value)
else:
_swig_setattr(self, class_type, name, value)
Request.__setattr__ = lambda self, name, value: \
_Request_getattr(self, Request, name, value)
}
Whilst none of this seems to be terribly well documented, it's possible to grok how it's supposed to work by skimming through the SWIG generated module file.
Dynamic SWIG __getattr__
Date: 2010-04-24 08:23 pm (UTC)Kari