Skip to main content

PEP-550 polyfill, execution context for asyncio.

Project description

aiocontextvars

https://img.shields.io/pypi/v/aiocontextvars.svg https://img.shields.io/travis/fantix/aiocontextvars.svg

This library offers the “thread local” for Python asyncio, also known as “task local”.

import asyncio
from aiocontextvars import ContextVar

var = ContextVar('my_variable')

async def main():
    var.set('main')
    await sub()
    assert var.get() == 'sub'

async def sub():
    assert var.get() == 'main'
    var.set('sub')
    assert var.get() == 'sub'

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

In above example, value main was stored in var, and the following code could then retrieve the value wherever var is available, without having to pass this value around as parameter, e.g. in sub. In the mean time, sub could also mutate the value, while being visible to its caller.

Different than a global variable and similar to thread local, a ContextVar keeps the different values set in different tasks, without messing up:

import asyncio
from aiocontextvars import ContextVar

var = ContextVar('my_variable')

async def main(count):
    var.set(count)
    await asyncio.sleep(1)  # make sure all counts are set before assertion
    await sub(count)
    assert var.get() == count + 1

async def sub(count):
    assert var.get() == count
    var.set(count + 1)
    assert var.get() == count + 1

loop = asyncio.get_event_loop()

tasks = []
for i in range(8):
    tasks.append(loop.create_task(main(i)))

loop.run_until_complete(asyncio.gather(*tasks))

With such, it is usually used to store non-global but shared states, e.g. requests or database connections.

Compatibility

This is a partial PEP-550 polyfill.

PEP-550 proposed a consistent way across threads, generators and asyncio Task s, to manage execution context, which is a kind of shared storage as the context of such executions, with which one could share data within the same execution without passing the data around as function parameters. A close example of such context is threading.local, which offers different data space for different threads. It is obvious that threading.local cannot be used for e.g. Task because usually all coroutines are scheduled within the same thread, generators are similar. That’s the main motivation of PEP-550. However there’s been so much discussion around this PEP about all kinds of possibilities and cases, and it couldn’t get accepted in a short while, thus we decided to come up with this project, trying to solve a subset of all the problems PEP-550 tried to solve, which is, as its name indicates, to implement a contextual storage for asyncio Task. We tried to build API that is compatible with the latest discussion and most possible direction, so that the cost to migrate code to future accepted PEP-550 may be minimized - just replace the import with:

try:
    from contextvars import ContextVar
except ImportError:
    from aiocontextvars import ContextVar, enable_inherit
    enable_inherit()

Inheritance

A key feature of ContextVar is the ability to inherit data across Task``s. When creating a new ``Task within another Task which had a ContextVar set, the new Task shall inherit the ContextVar values from the parent Task. However any changes to the context variables made in the parent task after the child task was spawned are not visible to the child task. The reason is explained in PEP-550 - common usage intent and backwards compatibility. Please follow the link and read more there. Here’s a simple example of inheritance:

import asyncio
from aiocontextvars import ContextVar, enable_inherit

var = ContextVar('my_variable')

async def main():
    var.set('main')
    loop.create_task(sub())
    assert var.get() == 'main'
    var.set('main changed')
    await asyncio.sleep(2)
    assert var.get() == 'main changed'

async def sub():
    assert var.get() == 'main'
    await asyncio.sleep(1)
    assert var.get() == 'main'
    var.set('sub')

loop = asyncio.get_event_loop()
enable_inherit(loop)
loop.run_until_complete(main())

Please be noted that, the inheritance feature needs to be enabled explicitly when using aiocontextvars, while it is a builtin feature for PEP-550. Because aiocontextvars needs to hack the task factory of a given loop to achieve inheritance, so if a custom task factory is needed, make sure it is installed before enabling inheritance. It is also possible to disable inheritance and remove the task factory hack by calling disable_inherit. Meanwhile the return value of enable_inherit is a PEP-343 context, you can do something like this to minimize the impact:

from aiocontextvars import enable_inherit

with enable_inherit():
    loop.create_task(main())

Or even aiocontextvars.create_task can be used as a short of this:

from aiocontextvars import create_task

create_task(main(), loop=loop)

Sometimes it is useful to know whether current ContextVar is inheriting from parent or not. This information is available through Context.inherited:

from aiocontextvars import Context

if Context.current().inherited:
    print('Inherited!')

Credits

Fantix King is the author and maintainer of this library. var.py is modified based on Guido’s simpler.py about PEP-550. This library is open source software under BSD license.

History

0.1.2 (2018-04-04)

  • Support Python 3.5.

0.1.1 (2017-12-03)

  • Fixed setup.py

0.1.0 (2017-12-03)

  • First release on PyPI.

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

aiocontextvars-0.1.2.tar.gz (15.3 kB view details)

Uploaded Source

File details

Details for the file aiocontextvars-0.1.2.tar.gz.

File metadata

File hashes

Hashes for aiocontextvars-0.1.2.tar.gz
Algorithm Hash digest
SHA256 77a8fd70e774de2778357503a5b5a702a405ff458b41d4583677732cfab3b25d
MD5 b297da3bcb42e5c2210da8762f5e08dd
BLAKE2b-256 6c4013c6f49f6c47f2cd4f890a193176abf75fe10d5c3486dd2a9bbde878b5e0

See more details on using hashes here.

Supported by

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