Quick Start

Installation

This package is installed with pip:

pip install greenletio

Converting a Regular Function to Asynchronous

The greenletio.async_() function makes a synchronous function awaitable:

import asyncio
from greenletio import async_

def sync_function(arg):
   pass

async def async_function():
   await async_(sync_function)(42)  # <-- non-blocking function call

asyncio.run(async_function())

The async_ function can also be used as a decorator:

import asyncio
from greenletio import async_

@async_
def sync_function(arg):
   pass

async def async_function():
   await sync_function(42)  # <-- non-blocking function call

asyncio.run(async_function())

Functions wrapped with async_ run inside a greenlet and have the ability to await for asynchronous functions without blocking the asyncio loop.

Awaiting in regular functions

The greenletio.await_() function can be used to await an asynchronous function in a synchronous one, without blocking the asyncio loop:

from greenletio import async_, await_

async def async_function():
   pass

@async_
def sync_function():
   await_(async_function())  # <-- non-blocking await

async def main():
   await sync_function()

asyncio.run(main())

Sometimes it is more convenient to use await_ as a decorator to make an asynchronous function callable as a regular function from synchronous code (once again, without blocking the loop):

from greenletio import async_, await_

@await_
async def async_function():
   pass

@async_
def sync_function():
   async_function()  # <-- this call is non-blocking

async def main():
   await sync_function()

asyncio.run(main())

The await_ function can only be used from code that is running inside a greenlet. A RuntimeError is raised if it is used directly in the asyncio thread.

Synchronous functions used in asynchronous applications must follow the rules that apply to asynchronous functions with regards to not calling any blocking code. They must also use await_ often to prevent blocking the loop.

Implicit Use of a Loop

To simplify some comon use cases such as unit testing, the await_ function can also be used directly in a non-asyncio application:

import asyncio
from greenletio import await_

def main():
   await_(asyncio.sleep(1))

When await_ is used in this way, an asyncio loop is started and managed automatically by greenletio.

Patching Blocking Functions in the Standard Libary

The greenletio.patch_blocking() context manager can be used to import code written for the Python standard library with blocking functions redirected to a set of non-blocking replacements:

from greenletio import patch_blocking

with patch_blocking():
   import requests

async def main():
   await async_(requests.get)('http://google.com')  # non-blocking requests

asyncio.run(main())

The modules that are currently patched are socket, select, selectors, ssl, threading, and time. Applications that use blocking functions in other modules or in third-party packages will need to be manually adapt their code to not block the asyncio loop.

Patching is achieved by replacing original modules from the standard library with drop-in replacements imported from greenletio.green. These adapted versions of the original modules use the async_() and await_() functions and a variety of other techniques to avoid blocking the asyncio loop.

Patching the psycopg2 module

The greenletio.patch_psycopg2() function configures the psycopg2 package to access Postgres databases in non-blocking mode. This function needs to be called once at the start of the application.

Green Functions

The modules under greenletio.green are drop-in replacements of the Python standard library modules of the same name, implemented using the async_, and await_ primitives.

The goal is to provide replacements for commonly used blocking functions in the standard library, so that code written in blocking style can be used asynchronously.

Currently implemented modules are socket, select, selectors, ssl, threading, and time.

Automatic patching of Blocking Functions

The greenletio command-line tool can be used instead of the python command to run a standard (i.e. non-asyncio) script with blocking functions in the Python Standard Library patched to non-blocking versions:

greenletio myscript.py arg1 arg2

The -m option can be used to run a module:

greenletio -m mymodule arg1 arg2