Tuesday, July 28, 2009

RESTful Python Objects.

Designing RESTful resources that behave like the web in this day and age makes for good API design practice. The resulting API has a very high level of simplicity that is sought after and valued by many developers. However, what about the client end? Can they too benefit from this elegant design? They sure can. Just like anything else in software design, APIs can be abused or they can be used as intended. So, why not make the client work similarly to how the resources themselves behave? Better yet, why not make them identical?

This can be achieved by mapping individual resources to Python instances. This makes for a good abstraction mapping. One resource, one Python instance. But this doesn't really help the Python developer if there are "special" methods they need to invoke on the instance just to interact with the actual resource. This instances acts as a proxy and so both the instance data and the instance behavior should be the same as the resource. This can be done by using the following principles:
  • The constructor could issue a POST HTTP request to create a new resource using the constructor parameters.
  • The attribute retrieval could be overridden to issue a HTTP GET request.
  • The attribute setting could be overridden to issue a HTTP PUT request.
  • The object deletion functionality could be overridden to issue a HTTP DELETE request.
That's it, the instance can then behave like a regular Python instance and be a RESTful resource at the same time. Mind you, these are just principles and not and ideal implementation, obviously. So, what is needed is an HTTP library of some kind to fully implement each of these methods. There will no doubt be variations to these methods as well. For instance, there is often the requirement of retrieving lists of resources as opposed to a single resource.

The following is a simple example illustrating these principles.
#Example; RESTful Python instances.

class RESTful(object):
def __init__(self, uri, **kw):
#Issue a HTTP POST request construct a new resource.
print "POSTING..."

def __getattr__(self, name):
#Issue a HTTP GET, possibly a conditional GET to
#retrieve the resource attribute.
print "GETTING..."

def __setattr__(self, name, value):
#Issue a HTTP PUT request to update the resource
#attribute.
print "PUTTING..."

def __del__(self):
#Issue a HTTP DELETE request to destroy the resource.
print "DELETING..."

class BlogEntry(RESTful):
def __init__(self, uri, **kw):
RESTful.__init__(self, uri, **kw)

if __name__=="__main__":
entry=BlogEntry("/blog", title="Hello", body="World")
entry.body="There"
body=entry.body