Using PyAMF With TurboGears2

Status:Work in progress

PyAMF provides a simple way to talk to Flex applications using the binary AMF protocol. The main advantages of AMF are that it:

  1. Provides native Flash ActionScript representations of your data, so instantiating it on the client side is almost instantaneous.
  2. Is understood by the RemoteObject in Flex, so it is very easy to implement remote procedure calls from the Flex client.

PyAMF provides serialization of a variety of native python types, from strings, lists, and dictionaries to datetime objects, elementtree elements, and custom classes. And a lot of this is automatic, and very cool. Return a dictionary from your PyAMF service, and you’ll get an ActionScript hash/object on the other side.

PyAMF provides a simple WSGI Application that can be used to setup RPC style service easily in Python. And because TG2 supports WSGI from top to bottom, it’s very simple to setup a TG2 app that contains web-services for your flex applications. All you need to do is:

  1. Install a bunch of stuff and setup a TG2 app
  2. Create a PyAMF gateway for your services
  3. Create a custom route to mount your services in your TG2 app
  4. Build a Flex client that consumes those services
  5. Profit

Installing Stuff:

If you haven’t installed TG2, you’ll need to do that first (see TurboGears 2.1 Standard Installation). You’ll need TurboGears version 2.1 or higher for this to work. All released versions of TG2 should work, but early SVN versions may need to be updated. Once you’ve got an up-to-date version of TG2, you’ll need to install PyAMF, which you can do by:

easy_install pyamf

After that’s done, you can create a new TG2 project in the normal way:

paster quickstart pyamftest
...
cd pyamftest
paster serve development.ini --reload

Your project should now be started, and you should be able to browse to it at http://127.0.0.1:8080

Creating The PyAMF Gateway:

Now, you’re ready to start creating a PyAMF gateway for your Flex app. The first thing to do is to create a new mygateway.py file wherever you want it:

from pyamf.remoting.gateway.wsgi import WSGIGateway

Class Services(object):

   def echo(data):
       return "Turbogears gateway says:" + str(data)

   def sum(a, b):
       return a + b

   def scramble(text):
       from random import shuffle
       s = [x for x in text]
       shuffle(s)
       return ''.join(s)

# Expose our services:
services = {"Services" : Services()}

Gateway = WSGIGateway(services)

This sets up a GatewayController WSGI app that has three services that can be called from flex: echo, sum, and scramble, which each do exactly what they say they do.

Setup A Controller That Uses The GatewayController WSGI App:

Then you can import your GatewayController into root.py:

from tg.controllers import WSGIAppController
from mygateway import Gateway

Now all you have to do is add a method that delegates to the wsgi app:

gateway = WSGIAppController(Gateway)

Of course, you’ll need to the WSGIAppController from tg, and your Gateway from wherever you put it. But once you’ve done those things you’ll have a AMF Gateway mounted at /gateway which you can use from flex.

Create A Flex Client

Now we’re ready for the big time event, we can create a brand new Flex client which talks to our TG2 hosted PyAMF services. This little tutorial pretty much assumes that you know how to use Flex and just want to see how to connect it to a TurboGears app. If that’s not the case you may want to run through one of the Flex tutorials before you try this next step.

Here’s the MXML:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" horizontalAlign="left">
<mx:RemoteObject id="remoteObj" endpoint="http://127.0.0.1:8080/gateway/" destination="Services"
    result="displayResult(event)" fault="remoteFault(event)">
    <mx:method name="scramble" result="scrambleResult(event)"/>
</mx:RemoteObject>
<mx:Button click="remoteObj.echo('Hello, There!')" label="Hello"/>
<mx:HBox width="100%">
    <mx:Button click="remoteObj.sum(new Number(a.text), new Number(b.text))" label="Sum"/>
    <mx:TextInput id="a" text="47"/>
    <mx:TextInput id="b" text="99"/>
</mx:HBox>
<mx:HBox width="100%">
    <mx:Button click="remoteObj.scramble(c.text)" label="Scramble"/>
    <mx:TextInput id="c" text="She sells seashells by the seashore" width="100%"/>
</mx:HBox>
<mx:Text id="result" width="100%" height="100%"/>

<mx:Script>
<![CDATA[
import mx.utils.ObjectUtil;
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;

private function displayResult(re:ResultEvent): void {
    result.text += ObjectUtil.toString(re.result) + "\n";
}

private function scrambleResult(re:ResultEvent): void {
    c.text = re.result as String;
}

private function remoteFault(fault:FaultEvent): void {
    result.text = ObjectUtil.toString(fault);
}
]]>
</mx:Script>
</mx:Application>

You can paste that into a new Flex Builder project (or use the free SDK to create a project with the text editor of your choice). You can then put the HTML and SWF files generated by the builder into your TG2 project’s static directory (wherever you want them to be available) at which point you should be able to browse there, get your Flex app, and use it to connect to the web services you just created.