The Artima Developer Community
Sponsored Link

Python Buzz Forum
My iWeb Space; Python Integration (Hacky Code Generation)

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Aaron Brady

Posts: 576
Nickname: insommeuk
Registered: Aug, 2003

Aaron Brady is lead developer for Crestsource
My iWeb Space; Python Integration (Hacky Code Generation) Posted: Mar 15, 2008 4:48 AM
Reply to this message Reply

This post originated from an RSS feed registered with Python Buzz by Aaron Brady.
Original Post: My iWeb Space; Python Integration (Hacky Code Generation)
Feed Title: insom.me.uk
Feed URL: http://feeds2.feedburner.com/insommeuk
Feed Description: Posts related to using Python. Some tricks and tips, observations, hacks, and the Brand New Things.
Latest Python Buzz Posts
Latest Python Buzz Posts by Aaron Brady
Latest Posts From insom.me.uk

Advertisement

(It's been a very long time since this RSS feed has had an update. Um. Suprise!)

At work, as a Lunch Time Project™, myself, Ed and David have designed an ultra-ultra-lightweight protocol for exposing a few methods returning simple structures, with the aim of creating rapid AJAX prototypes for internal and "fun" projects.

(In the spirit of internal and fun project, Pembo developed a system for tracking coffee output, Neil built a system for managing our lunch orders, and a starting goal for this new system is tracking our office currency; owed cups of tea.)

The basic premise is that there exists a central registry of thing that can be called, indexed by name and version. The registry knows about the URLs that these services live at, but keeps them a secret so that all calls pass through the proxy.

The proxy receives a request at the wellknown URI "/discovery/1/discover" and receives a response like

[['discovery', 1], ['trac', 1], ['time', 1], ['auth', 1]]

representing each of the services available, and their maximum version available.

Requests to http://[proxy]/[service]/[version]/[method] get passed verbatim through, meaning you could implement the proxy as a mod_rewrite / mod_proxy pair in Apache. (At least, at the moment, if we don't build in more smarts).

The basic architecture is:

Building a service is truly a doddle. Each service must provide a "listMethods" method which returns a JSON list of all of the methods available. You could, in PHP, just place some files in a directory, turn on Apache MultiViews, and be at it. That's what our test service does.

/time/1/listMethods.js

['listMethods', 'getTime']

/time/1/getTime.php

<?php
header("Content-type: text/javascript");
echo time();
?>

Now that we have a service (we have two actually, because the registry is, itself, defined as a service, to be as meta as possible) we should build a client. Obviously, you can just go to http://[proxy]/time/1/getTime and get the result, which is nice.

We don't limit the Content-Type returned, so if you're consuming things that aren't JSON (such as XML, iCal or PDF) you just return an appropriate type, and the code should avoid trying to parse it as Javascript.

Anyway, my first draft at a code-generating Python script is made up of a template (which has some global variables added) and a script to do a bare minimum of introspection:

template.py

import json
import urllib
class Call:
def __init__(self, method):
    self.method = method
def __call__(self, **kwargs):
    u = urllib.urlopen("%s/%s?%s" % (base_url, self.method, urllib.urlencode(kwargs)))
    body = u.read()
    return json.read(body)
                                        class O:
pass
o = O()
for method in methods:
    setattr(o, method, Call(method))

create_py.py

import urllib
import json
import sys
base = "http://[proxy address]/"
template = "template.py"
def main(name):
    u = urllib.urlopen("%s/discovery/1/discover" % base)
    body = u.read().strip()
    j = eval(body)
    f = open("x_%s.py" % name, 'w')
    for item in j:
        if item['name'] == name:
            version = item['version']
            service_url = '%s/%s/%s' % (base, name, version)
            lm_contents = eval(urllib.urlopen('%s/listMethods' % service_url).read())
            print >>f, "base_url = %s" % repr(service_url)
            print >>f, "methods = %s" % repr(lm_contents)
            print >>f, open(template).read()
    f.close()
if __name__ == '__main__':
    main(*sys.argv[1:])

This is ugly, but it does work. Yes, it's got hard-coded URLs, and it assumes Javascript and contains basically no error-checking, but this is a Lunch Time Project, and as such, certain time constraints are in play. You could almost say that the quick-and-dirty approach is half the point.

I would like to move the Call object out to a module that just gets imported, and then add the method to the module namespace itself, but I can't figure out how to do that (the methods in the namespace; I know how to move the code).

At the moment to use it I must do this:

>>> from x_time import o as time
>>> dir(time)
['__doc__', '__module__', 'getTime', 'listMethods']
>>> time.listMethods()
['listMethods', 'getAllTickets']

That import hackery, well, it rubs me up wrong. Can anyone help?

Read: My iWeb Space; Python Integration (Hacky Code Generation)

Topic: How to write a better book (or just better docs!) Previous Topic   Next Topic Topic: python4ply tutorial, part 3

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use