Twisted

Introduction

Twisted is a powerful Internet framework allowing you to develop networked applications quickly.

PyAMF features the TwistedGateway that allows you to bridge the gap between your Twisted application and AMF based programs.

Examples

Classic

The following example is the ‘classic’ way of setting up a Twisted web service, where you import the reactor manually and start it yourself. For the current, canonical way of setting up a service, see the next section.

The example below is a complete standalone twisted.web server that exposes various functions via PyAMF. We added comments to attempt to explain all relevant lines of code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
from twisted.internet import reactor, defer
from twisted.web import server, static, resource

from pyamf.remoting.gateway.twisted import TwistedGateway
from pyamf.remoting.gateway import expose_request

import logging
        
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s %(levelname)-5.5s [%(name)s] %(message)s'
)

class example:
    """
    An example class that can be used as a PyAMF service.
    """
    def test1(self):
        return "Test 1 Success!"
    
    @expose_request
    def testn(self, request, n):
        """
        This function is decorated to expose the underlying HTTP request,
        which provides access to things such as the requesting client's IP.
        """
        ip = request.getClientIP()

        return "%s said %s!" % (ip, n)

# A standalone function that can be bound to a service.
def add(a, b):
    return a + b

# Create a dictionary mapping the service namespaces to a function
# or class instance
services = {
    'example': example(),
    'myadd': add
}

# Place the namespace mapping into a TwistedGateway
gateway = TwistedGateway(services, logger=logging, expose_request=False,
                         debug=True)

# A base root resource for the twisted.web server
root = resource.Resource()

# Publish the PyAMF gateway at the root URL
root.putChild('', gateway)

# Start the twisted reactor and listen on HTTP port 8080
print 'Running AMF gateway on http://localhost:8080'

reactor.listenTCP(8080, server.Site(root))
reactor.run()

The gateway supports returning a Deferred from your callable. If you are not familiar with Deferreds you should check out the Twisted documentation.

Hopefully this gives you a basic understanding of how to expose functions within Twisted.

Preferred Method

Here is the same service, designed to be run with twistd:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
from twisted.web import resource, server
from twisted.application import service, strports

from pyamf.remoting.gateway.twisted import TwistedGateway
from pyamf.remoting.gateway import expose_request

import logging
        
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s %(levelname)-5.5s [%(name)s] %(message)s'
)

class example:
    """
    An example class that can be used as a PyAMF service.
    """
    def test1(self, n):
        return "Test 1 Success!"

    @expose_request
    def testn(self, request, n):
        """
        This function is decorated to expose the underlying HTTP request,
        which provides access to things such as the requesting client's IP.
        """
        ip = request.getClientIP()

        return "%s said %s!" % (ip, n)

# A standalone function that can be bound to a service.
def add(a, b): 
    return a + b 

# Create a dictionary mapping the service namespaces to a function
# or class instance
services = { 
    'example': example(),
    'myadd': add 
}

# Ideally, just the imports and the code below this comment would be
# in the .tac file; the AMF service would be defined in a module of
# your making

# Place the namespace mapping into a TwistedGateway
gateway = TwistedGateway(services, logger=logging, expose_request=False,
                         debug=True)

# A base root resource for the twisted.web server
root = resource.Resource()

# Publish the PyAMF gateway at the root URL
root.putChild('', gateway)

print 'Running AMF gateway on http://localhost:8080'

application = service.Application('PyAMF Sample Remoting Server')
server = strports.service('tcp:8080', server.Site(root))
server.setServiceParent(application)

WSGI

You can also use Twisted WSGI support in combination with PyAMF WSGIGateway like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import logging

from pyamf.remoting.gateway.wsgi import WSGIGateway
from pyamf.remoting.gateway import expose_request

from twisted.web import server
from twisted.web.wsgi import WSGIResource
from twisted.python.threadpool import ThreadPool
from twisted.internet import reactor
from twisted.application import service, strports

        
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s %(levelname)-5.5s [%(name)s] %(message)s'
)


class example:
    """
    An example class that can be used as a PyAMF service.
    """
    def test1(self, n):
        return "Test 1 Success!"

    @expose_request
    def testn(self, request, n):
        """
        This function is decorated to expose the underlying HTTP request,
        which provides access to things such as the requesting client's IP.
        """
        ip = request['REMOTE_ADDR']

        return "%s said %s!" % (ip, n)


# A standalone function that can be bound to a service.
def add(a, b): 
    return a + b 


# Create a dictionary mapping the service namespaces to a function
# or class instance
services = { 
    'example': example(),
    'myadd': add 
}

# Create and start a thread pool,
wsgiThreadPool = ThreadPool()
wsgiThreadPool.start()

# ensuring that it will be stopped when the reactor shuts down
reactor.addSystemEventTrigger('after', 'shutdown', wsgiThreadPool.stop)

# PyAMF gateway
gateway = WSGIGateway(services, logger=logging, expose_request=False,
                      debug=True)

# Create the WSGI resource
wsgiAppAsResource = WSGIResource(reactor, wsgiThreadPool, gateway)
site = server.Site(wsgiAppAsResource)
server = strports.service('tcp:8080', site)

# Hooks for twistd
application = service.Application('PyAMF Sample Remoting Server')
server.setServiceParent(application)

Run the example using twistd

Save this in a file called something like mytest.tac and then start the web server with the following command:

twistd -noy mytest.tac

Using Twisted’s twistd commandline tool and a .tac file is good for many reasons. Here are some highlights:

  • Using this infrastructure frees you from from having to write a large amount of boilerplate code by hooking your application into existing tools that manage daemonization, logging, choosing a reactor and more.
  • This provides a convenient and standard methodology for separating Twisted services and service configuration from the rest of your project (i.e., the code upon which your service(s) depend).
  • It’s easy to run with a daemon manager (e.g. daemontools).

Test the example

To test the gateway you can use a Python AMF client like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from pyamf.remoting.client import RemotingService

import logging
        
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s %(levelname)-5.5s [%(name)s] %(message)s'
)


url = 'http://localhost:8080'
client = RemotingService(url, logger=logging)

service1 = client.getService('example')
print service1.testn('Hello World')

service2 = client.getService('myadd')
print service2(1,2)

Other Twisted Examples

Binary Socket
Binary Socket using Twisted and Flex.
Guestbook
Simple guestbook using Twisted and Flex.
http://www.artima.com/weblogs/viewpost.jsp?thread=230001
Concurrency with Python, Twisted, and Flex.
Stackless Python
Using Stackless Python and Twisted.