Aiopyramid

A library for leveraging pyramid infrastructure asynchronously using the new asyncio.

Aiopyramid provides tools for making web applications with Pyramid and asyncio. It will not necessarily make your application run faster. Instead, it gives you some tools and patterns to build an application on asynchronous servers that handle many active connections.

This is not a fork of Pyramid and it does not rewrite any Pyramid code to run asynchronously! Pyramid is just that flexible.

Getting Started

Aiopyramid includes a scaffold that creates a “hello world” application, check it out! The scaffold is designed to work with either gunicorn via a custom worker or uWSGI via the uWSGI asyncio plugin.

For example:

pip install aiopyramid gunicorn
pcreate -s aio_starter <project>
cd <project>
python setup.py develop
gunicorn --paste development.ini

There is also a websocket scaffold aio_websocket with basic tools for setting up a websocket server.

For a more detailed walkthrough of how to setup Aiopyramid see the Tutorial.

Contents

Features

Rather than trying to rewrite Pyramid, Aiopyramid provides a set of features that will allow you to run existing code asynchronously where possible.

Views

Aiopyramid provides three view mappers for calling view callables:

When you include Aiopyramid, the default view mapper is replaced with the CoroutineOrExecutorMapper which detects whether your view callable is a coroutine and does a yield from to call it asynchronously. If your view callable is not a coroutine, it will run it in a separate thread to avoid blocking the thread with the main loop. asyncio is not thread-safe, so you will need to guarantee that either in memory resources are not shared between view callables running in the executor or that such resources are synchronized.

This means that you should not necessarily have to change existing views. Also, it is possible to restore the default view mapper, but note that this will mean that coroutine views that do not specify CoroutineMapper as their view mapper will fail.

If most of your view needs to be a coroutine but you want to call out to code that blocks, you can always use run_in_executor. Aiopyramid also provides a decorator, use_executor(), for specifying declaratively that a particular routine should run in a separate thread.

For example:

import asyncio
from aiopyramid.helpers import use_executor

class DatabaseUtilies:

    @use_executor  # query_it is now a coroutine
    def query_it():
        # some code that blocks

Authorization

If you are using the default authorization policy, then you will generally not need to make any modifications to authorize users with Aiopyramid. The exception is if you want to use a callable that performs some io for your __acl__. In that case you will simply need to use a synchronized coroutine so that the authorization policy can call your coroutine like a normal Python function during view lookup.

For example:

import asyncio

from aiopyramid.helpers import synchronize


class MyResource:
    """
    This resource uses a callable for it's
    __acl__ that accesses the db.
    """

    # this
    __acl__ = synchronize(my_coroutine)

    # or this

    @synchronize
    @asyncio.coroutine
    def __acl__(self):
        ...

    # will work

If you are using a custom authorization policy, most likely it will work with Aiopyramid in the same fashion, but it is up to you to guarantee that it does.

Authentication

Authentication poses a problem because the interface for authentication policies uses normal Python methods that the framework expects to call noramlly but at the same time it is usually necessary to perform some io to retrieve relevant information. The built-in authentication policies generally accept a callback function that delegates retrieving principals to the application, but this callback function is also expected to be called in the regular fashion. So, it is necessary to use a synchronized coroutine as a callback function.

The final problem is that synchronized coroutines are expected to be called from within a child greenlet, or in other words from within framework code (see Architecture). However, it is often the case that we will want to access the policy through pyramid.request.Request.authenticated_userid or by calling remember(), etc. from within another coroutine such as a view callable.

To handle both situations, Aiopyramid provides tools for wrapping a callback-based authentication policy to work asynchronously. For example, the following code in your app constructor will allow you to use a coroutine as a callback.

from pyramid.authentication import AuthTktAuthenticationPolicy
from aiopyramid.auth import authn_policy_factory

from .myauth import get_principals

...

# In the includeme or constructor
authentication = authn_policy_factory(
    AuthTktAuthenticationPolicy,
    get_principals,
    'sosecret',
    hashalg='sha512'
)
config.set_authentication_policy(authentication)

