Skip to main content

Boilerplate for asyncio applications

Project description

https://travis-ci.org/cjrh/aiorun.svg?branch=master https://coveralls.io/repos/github/cjrh/aiorun/badge.svg?branch=master https://img.shields.io/pypi/pyversions/aiorun.svg https://img.shields.io/github/tag/cjrh/aiorun.svg https://img.shields.io/badge/install-pip%20install%20aiorun-ff69b4.svg https://img.shields.io/pypi/v/aiorun.svg https://img.shields.io/badge/calver-YYYY.MM.MINOR-22bfda.svg

aiorun

Here’s the big idea (how you use it):

import asyncio
from aiorun import run

async def main():
    # Put your application code here
    await asyncio.sleep(1.0)

if __name__ == '__main__':
    run(main())

This package provides a run() function as the starting point of your asyncio-based application. The run() function will run forever. If you want to shut down when main() completes, just call loop.stop() inside it: that will initiate shutdown.

Why?

The run() function will handle everything that normally needs to be done during the shutdown sequence of the application. All you need to do is write your coroutines and run them.

So what the heck does run() do exactly?? It does these standard, idiomatic actions for asyncio apps:

  • creates a Task for the given coroutine (schedules it on the event loop),

  • calls loop.run_forever(),

  • adds default (and smart) signal handlers for both SIGINT and SIGTERM that will stop the loop;

  • and when the loop stops, the it will…

  • …gather all outstanding tasks,

  • cancel them using task.cancel(),

  • wait for the executor to complete shutdown, and

  • finally close the loop.

All of this stuff is boilerplate that you will never have to write again. So, if you use aiorun this is what you need to remember:

  • Spawn all your work from a single, starting coroutine

  • When a shutdown signal is received, all currently-pending tasks will have CancelledError raised internally. It’s up to you whether you want to handle this (inside your coroutines) with a try/except or not.

  • Try to have executor jobs be shortish, since shutdown will wait for them to finish. If you need a long-running thread or process tasks, use a dedicated thread/subprocess and set daemon=True instead.

There’s not much else to know for general use. aiorun has a few special tools that you might need in unusual circumstances. These are discussed next.

Smart shield for shutdown

It’s unusual, but sometimes you’re going to want a coroutine to not get interrupted by cancellation during the shutdown sequence. You’ll look in the official docs and find asyncio.shield().

The problem is that shield() doesn’t work in shutdown scenarios because the protection offered by shield() only applies if the specific coroutine inside which the shield() is used, gets cancelled directly.

If, however, you go through a conventional shutdown sequence (like aiorun is doing internally), you would call:

  • tasks = all_tasks(), followed by

  • group = gather(*tasks), and then

  • group.cancel()

The problem is that `shield() creates a secret, inner task that will also be captured in the all_tasks() call above, so it will also receive a cancellation signal just like everything else.

Therefore, we have a version of shield() that works better for us: shutdown_waits_for(). If you’ve got a coroutine that must not be cancelled during the shutdown sequence, just wrap it in shutdown_waits_for()!

Here’s an example:

import asyncio
from aiorun import run, shutdown_waits_for

async def corofn():
    await asyncio.sleep(60)
    print('done!')

async def main():
    try:
        await shutdown_waits_for(corofn())
    except asyncio.CancelledError
        print('oh noes!')

run(main())

If you run this program and do nothing, it’ll run forever (‘cause that’s how aiorun.run() works) and you’ll see only done! printed in the output. You’ll have to send a signal or CTRL-C to stop it, at which point you’ll see oh noes! printed. So far no surprises.

If, however, you hit CTRL-C before 60 seconds has passed, you will see oh noes! printed immediately, and then after 60 seconds (since start), done! is printed, and thereafter the program exits.

Behind the scenes, all tasks() would have been cancelled by CTRL-C, except ones wrapped in shutdown_waits_for() calls. In this respect, it is loosely similar to asyncio.shield(), but with special applicability to our shutdown scenario in aiorun().

Oh, and you can use shutdown_waits_for() as if it were asyncio.shield() too. For that use-case it works the same. If you’re using aiorun, there is no reason to use shield().

Project details


Download files

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

Source Distribution

aiorun-2017.11.1.tar.gz (11.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aiorun-2017.11.1-py2.py3-none-any.whl (21.8 kB view details)

Uploaded Python 2Python 3

File details

Details for the file aiorun-2017.11.1.tar.gz.

File metadata

  • Download URL: aiorun-2017.11.1.tar.gz
  • Upload date:
  • Size: 11.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No

File hashes

Hashes for aiorun-2017.11.1.tar.gz
Algorithm Hash digest
SHA256 44c78c4a647696b910d3806c482307471daf455bdef058e5cdaf8049b0fa543a
MD5 6464c9eeda9120f3a4a466a2645f7804
BLAKE2b-256 0528c8488396940ecb9d395b47cf3e7d3509c9e0e7ab02b12a741b07d47a8872

See more details on using hashes here.

File details

Details for the file aiorun-2017.11.1-py2.py3-none-any.whl.

File metadata

File hashes

Hashes for aiorun-2017.11.1-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 6be190a96fae6c3f9f6223b35c53b1eef0acfa94fb4d9bf5356fd6a49c06ad06
MD5 a42d8d52a8a6702bd4a2a1d64b4ef613
BLAKE2b-256 0a1ede347bc5b1c1840e1288326d0611cd814d2eadaa3997e56d5e7396331f9e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page