sawyl: (Default)
[personal profile] sawyl
Here's an interesting question: how can a python object be connected to an API library such that contents of the object correctly reflect the status of the underlying object in the API? Here are some details of the way I chose to do it.

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)
From: (Anonymous)
Thanks a lot for this entry - exactly what I was looking after!

Kari

Profile

sawyl: (Default)
sawyl

August 2018

S M T W T F S
   123 4
5 6 7 8910 11
12131415161718
192021222324 25
262728293031 

Most Popular Tags

Page Summary

Style Credit

Expand Cut Tags

No cut tags
Page generated Feb. 5th, 2026 10:27 am
Powered by Dreamwidth Studios