Relevant authentication tools will now return a coroutine when called from another coroutine, so you would access the authentication policy using yield from in your view callable since it performs io.

from pyramid.security import remember, forget

...

# in some coroutine

maybe = yield from request.unauthenticated_userid
checked = yield from request.authenticated_userid
principals = yield from request.effective_principals
headers = yield from remember(request, 'george')
fheaders = yield from forget(request)

Note

If you don’t perform asynchronous io or wrap the authentication policy as above, then don’t use yield from in your view. This approach only works for coroutine views. If you have both coroutine views and legacy views running in an executor, you will probably need to write a custom authentication policy.

Tweens

Pyramid allows you to write tweens which wrap the request/response chain. Most existing tweens expect those tweens above and below them to run synchronously. Therefore, if you have a tween that needs to run asynchronously (e.g. it looks up some data from a database for each request), then you will need to write that tween so that it can wait without other tweens needing to explicitly yield from it. For example:

import asyncio

from aiopyramid.helpers import synchronize


def coroutine_logger_tween_factory(handler, registry):
    """
    Example of an asynchronous tween that delegates
    a synchronous function to a child thread.
    This tween asynchronously logs all requests and responses.
    """

    # We use the synchronize decorator because we will call this
    # coroutine from a normal python context
    @synchronize
    # this is a coroutine
    @asyncio.coroutine
    def _async_print(content):
        # print doesn't really need to be run in a separate thread
        # but it works for demonstration purposes

        yield from asyncio.get_event_loop().run_in_executor(
            None,
            print,
            content
        )

    def coroutine_logger_tween(request):
        # The following calls are guaranteed to happen in order
        # but they do not block the event loop

        # print the request on the aio event loop
        # without needing to say yield
        # at this point,
        # other coroutines and requests can be handled
        _async_print(request)

        # get response, this should be done in this greenlet
        # and not as a coroutine because this will call
        # the next tween and subsequently yield if necessary
        response = handler(request)

        # print the response on the aio event loop
        _async_print(request)

        # return response after logging is done
        return response

    return coroutine_logger_tween

Traversal

When using Pyramid’s traversal view lookup, it is often the case that you will want to make some io calls to a database or storage when traversing via __getitem__. When using the default traverser, Pyramid will call __getitem__ as a normal Python function. Therefore, it is necessary to synchronize __getitem__ on any asynchronous resources like so:

import asyncio

from aiopyramid.helpers import synchronize


class MyResource:
    """ This resource performs some asynchronous io. """

    __name__ = "example"
    __parent__ = None

    @synchronize
    @asyncio.coroutine
    def __getitem__(self, key):
        yield from self.example_coroutine()
        return self  # no matter the path, this is the context

    @asyncio.coroutine
    def example_coroutine(self):
        yield from asyncio.sleep(0.1)
        print('I am some async task.')

Servers

Aiopyramid supports both asynchronous gunicorn and the uWSGI asyncio plugin.

Example gunicorn config:

[server:main]
use = egg:gunicorn#main
host = 0.0.0.0
port = 6543
worker_class = aiopyramid.gunicorn.worker.AsyncGunicornWorker

Example uWSGI config:

[uwsgi]
http-socket = 0.0.0.0:6543
workers = 1
plugins =
    asyncio = 50
    greenlet

For those setting up Aiopyramid on a Mac, Ander Ustarroz’s tutorial may prove useful. Rickert Mulder has also provided a fork of uWSGI that allows for quick installation by running pip install git+git://github.com/circlingthesun/uwsgi.git in a virtualenv.

Websockets

Aiopyramid provides additional view mappers for handling websocket connections with either gunicorn or uWSGI. Websockets with gunicorn use the websockets library whereas uWSGI has native websocket support. In either case, the interface is the same.

A function view callable for a websocket connection follows this pattern:

@view_config(mapper=<WebsocketMapper>)
def websocket_callable(ws):
    # do stuff with ws

The ws argument passed to the callable has three methods for communicating with the websocket recv(), send(), and close() methods, which correspond to similar methods in the websockets library. A websocket connection that echoes all messages using gunicorn would be:

from pyramid.view import view_config
from aiopyramid.websocket.config import WebsocketMapper

