pyRest part 3: Routing and responsibilities
In part 2, I hooked up the API to CherryPy in a very crude fashion, and this time we’ll look at how we can add handlers for resources in a less clumsy way. I decided to keep handlers on one ‘level’ only – that is, /sketch/parrot and /sketch will both be handled by the /sketch handler. This is because I find that the same sub-resource often is present in several places (what about /props/parrot?) and having handlers like this simplifies stuff and makes the magic more readable.
That magic looks like this – it is passed a package, find all modules that has at least one of get/post/put/delete implemented, and stores them in a name->module dict.
def `get`_handlers(package):
handlers = {}
for `member`_name, `member` in
[module for module in inspect.getmembers(package)
if inspect.ismodule(module[1])]:
if [fn for name, fn in inspect.getmembers(member)
if name in ('get', 'post', 'put', 'delete')]:
print("Adding handler %s" % `member`_name)
handlers[`member`_name] = `member`
return handlers
Later, when we get a request, we interpret the first part of the path as resource name (although I mounted it at /api, so it becomed /api/
def requesthandler(handlers, method, resource, *pathargs, **kwargs):
"""Main dispatch for calls to PyRest; no framework specific
code to be present after this point"""
if not resource in handlers:
return Response('404 Not Found', 'No such resource')
if not hasattr(handlers[resource], method):
return Response('405 Method Not Allowed',
'Unsupported method for resource')
`return`_data = getattr(handlers[resource],
method)(*pathargs, **kwargs)
`return` Response('200 OK', json.dumps(`return`_data))
Right now, there’s nothing exciting going on in the API, so the routing logic just calls hgapi and assumes everything will be in order:
def get(ref=None):
rev = hgapi.Repo('.')[ref]
return {
'node': rev.node,
'desc': rev.desc
}
So, when we GET /api/changeset/1, the requesthandler will be passed this: ({‘changeset’:
As it looks now, the next part should probably be adding some tests, but since I’m not totally decided on how I want to write my tests, I’ll push ahead with separating the code instead – the current PyRest class and everything that has to do with CherryPy should go into a pyrest.cherrypy package or something similar, the requesthandler and get_handler functions should stay as part of pyRest proper, and the backend package should probably end up in an example package.
Code is as always available at Bitbucket.