Tuesday, October 13, 2009

Ext JS Direct in Python

My very bright, Canadian friend Ian McCracken wrote a Python package that implements the Ext JS Direct protocol. Ian and I work together at Zenoss and in our next release we will revamp the web UI using Ext JS. I took the time to learn a little bit about Ian's package and came up with a minimal example that I want to share. The code is available in the Zenoss public subversion repository.

This example is all Python code contrasted to the typical use of JavaScript as the client. The server uses wsgiref and webob to bring up a web server and pass all requests to an extdirect DirectRouter. Python definitely shines for this kind of use. It is simple, elegant, and readable.

#! /usr/bin/env python

from extdirect.router import DirectRouter
from webob import Request, Response
from wsgiref import simple_server

class TestUtils(DirectRouter):

def upper(self, word):
return word.upper()

def today(self):
return "Today is July 4, 1849."

class DirectApp(object):

def __init__(self):
self._testUtils = TestUtils()

def __call__(self, environ, startResponse):
req = Request(environ)
res = Response(content_type='application/json')
res.body = self._testUtils(req.body)
return res(environ, startResponse)

if __name__ == '__main__':
port = 7999
print "Listening on %s" % port
app = DirectApp()
server = simple_server.make_server('', port, app)

The client uses httplib and json to post a JSON message to the server. It then prints out the result.

#! /usr/bin/env python

HOSTNAME = "localhost"
PORT = 7999
ACTION = "TestUtils"

import sys
import httplib
import json

if len(sys.argv) < 2:
print "Usage: client.py [key=value ...]"
method = sys.argv[1]
data = {}
for key, value in [item.split("=") for item in sys.argv[2:]]:
data[key] = value
conn = httplib.HTTPConnection("%s:%s" % (HOSTNAME, PORT))
body = json.dumps({"action": ACTION,
"method": method,
"data": [data],
"type": "rpc",
"tid": 1})
headers = {"Content-type": "application/x-www-form-urlencoded",
"Accept": "application/json"}
conn.request("POST", "", body, headers)
response = conn.getresponse()
respBody = response.read()
if response.status == 200:
returned = json.loads(respBody)
print returned["result"]
print response.status, response.reason
print respBody

Here are some examples of running it.

$ ./server.py &
[1] 4903
Listening on 7999
$ ./client.py today
localhost - - [13/Oct/2009 14:17:56] "POST / HTTP/1.1" 200 103
Today is July 4, 1849.
$ ./client.py upper word=CrabEater
localhost - - [13/Oct/2009 14:18:15] "POST / HTTP/1.1" 200 90