@view_config(route_name="ws", mapper=WebsocketMapper)
def echo(ws):
    while True:
        message = yield from ws.recv()
        if message is None:
            break
        yield from ws.send(message)

Aiopyramid also provides a view callable class WebsocketConnectionView that has on_message(), on_open(), and on_close() callbacks. Class-based websocket views also have a send() convenience method, otherwise the underyling ws may be accessed as self.ws. Simply extend WebsocketConnectionView specifying the correct view mapper for your server either via the __view_mapper__ attribute or the view_config decorator. The above example could be rewritten in a larger project, this time using uWSGI, as follows:

from pyramid.view import view_config
from aiopyramid.websocket.view import WebsocketConnectionView
from aiopyramid.websocket.config import UWSGIWebsocketMapper

from myproject.resources import MyWebsocketContext

class MyWebsocket(WebsocketConnectionView):
    __view_mapper__ = UWSGIWebsocketMapper


@view_config(context=MyWebsocketContext)
class EchoWebsocket(MyWebsocket):

    def on_message(self, message):
        yield from self.send(message)

The underyling websocket implementations of uWSGI and websockets differ in how they pass on the WebSocket message. uWSGI always sends bytes even when the WebSocket frame indicates that the message is text, whereas websockets decodes text messages to str. Aiopyramid attempts to match the behavior of websockets by default, which means that it coerces messages from uWSGI to str where possible. To adjust this behavior, you can set the use_str flag to False, or alternatively to coerce websockets messages back to bytes, set the use_bytes flag to True:

# In your app constructor
from aiopyramid.websocket.config import WebsocketMapper

WebsocketMapper.use_bytes = True
uWSGI Special Note

Aiopyramid uses a special WebsocketClosed exception to disconnect a greenlet after a websocket has been closed. This exception will be visible in log ouput when using uWSGI. In order to squelch this message, wrap the wsgi application in the ignore_websocket_closed() middleware in your application’s constructor like so:

from aiopyramid.websocket.helpers import ignore_websocket_closed

...
app = config.make_wsgi_app()
return ignore_websocket_closed(app)

Tutorial

This is a basic tutorial for setting up a new project with Aiopyramid.

Install Aiopyramid and Initialize Project

It is highly recommended that you use a virtual environment for your project. The tutorial will assume that you are using virtualenvwrapper with a virtualenv created like so:

mkvirtualenv aiotutorial --python=/path/to/python3.4/interpreter

Once you have your tutorial environment active, install Aiopyramid:

pip install aiopyramid

This will also install the Pyramid framework. Now create a new project using the aio_websocket scaffold.

pcreate -s aio_websocket aiotutorial

This will make an aiotutorial directory with the following structure:

.
├── aiotutorial         << Our Python package
│   ├── __init__.py     << main file, contains the app constructor
│   ├── templates       << directory for storing jinja templates
│   │   └── home.jinja2 << template for the example homepage, contains a websocket test
│   ├── tests.py        << tests module, contains tests for each of our existing views
│   └── views.py        << views module, contains view callables
├── CHANGES.rst         << file for tracking changes to the library
├── development.ini     << config file, contains project and server settings
├── MANIFEST.in         << manifest file for distributing the project
├── README.rst          << readme for bragging about the project
└── setup.py            << Python module for distributing the package and managing dependencies

Let’s look at some of these files a little closer.

App Constructor

The aiotutorial/__init__.py file contains the constructor for our app. It loads the logging config from the development.ini config file and sets up Python logging. This is necessary because the logging configuration won’t be automatically detected when using Python3. Then, it sets up two routes home and echo that we can tie into with our views. Finally, the constructor scans the project for configuration decorators and builds the wsgi callable.

The app constructor is the place where we will connect Python libraries to our application and perform other configuration tasks.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import logging.config

from pyramid.config import Configurator


