Skip to content

PyAwaitable

Note

This project originates from a scrapped PEP. For the original text, see here.

CPython currently has no existing C interface for writing asynchronous functions or doing any sort of await operations, other than defining extension types and manually implementing methods like __await__ from scratch. This lack of an API can be seen in some Python-to-C transpilers (such as mypyc) having limited support for asynchronous code.

In the C API, developers are forced to do one of three things when it comes to asynchronous code:

  • Manually implementing coroutines using extension types.
  • Use an external tool to compile their asynchronous code to C.
  • Defer their asynchronous logic to a synchronous Python function, and then call that natively.

PyAwaitable aims to provide a generic interface for working with asynchronous primitives only, as there are other event loop implementations.

For this reason, PyAwaitable does not provide any interface for executing C blocking I/O, as that would likely require leveraging something in the event loop implementation (in asyncio, it would be something like asyncio.to_thread).

Installation

$ pip install pyawaitable

You can then use it in your extension module (example with setuptools):

from setuptools import setup, Extension
import pyawaitable

if __name__ == "__main__":
    setup(
        ...,
        ext_modules=[
            Extension(
                ...,
                include_dirs=[pyawaitable.include()]
            )
        ]
    )

However, if you're distributing a library or something similar, then you want to specify PyAwaitable as a build dependency:

# pyproject.toml example with setuptools
[build-system]
requires = ["setuptools", "pyawaitable"]
build-backend = "setuptools.build_meta"

Note

PyAwaitable needs to be installed as both a runtime dependency and build time dependency.

Vendored Copies

PyAwaitable ships a vendorable version of each release, containing both a pyawaitable.c and pyawaitable.h file. For many user, it's much easier to vendor PyAwaitable than use it off PyPI.

Initialization is slightly different on a vendored version - instead of using pyawaitable_init, you are to use pyawaitable_vendor_init, which takes a module object. For example:

#include "pyawaitable.h"
/* ... */

PyMODINIT_FUNC
PyInit_foo(void)
{
    PyObject *m = PyModule_Create(/* ... */);
    if (!m)
        return NULL;

    if (pyawaitable_init_vendor(m) < 0)
    {
        Py_DECREF(m);
        return NULL;
    }

    return m;
}

Acknowledgements

Special thanks to: