Hacking the Google App engine with python

Our company had a hack day last week and I thought that I’d have a look at the Google App engine. I was getting used to the management reply of “do you have a budget code?” when asking for a server in the racks, so I thought that I’d experiment with some data in the cloud. I had taken in the story about the Guardian knocking up a quick python web app using Django to notate the MP’s expenses information. I heard tell it took a week so I thought I might get something simple done in a day.

What I hadn’t realised is that Simon Willison, who wrote the Guardian app, had been a major contributor to the Django framework and so knew it backwards. Sigh. I did have a look at the framework but in the midst of various problems with wifi, there was just too much for the first half day. In the end I just cut some simple python code together to see what response times were like and to get a flavour of the database side in the Google App engine. It was a good experience and convinced me to give it another go after learning more about Django. I’ve put some notes here about what I needed for development and a few demo screens about the app that I created.

I needed python on the laptop, so I went for the latest version, v3. That was my first mistake. Google doesn’t work too well with this version; it has some idiosyncracies that require 2.6. I had to uninstall the new version and set up the older version. I would advise over riding the default directories, as some parts of the operation (patchng for django?) seem to falter over spaces in directory names. In the end I installed to the C:/python26 directory and all seemed to work. I then installed the app-engine-patch that is needed to run Django on the Google App engine. I used version 1.1. It was interesting to start looking at Django, but I found that I needed serious learning time. In the end I decided just to set up some simple REST service to input some records and return some XML.

Once python is on your development machine, you need to add the Google app engine tools. These give a simple gui to allow you to control the loading and running of several services, each with their own socket. Write the app using any favourite editor (htmlkit is a simple, free one that I can recommend – although I’m normally on eclipse), then compile and run with this gui. It will run the app on a local server, which is booted automatically. Browse to the server with eg http://localhost:8080 and the app will be run. A few seconds delay the first time, as it is being compiled. You choose the port number when loading the app, so you can have several apps running at one time. Pause them, continue etc.
gae-python

What happens in the app and what sub pages exist, are defined in the application. It effectively links a routine to a page url. When you’ve tested your application locally, just click the Deploy button and it will be sent up to Google’s cloud and it will then be available across the net. It really is simple.

Two main files are required, app.yaml and main.py

An example of app.yaml is,

application: data-form
version: 1
runtime: python
api_version: 1
handlers:
- url: .*
script: main.py

This names the application as data-form and defines the action for all the pages in the application as main.py. There are only a limited number of application names allowed per developer (10, I think).

Main.py is where the logic is stored and has the definitions of the processing, the classes and the urls.

First we have the normal includes for the libraries,

import cgi
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db

The class definition for the objects in the database, in this case a football fixture,

class Fixture(db.Model):
user = db.UserProperty()
league = db.StringProperty()
status = db.StringProperty()
home = db.StringProperty()
hscore = db.StringProperty()
ascore = db.StringProperty()
away = db.StringProperty()
date = db.DateTimeProperty(auto_now_add=True)

The following class creates the display page. It reads from the fixtures database, using the gql querylanguage. Then it writes out the fixtures, followed by a form to input a new fixture.


class MainPage(webapp.RequestHandler):
def get(self):
self.response.out.write('')
fixtures = db.GqlQuery("SELECT * FROM Fixture ORDER BY home ASC LIMIT 10")
#
for fixture in fixtures:
if fixture.status:
self.response.out.write('%s ' % fixture.league )
self.response.out.write('%s ' % fixture.home )
self.response.out.write('%s-' % fixture.hscore )
self.response.out.write('%s ' % fixture.ascore )
self.response.out.write('%s ' % fixture.away )
self.response.out.write('

' )
else:
self.response.out.write('%s v
' % fixture.home)
#
# Write the submission form and the footer of the page
self.response.out.write("""
<div>
Fixture
Started
Finished
<br />
Prem
Championship
Second
<br />
Home
Away
<br />
</div>
<div></div>
""")

Which gives a screen like this,

gae-python-list

The following class is used to add the fixture from the form data.

class AddFixture(webapp.RequestHandler):
def post(self):
fixture = Fixture()
#
if users.get_current_user():
fixture.author = users.get_current_user()
#
fixture.league = self.request.get('league')
fixture.status = self.request.get('status')
fixture.home = self.request.get('home')
fixture.hscore = self.request.get('hscore')
fixture.ascore = self.request.get('ascore')
fixture.away = self.request.get('away')
#
fixture.put()
self.redirect('/')

This class will simply list the matches,

class ListFixtures(webapp.RequestHandler):
def get(self):
self.response.out.write('List fixtures')
#
fixtures = db.GqlQuery("SELECT * FROM Fixture ORDER BY home ASC LIMIT 10")
#
for fixture in fixtures:
if fixture.status:
self.response.out.write('%s ' % fixture.home )
self.response.out.write('%s-' % fixture.hscore )
self.response.out.write('%s ' % fixture.ascore )
self.response.out.write('%s ' % fixture.away )
self.response.out.write('

' )
else:
self.response.out.write('%s v
' % fixture.home)
#
self.response.out.write("""
""")

This class lists the fixtures as XML.

class ListFixturesXML(webapp.RequestHandler):
def get(self):
self.response.out.write('')
#
fixtures = db.GqlQuery("SELECT * FROM Fixture ORDER BY league, home ASC LIMIT 10")
#
for fixture in fixtures:
self.response.out.write('%s' % fixture.league )
self.response.out.write('')
self.response.out.write('%s' % fixture.home )
self.response.out.write('%s' % fixture.hscore )
self.response.out.write('%s' % fixture.ascore )
self.response.out.write('%s' % fixture.away )
self.response.out.write('')

Finally a section that defines which page urls are associated with which procedure. If the address localhost:8080/xml is used, the ListFixturesXML routine will be called.

application = webapp.WSGIApplication(
[('/', MainPage),
('/sign', AddFixture),
('/xml', ListFixturesXML),
('/list', ListFixtures)],
debug=True)
def main():
run_wsgi_app(application)
if __name__ == "__main__":
main()

All relatively painless, once you get the overall idea of the layout. It becomes more complex as the operations require more processing, further classes and interaction, which is where having a framework like Django will bring benefits. I shall take the time to look more into Django, as the value of running this type of service on the cloud are that it is largely free for moderate usage and free of the overheads of setting up any hardware and web server software. Javascript could have been used in the same way; I just wanted to have a look at python, as I may need it in other systems.

Advertisements

~ by ianm on September 25, 2009.

One Response to “Hacking the Google App engine with python”

  1. This is so untidy with this template. I shall return to clean it up when I get time.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: