# 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, debug=True)

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

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

## Other Twisted Examples

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