This contains various async utility functions I've written
While my intent is that all of this is already suitably ready for use (and started by extracting private in-use code for public consumption) and will not require breaking changes, you may want to Subscribe to this issue for breaking change notices #10
Until I publish a version Without labeling it either alpha or beta, I intend to keep the option of breaking usage if it improves performance, behavior, or ergonomics to do so.
Until that time, git history may be re-written as well. an artificial history explaining how certain things came to be will more useful in the long term than the reality of how some of this was extracted from other projects of mine for reuse, as well as the amount I have been willing to make small changes for things as small as aesthetics during the early development lifecycle.
A more detailed policy will be provided when I'm more sure of the ergonomics of the provided APIs
Unless specifically documented as supporting it, none of the types within
are intended to be subclassed, and things which impact that will not be taken
into account. These are not marked with typing.Final
due to typecheckers
not supporting the usage pattern required to have lazy typing imports when
taking Final
into account.
pip install mikeshardmind-async-utils
or
pip install async-utils @ git+https://github.com/mikeshardmind/async-utils
swap pip commands for pdm, uv, or other tool as desired.
Examples of this:
- Any caveats should be documented prominently.
- Internally wrapping user functions should retain features such as laziness and backpressure (see: gen_transform.py)
- If the library can't correctly wrap behavior, it shouldn't wrap that behavior.
This library is designed so that you only pay for the abstractions you use, without breaking potential interaction with other library uses.
This includes:
- imports are namespaced, (inspect import is incurred only if you import task_cache).
- typing imports are resolvable at runtime.
- typing imports are lazily evaluated (see _typings.py).
- Some classes/functions with only minor variations are intentionally duplicated partially.
- possibly more things I'm forgetting right now.
There's not much further to say about this goal right now, but this should be expanded on later in the WIP accompanying guide on making concurrent systems written in python fault tolerant at scale.
- The public API surface should be well-typed.
- The public API surface should be introspectible at runtime.
- Decorators that transform types should not destroy introspection.
- Expensive types should be lazily imported or otherwise avoided.
The public api surface is defined by everything in any non-underscored
import name's __all__
. Certain type aliases are provided, but are not
exported as part of the public api surface, and may change.
The project currently uses pyright for development, and both pyright and mypy when ensuring the public api surface is well-typed and compatible with strict use of typechecking. The configurations used are in pyproject.toml.
Use of Any in a few places is intentional for internals.
When possible, things should "just work" even in event loop per thread scenarios.
Examples: caching decorators and ratelimiter
At the current moment, the following are non-goals
- compatability with gevent or other libraries which patch threading and async behavior.
- compatability with non-asyncio event loops
- compatability with non CPython python implementations
Most things now have initial documentation, but no rendered docs site, examples, or prose yet.
What's in each public export, below
Module | Description | Notes |
---|---|---|
bg_loop.py | Contains a context manager that creates an asyncio event loop in a background thread and wraps scheduling to it, | |
bg_tasks.py | Contains a lightweight alternative to asyncio TaskGroups, without the problematic cancellation semantics. | |
corofunc_cache.py | Contains lightweight preemptive async caching decorators. | |
dual_color.py | Contains thread-safe queues with both sync and async colored access. | Missing docs, but behaves similarly to the combined interfaces of threading and asyncio Queues |
gen_transform.py | Contains a function to wrap a synchronous generator in a thread use it asynchronously | |
lockout.py | multi-timeout lockouts tags | |
lru.py | A lightweight lru-cache mapping | |
priority_sem.py | A priority semaphore. | |
ratelimiter.py | A ratelimiting context manager. | |
scheduler.py | A simple in-memory asyncio job runner. | |
sig_service.py | A means of abstracting signal handling for graceful shutdown in multi-color concurrent applications | This needs much better examples and documentation |
task_cache.py | task-based decorators for preemptive async caching. | |
waterfall.py | an async batching mechanism that dispatches by volume or time interval, whichever is satisfied first. |