def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """

    # support logging in python3
    logging.config.fileConfig(
        settings['logging.config'],
        disable_existing_loggers=False
    )

    config = Configurator(settings=settings)
    config.add_route('home', '/')
    config.add_route('echo', '/echo')
    config.scan()
    return config.make_wsgi_app()

Note

Thinking Asynchronously

The app constructor is called once to setup the application, which means that it is a synchronous context. The app is constructed before any requests are served, so it is safe to call blocking code here.

Tests

The aiotutorial/tests.py file is a Python module with unittests for each of our views. Let’s look at the test case for the home page:

1
2
3
4
5
6
7
8
class HomeTestCase(unittest.TestCase):

    def test_home_view(self):
        from .views import home

        request = testing.DummyRequest()
        info = asyncio.get_event_loop().run_until_complete(home(request))
        self.assertEqual(info['title'], 'aiotutorial websocket test')

Since test runners for unittest expect tests, such as test_home_view, to run synchronously but our home view is a coroutine, we need to manually obtain an asyncio event loop and run our view. Line 6 obtains a dummy request from pyramid.testing. We then pass that request to our view and run it on line 7. Finally, line 8 makes assertions about the kind of output we expect from our view.

Views

This is the brains of our application, the place where decisions about how to respond to a particular request are made, and as such this is the place where you will most often start chaining together coroutines to perform asynchronous tasks. Let’s look at each of the example views in turn:

1
2
3
4
5
6
@view_config(route_name='home', renderer='aiotutorial:templates/home.jinja2')
@asyncio.coroutine
def home(request):
    wait_time = float(request.params.get('sleep', 0.1))
    yield from asyncio.sleep(wait_time)
    return {'title': 'aiotutorial websocket test', 'wait_time': wait_time}

For those already familiar with Pyramid most of this view should require no explanation. The important parts for running asynchronously are lines 2 and 5.

The view_config() decorator on line 1 ties this view to the ‘home’ route declared in the app constructor. It also assigns a renderer to the view that will render the data returned into the template/home.jinja template and return a response to the user. Line 2 wraps the view in a coroutine which differentiates it from a generator or native coroutine. Line 3 is the signature for the coroutine. Aiopyramid view mappers do not change the two default signarures for views, i.e. views that accept a request and views that accept a context and a request. On line 4, we retrieve a sleep parameter, from the request (the parameter can be either part of the querystring or the body). If the request doesn’t include a sleep parameter, the view defaults to 0.1. We don’t need to use yield from because request.params.get doesn’t return a coroutine or future. The data for the request exists in memory so retrieving the parameter should be very fast. Line 5 simulates performing some asynchronous task by suspending the coroutine and delegating to another coroutine, asyncio.sleep(), which uses events to wait for wait_time seconds. Using yield from is very important, without it the coroutine would continue without sleeping. Line 6 returns a Python dictionary that will be passed to the jinja2 renderer.

The second view accepts a websocket connection:

1
2
3
4
5
6
7
8
@view_config(route_name='echo', mapper=WebsocketMapper)
@asyncio.coroutine
def echo(ws):
    while True:
        message = yield from ws.recv()
        if message is None:
            break
        yield from ws.send(message)

This view is tied to the ‘echo’ route from the app constructor. Note that we use a special view mapper for websocket connections. The aiopyramid.websocket.config.WebsocketMapper changes the signature of the view to accept a single websocket connection instead of a request. The connection object has three methods for communicating with the websocket recv(), send(), and close() that correspond to similar methods in the websockets library.

This websocket view will run echoing the data it recieves until the connection is closed. On line 5 we use yield from to wait until a message is received. If the message is None, then we know that the websocket has closed and we break the loop to complete the echo coroutine. Otherwise, line 7 simply returns the same message back to the websocket. Very simple. In both cases when we need to perform some io we use yield from to suspend our coroutine and delegate to another.

This kind of explicit yielding is a nice advantage for readability in Python code. It shows us exactly where we are calling asynchronous code.

Development.ini

The development.ini file contains the config for the project. Most of these settings could be specified in the app constructor but it makes sense to separate out these values from procedural code. Here is an overview of the two most important sections:

[app:main]
use = egg:aiotutorial

pyramid.includes =
    aiopyramid
    pyramid_jinja2

# for py3
logging.config = %(here)s/development.ini

The [app:main] section contains the settings that will be passed to the app constructor as settings. This is where we include extensions for Pyramid such as Aiopyramid and the jinja templating library.

The [server:main] configures the default server for the project, which in this case is gunicorn:

[server:main]
use = egg:gunicorn#main
host = 0.0.0.0
port = 6543
worker_class = aiopyramid.gunicorn.worker.AsyncGunicornWorker

The port setting here is the port that we will use to access the application, such as in a browser. The worker_class is set to the aiopyramid.gunicorn.worker.AsyncGunicornWorker because we need to have gunicorn setup the Aiopyramid Architecture for us.

Setup

The setup.py file makes the aiotutorial package easy to distirbute, and it is also a good way, although not the only good way, to manage dependencies for our project. Lines 18-21 list the Python packages that we need for this project.

requires = [
    'aiopyramid[gunicorn]',
    'pyramid_jinja2',
]

Note about View Mappers

The default view mapper that Aiopyramid sets up when it is included by the application tries to be as robust as possible. It will inspect all of the views that we configure and try to guess whether or not they are coroutines. If the view looks like a coroutine, in other words if it has a yield from in it, the framework will treat it as a coroutine, otherwise it will assume it is legacy code and will run it in a separate thread to avoid blocking the event loop. This is very important.

When using Aiopyramid view mappers, it is actually not necessary to explicitly decorate view callables with asyncio.coroutine() as in the examples because the mapper will wrap views that appear to be coroutines for you. It is still good practice to explicitly wrap your views because it facilitates using them in places where a view mapper may not be active, but if you are annoyed by the repetition, then you can skip writing @asyncio.coroutine before every view as long as you remember what is a coroutine.

Making Sure it Works

The last step in initializing the project is to install out dependencies and test out that the scaffold works as we expect:

python setup.py develop

You can also use setup.py to run unittests:

python setup.py test

You should see the following at the end of the output:

test_home_view (aiotutorial.tests.HomeTestCase) ... ok
test_echo_view (aiotutorial.tests.WSTest) ... ok

----------------------------------------------------------------------
Ran 2 tests in 1.709s

OK

If you don’t like the test output from setup.py, consider using a test runner like pytest.

Now try running the server and visiting the homepage:

gunicorn --paste development.ini

Open your browser to http://127.0.0.1:6543 to see the JavaScript test of the our echo websocket. You should see the following output:

aiotutorial websocket test

CONNECTED

SENT: Aiopyramid echo test.

RESPONSE: Aiopyramid echo test.

DISCONNECTED

This shows that the websocket is working. If you want to verify that the server is able to handle multiple requests on a single thread, simply open a different browser (to avoid browser connection limitations) and go to http://127.0.0.1:6543?sleep=10. The new browser should take roughly ten seconds to load the page because our view is waiting for the value of sleep. However, while that request is ongoing, you can refresh your first browser and see that the server is still able to fulfill requests.

Congratulations! You have successfuly setup a highly configurable asynchronous server using Aiopyramid!

Note

Extra Credit

If you really want to see the power of asynchronous programming in Python, obtain a copy of slowloris and run it against your knew Aiopyramid server and some non-asynchronous server. For example, you could run a simple Django application with gunicorn. You should see that the Aiopyramid server is still able to respond to requests whereas the Django server is bogged down. You could also use a simple PHP application using Apache to see this difference.

Architecture

Aiopyramid uses a design similar to the uWSGI asyncio plugin. The asyncio event loop runs in a parent greenlet, while wsgi callables run in child greenlets. Because the callables are running in greenlets, it is possible to suspend a callable and switch to parent to run coroutines all on one event loop. Each task tracks which child greenlet it belongs to and switches back to the appropriate callable when it is done.

The greenlet model makes it possible to have any Python code wait for a coroutine even when that code is unaware of asyncio. The uWSGI asyncio plugin sets up the architecture by itself, but it is also possible to setup this architecture whenever we have a running asyncio event loop using spawn_greenlet().

For example, there may be times when a coroutine would need to call some function a that later calls a coroutine b. Since coroutines run in the parent greenlet (i.e. on the event loop) and the function a cannot yield from b because it is not a coroutine itself, the parent coroutine will need to set up the Aiopyramid architecture so that b can be synchronized with synchronize() and called like a normal function from inside a.

The following code demonstrates this usage without needing to setup a server.

>>> import asyncio
>>> from aiopyramid.helpers import synchronize, spawn_greenlet
>>>
>>> @synchronize
... @asyncio.coroutine
... def some_async_task():
...   print('I am a synchronized coroutine.')
...   yield from asyncio.sleep(0.2)
...   print('Synchronized task done.')
...
>>> def normal_function():
...   print('I am normal function that needs to call some_async_task')
...   some_async_task()
...   print('I (normal_function) called it, and it is done now like I expect.')
...
>>> @asyncio.coroutine
... def parent():
...   print('I am a traditional coroutine that needs to call the naive normal_function')
...   yield from spawn_greenlet(normal_function)
...   print('All is done.')
...
>>> loop = asyncio.get_event_loop()
>>> loop.run_until_complete(parent())
I am a traditional coroutine that needs to call the naive normal_function
I am normal function that needs to call some_async_task
I am a synchronized coroutine.
Synchronized task done.
I (normal_function) called it, and it is done now like I expect.
All is done.

Please feel free to use this in other asyncio projects that don’t use Pyramid because it’s awesome.

To avoid confusion, it is worth making explicit the fact that this approach is for incorporating code that is fast and non-blocking itself but needs to call a coroutine to do some io. Don’t try to use this to call long-running or blocking Python functions. Instead, use run_in_executor, which is what Aiopyramid does by default with view callables that don’t appear to be coroutines.

History

Aiopyramid was originally based on pyramid_asyncio, but I chose a different approach for the following reasons:

On the other hand Aiopyramid is designed to follow these principles:

  • Aiopyramid should extend Pyramid through existing Pyramid mechanisms where possible.
  • Asynchronous code should be wrapped so that existing callers can treat it as synchronous code.
  • Ultimately, no framework can guarantee that all io calls are non-blocking because it is always possible for a programmer to call out to some function that blocks (in other words, the programmer forgets to wrap long-running calls in run_in_executor). So, frameworks should leave the determination of what code is safe to the programmer and instead provide tools for programmers to make educated decisions about what Python libraries can be used on an asynchronous server. Following the Pyramid philosophy, frameworks should not get in the way.

The first principle is one of the reasons why I used view mappers rather than patching the router. View mappers are a mechanism already in place to handle how views are called. We don’t need to rewrite vast parts of Pyramid to run a view in the asyncio event loop. Yes, Pyramid is that awesome.

The second principle is what allows Aiopyramid to support existing extensions. The goal is to isolate asynchronous code from code that expects a synchronous response. Those methods that already exist in Pyramid should not be rewritten as coroutines because we don’t know who will try to call them as regular methods.

Most of the Pyramid framework does not run io blocking code. So, it is not actually necessary to change the framework itself. Instead we need tools for making application code asynchronous. It should be possible to run an existing simple url dispatch application asynchronously without modification. Blocking code will naturally end up being run in a separate thread via the run_in_executor method. This allows you to optimize only those highly concurrent views in your application or add in websocket support without needing to refactor all of the code.

It is easy to simulate a multithreaded server by increasing the number of threads available to the executor.

For example, include the following in your application’s constructor:

import asyncio
from concurrent.futures import ThreadPoolExecutor
...
asyncio.get_event_loop().set_default_executor(ThreadPoolExecutor(max_workers=150))

Note

It should be noted that Aiopyramid is not thread-safe by nature. You will need to ensure that in memory resources are not modified by multiple non-coroutine view callables. For most existing applications, this should not be a problem.

Tests

Core functionality is backed by tests. The Aiopyramid requires pytest. To run the tests, grab the code on github, install pytest, and run it like so:

git clone https://github.com/housleyjk/aiopyramid
cd aiopyramid
pip install pytest
py.test

Contributors

  • Jason Housley
  • Guillaume Gauvrit
  • Tiago Requeijo
  • Ander Ustarroz
  • Ramon Navarro Bosch
  • Rickert Mulder