Skip to main content

asyncio-loop-local

asyncio-loop-local storage, singletons and deferred aexit's. Init that pool once and reuse it!

why have loop-local things

Suppose you have some async context manager, like aiohttp.ClientSession:

async with aiohttp.ClientSession() as session:
    async with session.get('http://httpbin.org/get') as resp:
        print(await resp.text())

But the docs tell you: "don't create a session per request". So, you need to reuse it somehow, and this means you need to init it somewhere and then either pass it around all the code that might need it, or... use global variables? There must be a better way.

sticky_singleton_acm

The all-in-one solution to this is:

_ClientSession = asyncio_loop_local.sticky_singleton_acm(ClientSession)

...

async _ClientSession() as session:
    async with session.get('http://httpbin.org/get') as resp:
        print(await resp.text())

The wrapper gives you

  • the same ClientSession for every combination of parameters passed to it (singleton)
  • that doesn't __aexit__ when the block is over, only when the async event loop is closed (sticky ACM)

enter_once + singleton

Those willing to save up a level of indentation (after all, de-denting is a no-op as __aexit__'ing effect is deferred anyway) might be interested in combining two simpler primitives to the same effect:

_ClientSession = asyncio_loop_local.singleton(ClientSession)

...

session = await asyncio_loop_local.enter_once(_ClientSession())
async with session.get('http://httpbin.org/get') as resp:
    print(await resp.text())

singleton

Decorate your callable that generates something, and the returned value will be cached per async loop (and per passed parameters).

_ClientSession = asyncio_loop_local.singleton(ClientSession)

_ClientSession() is _ClientSession()  # as long you're in the same event loop

enter_once

Invoke __anter__ of an asynchronous context manager now, defer __aexit__ to the end of the event loop. Repeated attempts to enter_once on the same object will do nothing.

session = await asyncio_loop_local.enter_once(ClientSession())
...
# __aexit__ will be executed at the end of the event loop

sticky_acm

Wrap an asynchronous context manager. __anter__ just proxies the original context manager's __aenter__, __aexit__ does nothing and is deferred to end of the event loop. It's like enter_once, but in a form to use with async with.

cs = asyncio_loop_local.sticky_acm(ClientSession())
async with cs:  # __aenter__'s
    ...
# __aexit__ will be executed at the end of the event loop

enter

Invoke __anter__ of an asynchronous context manager now, defer __aexit__ to the end of the event loop.

session = await asyncio_loop_local.enter_once(ClientSession())
...
# __aexit__ will be executed at the end of the event loop

storage

Async-loop-local storage. Like thread-local, but loop-local. All the hacks above are built up on it.

alls = asyncio_loop_local._storage.storage()  # gives a loop-associated dict
alls['purpose'] = ('whatever', {'you': 'want'})

If you wanna use locking, consider this:

class LockingDict(asyncio.Lock, dict): pass
alls['careful'] = LockingDict()
with alls['careful'] as d:
   d['a'] = 'b'

_atexit

There's also an async-loop-local atexit hook implementation. It's private so far until some interest is expressed.

Metadata

Release files for asyncio-loop-local 0.0.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for asyncio-loop-local 0.0.4
File Size Uploaded Provenance
asyncio_loop_local-0.0.4.tar.gz 47.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for asyncio-loop-local 0.0.4
File Interpreter ABI Platform
asyncio_loop_local-0.0.4-py3-none-any.whl Python 3 none any Details

Total release size: 80.1 kB

Release files / asyncio_loop_local-0.0.4.tar.gz

Download URL asyncio_loop_local-0.0.4.tar.gz
Size 47.1 kB
Tags Source
SHA-256 checksum
How to use checksums
d4cde7234066a1adc3c27c6dd6a8dc2d87173de3702f9ae0948e44b8a46edde6
BLAKE2b-256 checksum
How to use checksums
842c6b55ad2e0356598f35f296533b7f1e14426403c8a41525b438b0d96110f2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.1 CPython/3.11.6

Release files / asyncio_loop_local-0.0.4-py3-none-any.whl

Download URL asyncio_loop_local-0.0.4-py3-none-any.whl
Size 33.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b3fc14b40349190b3488ed62a0e2f3647c79c3b5ab6b652bcb3a2e139d2192e2
BLAKE2b-256 checksum
How to use checksums
7405a59804e2e68ae1316e3d577738d1cc045936e9a5d03f3e42a9c0c9485625
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.1 CPython/3.11.6

Security for asyncio-loop-local 0.0.4

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page