Skip to main content

Aio application runner

Project description

Application runner for the aio asyncio framework

Build status


Requires python >= 3.4

Install with:

pip install

Quick start - hello world scheduler

Save the following into a file “hello.conf”

every = 2
func = my_example.schedule_handler

And save the following into a file named “”

import asyncio

def schedule_handler(event):
    yield from asyncio.sleep(1)
    print ("Received scheduled: %s" %

Run with the aio run command

aio run -c hello.conf

The aio config command

When saving or reading configuration options, configuration files are searched for in order from the following locations

  • aio.conf
  • etc/aio.conf
  • /etc/aio/aio.conf

To dump the system configuration you can run

aio config

To dump a configuration section you can use -g or –get with the section name

aio config -g aio

aio config --get aio/commands

To get a configuration option, you can use -g with the section name and option

aio config -g aio:log_level

aio config --get listen/example:example-signal

You can set a configuration option with -s or –set

Options containing interpolation should be enclosed in single quotes

Multi-line options should be enclosed in ” and separated with “\n”

aio config --set aio:log_level DEBUG

aio config -s aio/otherapp:log_level '${aio:log_level}'

aio config -s listen/example:example-signal "my.listener\nmy.listener2"

If no configuration files are present in the standard locations, aio will attempt to save in “aio.conf” in the current working directory

To get or set an option in a particular file you can use the -f flag

aio config -g aio:modules -f custom.conf

aio config -s aio:log_level DEBUG -f custom.conf

When getting config values with the -f flag, ExtendedInterpolation is not used, and you therefore see the raw values

the aio run command

You can run an aio app as follows:

aio run

Or with a custom configuration file

aio -c custom.conf run

On startup aio run sets up the following

  • Configuration - system-wide configuration
  • Modules - initialization and configuration of modules
  • Logging - system logging policies
  • Schedulers - functions called at set times
  • Servers - listening on tcp/udp or other type of socket
  • Signals - functions called in response to events


Configuration is in ini syntax

foo = eggs

While the app is running the system configuration is importable from

from import config

Configuration is parsed using ExtendedInterpolation as follows

  • defaults read
  • user configuration read to initialize modules
  • “aio.conf” read from initialized modules where present
  • user configuration read again


Logging policies can be placed in the configuration file, following pythons fileConfig format

As the configuration is parsed with ExtendedInterpolation you can use options from other sections


The default aio:log_level is INFO

Any sections that begin with handler, logger, or formatter will automattically be added to the relevant logging section

So by adding a section such as


“logger_custom” will automatically be added to the logger keys:



You can list any modules that should be imported at runtime in the configuration

modules = aio.web.server

Configuration for each module is read from a file named “aio.conf” in the module’s path, if it exists.

The initialized modules can be accessed from

from import modules


Schedule definition sections are in the following format


Specify the frequency and the function to call. The function will be wrapped in a coroutine if it isnt one already

every = 2
func = my.scheduler.example_scheduler

The scheduler function receives a ScheduledEvent object

def example_scheduler(event):
    yield from asyncio.sleep(2)
    # do something


Server definition sections are in the following format


The server requires either a factory or a protocol to start

Protocol configuration example:

protocol = my.example.MyServerProtocol
port = 8888

Protocol example code:

class MyServerProtocol(asyncio.Protocol):

    def connection_made(self, transport):
        self.transport = transport

    def data_received(self, data):
        # do stuff

For the protocol option you can either specify a subclass of asyncio.Protocol or you can use a function decorated with

protocol = my.example.protocol
port = 8888

Example code for a server protocol function

import asyncio
def server_protocol():
    yield from asyncio.sleep(1)
    # do something

    return MyServerProtocol

If you need further control over how the protocol is attached you can specify a factory method

Factory configuration example:

factory = my.example.server_factory
port = 8080

The factory method must be wrapped in, and is called in a coroutine
def server_factory(name, protocol, address, port):
    yield from asyncio.sleep(1)
    # do something

    loop = asyncio.get_event_loop()
    return (
        yield from loop.create_server(
           MyServerProtocol, address, port))


Signal definition sections are in the following format


An example listen configuration section

example-signal = my.example.listener

And an example listener function. The listener function will be called as a coroutine

def listener(signal):
    yield from asyncio.sleep(2)

Signals are emitted in a coroutine

yield from app.signals.emit(
    'example-signal', "BOOM!")

You can add multiple subscriptions within each configuration section

You can also subscribe multiple functions to a signal, and you can have multiple “listen/” sections

example-signal = my.example.listener
example-signal-2 = my.example.listener2

example-signal-3 = my.example.listener2

The aio test command

You can test the modules set in the aio:modules configuration option

modules = aio.config

By default the aio test command will test all of your test modules

aio test

You can also specify a module, or modules

aio test

aio test aio.core

If you want to specify a set of modules for testing other than your app modules, you can list them in aio/testing:modules

modules = aio.config

These can include the app modules

modules = ${aio:modules}

Dependencies depends on the following packages usage

The aio command can be run with any commands listed in the [aio/commands] section of its configuration

There are also 3 builtin commands - run, config and test

Initially does not have any config, signals, modules or servers

>>> import
>>> print(,,,
None None () {}

Lets start the app runner in a test loop with the default configuration and print out the signals and config objects

>>> import aio.testing
>>> from import runner
>>> @aio.testing.run_until_complete
... def run_app():
...     runner(['run'])
...     print(
...     print(
...     print(
...     print(
>>> run_app()
<aio.signals.Signals object ...>
<configparser.ConfigParser ...>
(<module '' from ...>,)

Clear the app

We can clear the app vars.

This will also close any socket servers that are currently running

>>> print(,,,
None None () {}

Adding a signal listener

We can add a signal listener in the app config

>>> config = """
... [listen/testlistener]
... test-signal =
... """

Lets create a test listener and make it importable

The listener needs to be wrapped with and is called in a coroutine

>>> import asyncio
... def listener(signal):
...     yield from asyncio.sleep(1)
...     print("Listener received: %s" %
>>> = listener

Running the test…

>>> @aio.testing.run_until_complete
... def run_app(message):
...     runner(['run'], config_string=config)
...     yield from'test-signal', message)
>>> run_app('BOOM!')
Listener received: BOOM!

We can also add listeners programatically

>>> @aio.testing.run_until_complete
... def run_app(message):
...     runner(['run'])
...     yield from'test-signal-2', message)
>>> run_app('BOOM AGAIN!')
Listener received: BOOM AGAIN!

Adding app modules

When you run the app with the default configuration, the only module listed is

>>> @aio.testing.run_until_complete
... def run_app(config_string=None):
...     runner(['run'], config_string=config_string)
...     print(
>>> run_app()
(<module '' from ...>,)

We can make the app runner aware of any modules that we want to include, these are imported at runtime

>>> config = """
... [aio]
... modules =
...          aio.core
... """
>>> run_app(config_string=config)
(<module '' from ...>, <module 'aio.core' from ...>)

Running a scheduler

A basic configuration for a scheduler

>>> config = """
... [schedule/test-scheduler]
... every: 2
... func:
... """

Lets create a scheduler function and make it importable.

The scheduler function is wrapped in a coroutine

>>> def scheduler(event):
...      print('HIT: %s' %
>>> = scheduler

We need to use a aio.testing.run_forever to wait for the scheduled events to occur

>>> @aio.testing.run_forever(timeout=5)
... def run_app():
...     runner(['run'], config_string=config)
...     return

Running the test for 5 seconds we get 3 hits

>>> run_app()
HIT: test-scheduler
HIT: test-scheduler
HIT: test-scheduler

Running a server

Lets set up and run an addition server

At a minimum we should provide a protocol and a port to listen on

>>> config_server_protocol = """
... [server/additiontest]
... protocol:
... port: 8888
... """

Lets create the server protocol and make it importable

>>> class AdditionServerProtocol(asyncio.Protocol):
...     def connection_made(self, transport):
...         self.transport = transport
...     def data_received(self, data):
...         nums = [
...            int(x.strip())
...            for x in
...            data.decode("utf-8").split("+")]
...         self.transport.write(str(sum(nums)).encode())
...         self.transport.close()
>>> = AdditionServerProtocol

After the server is set up, let’s call it with a simple addition

>>> @aio.testing.run_forever
... def run_addition_server(config_string, addition):
...     runner(['run'], config_string=config_string)
...     def call_addition_server():
...          reader, writer = yield from asyncio.open_connection(
...              '', 8888)
...          writer.write(addition.encode())
...          yield from writer.drain()
...          result = yield from
...          print(int(result))
...     return call_addition_server
>>> run_addition_server(
...     config_server_protocol,
...     '2 + 2 + 3')

If you need more control over how the server protocol is created you can specify a factory instead

>>> config_server_factory = """
... [server/additiontest]
... factory =
... port: 8888
... """

The factory method must be decorated with

... def addition_server_factory(name, protocol, address, port):
...     loop = asyncio.get_event_loop()
...     return (
...         yield from loop.create_server(
...            AdditionServerProtocol,
...            address, port))
>>> = addition_server_factory
>>> run_addition_server(
...     config_server_protocol,
...     '17 + 5 + 1')

Project details

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for, version 0.1.8
Filename, size File type Python version Upload date Hashes
Filename, size (16.1 kB) File type Source Python version None Upload date Hashes View

Supported by

AWS AWS Cloud computing Datadog Datadog Monitoring DigiCert DigiCert EV certificate Facebook / Instagram Facebook / Instagram PSF Sponsor Fastly Fastly CDN Google Google Object Storage and Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Salesforce Salesforce PSF Sponsor Sentry Sentry Error logging StatusPage StatusPage Status page