Simple REST-ful (-ish) exposure of Python APIs
After having written code to expose APIs through RESTful web services a couple of times, I’ve decided to do it once more, only this time I won’t get paid, I won’t have deadlines, I’ll write it so I’ll never have to write it again, and I’ll make it available as open source.
Problem is, I’m a lazy, lazy person, and have not been able to muster the energy to actually get writing, which leads me to this blog post – since I’ve not been updating the blog as I should either, I’ll kill two projects with one meeting and make the actual development process open as well, as a series of blog posts and a repository at BitBucket.
For someone else to be able to follow the work, I obviously have to nail down what the goal of this exercise is:
Listing 1.
- Create a tool that can expose a Python API in a RESTish fashion
- The API itself must not have to know about the tool
- It must run on at least CherryPy and two other webapp frameworks TBD (no, not Django)
- It must handle HTTP errors
- It must be able to encode data into JSON before returning it
- It must run on Python 3.2+
- It must not care what the proper definition of RESTful is
In addition, some good-to-haves:
Listing 1.
- It may make linking between resources easier (if feasible)
- It may be able to use other data formats than JSON
- It may run on Python 2.7
Because I enjoy working with CherryPy since it’s very good at staying out of my way, I’ll start out writing for CherryPy and then generalize from there. Just to get started, I have created a minimal CherryPy app to work from, even though I’ll split the tool from the framwork (or the REST framework from the web framework?) later. The entire code looks like this:
import cherrypy
def requesthandler(*pathargs, **kwargs):
cherrypy.response.status = "500 Server Error"
return "Not implemented"
class PyRest(object):
def index(self, *args, **kwargs):
return requesthandler(*args, **kwargs)
index.exposed = True
CONF = {
'global': {
'server.`socket`_host': '0.0.0.0',
'server.`socket`_port': 8888,
}
}
if `__name_`_ == '`__main_`_':
ROOT = PyRest()
cherrypy.quickstart(ROOT, '/', CONF)
def application(environ, `start`_response):
cherrypy.tree.mount(PyRest(), '/', None)
return cherrypy.tree(environ, `start`_response)