API Reference¶
All public symbols are importable from the top-level asyncio_util package:
For trio-util compatibility, BoolEvent and ValueEvent are provided as
aliases of AsyncBool and AsyncValue.
Awaitable values¶
asyncio_util.AsyncValue ¶
Bases: Generic[T]
A mutable value whose changes can be awaited.
AsyncValue wraps a value and lets any number of tasks wait for
states ("the value is 20") or transitions ("the value just changed
to 20") without polling and without managing events by hand.
Matching is evaluated synchronously at assignment time, so
:meth:wait_value and :meth:wait_transition never miss a match
even if the value changes again immediately afterwards.
Assigning a value equal to the current one is a no-op and wakes nobody.
Predicates must be fast, side-effect free, and must not raise: they
run synchronously inside the value setter. Because any callable
is treated as a predicate, a value that is itself callable cannot be
waited on by equality — wrap it: lambda v: v == target. If a
predicate does raise, the exception is delivered to that waiter
(raised from its await); other waiters are unaffected.
Note
AsyncValue is not thread-safe. Assign value only from
the thread running the event loop; from other threads use
loop.call_soon_threadsafe.
Example
Source code in src/asyncio_util/_async_value.py
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 | |
value
property
writable
¶
The current value.
Assigning a new value synchronously notifies all matching waiters. Assigning a value equal to the current one does nothing.
wait_value
async
¶
Wait until the value matches, and return the matched value.
Returns immediately if the current value already matches. Otherwise the match is captured at assignment time, so a matching value is returned even if the value changes again before this coroutine resumes (the current value may differ by then).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value_or_predicate
|
T | P
|
A plain value to compare with |
required |
held_for
|
float
|
If greater than 0, only return once the match has held for at least that many seconds. Each time the match is lost, the hold timer restarts. |
0.0
|
timeout
|
float | None
|
If not None, raise :class: |
None
|
Returns:
| Type | Description |
|---|---|
T
|
The value that satisfied the match. |
Source code in src/asyncio_util/_async_value.py
eventual_values
async
¶
Iterate over matching values, always converging to the latest.
Yields the current value first if it matches, then each matching value as it is assigned. A slow consumer may miss intermediate values, but is always caught up with the latest matching value — hence "eventual". Consecutive duplicates are suppressed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value_or_predicate
|
T | P
|
A plain value to compare with |
_any_value
|
held_for
|
float
|
If greater than 0, only yield a value once it has matched continuously for that many seconds. |
0.0
|
Source code in src/asyncio_util/_async_value.py
wait_transition
async
¶
wait_transition(value_or_predicate: T | P2 = _any_transition, *, timeout: float | None = None) -> tuple[T, T]
Wait for the next matching change, and return (value, old_value).
Unlike :meth:wait_value, the current value is never a match:
this waits for an edge, an actual assignment that changes the
value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value_or_predicate
|
T | P2
|
A plain value the new value must equal,
or a callable |
_any_transition
|
timeout
|
float | None
|
If not None, raise :class: |
None
|
Returns:
| Type | Description |
|---|---|
tuple[T, T]
|
The |
Source code in src/asyncio_util/_async_value.py
transitions
async
¶
Iterate over matching changes as (value, old_value) pairs.
Like :meth:wait_transition in a loop: only edges are reported,
never the current value.
Warning
Transitions are not queued. A change that happens while the
loop body is still processing the previous one is missed.
If every edge matters, have the producer push into an
asyncio.Queue (or a :class:~asyncio_util.MulticastQueue)
instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value_or_predicate
|
T | P2
|
A plain value the new value must equal,
or a callable |
_any_transition
|
Source code in src/asyncio_util/_async_value.py
open_transform
async
¶
Derive a new :class:AsyncValue that tracks function(value).
Within the async with block, the derived value is updated
synchronously on every assignment to this value. Updates that
produce an equal output are deduplicated by the derived value's
own setter. Opening the same function object twice shares one
derived value (reference-counted).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
function
|
Callable[[T], T_OUT]
|
A pure, non-raising function applied to each value.
It runs inside the source's setter; if it raises, the
exception propagates to the code assigning |
required |
Example
Source code in src/asyncio_util/_async_value.py
asyncio_util.AsyncBool ¶
Bases: AsyncValue[bool]
Async boolean value with wait/transition capabilities.
Convenience subclass of AsyncValue[bool] with False as default.
Source code in src/asyncio_util/_async_bool.py
asyncio_util.compose_values ¶
compose_values(*, _transform_: Callable[..., T_OUT] | None = None, **value_map: AsyncValue[Any]) -> _ComposeContext
Compose multiple AsyncValues into a single derived AsyncValue.
Changes to any input AsyncValue automatically update the output.
Usage::
with compose_values(x=val_x, y=val_y) as composite:
# composite.value is a namedtuple(x=..., y=...)
await composite.wait_value(lambda v: v.x > 10)
# With transform:
with compose_values(_transform_=lambda v: v.x + v.y,
x=val_x, y=val_y) as total:
await total.wait_value(lambda v: v > 100)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
_transform_
|
Callable[..., T_OUT] | None
|
Optional function to transform the composite namedtuple into the output value. |
None
|
**value_map
|
AsyncValue[Any]
|
Named AsyncValue sources. |
{}
|
Returns:
| Type | Description |
|---|---|
_ComposeContext
|
A context manager yielding the composed AsyncValue. |
Source code in src/asyncio_util/_compose_values.py
asyncio_util.open_held_for
async
¶
Track whether src has been unchanged for duration seconds.
Yields an :class:AsyncBool that becomes True when src has not
changed for duration seconds, and resets to False on any change.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
src
|
AsyncValue[Any]
|
The source value to monitor. |
required |
duration
|
float
|
Seconds the value must remain stable. |
required |
Source code in src/asyncio_util/_async_value_transforms.py
asyncio_util.open_hysteresis
async
¶
open_hysteresis(src: AsyncValue[bool], *, rising_duration: float = 0, falling_duration: float = 0) -> AsyncIterator[AsyncBool]
Apply hysteresis filtering to a boolean source.
The output transitions from False to True only after src has been True for rising_duration seconds, and from True to False only after src has been False for falling_duration seconds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
src
|
AsyncValue[bool]
|
Boolean source value to filter. |
required |
rising_duration
|
float
|
Seconds src must stay True before output rises. |
0
|
falling_duration
|
float
|
Seconds src must stay False before output falls. |
0
|
Source code in src/asyncio_util/_async_value_transforms.py
Tasks and cancellation¶
asyncio_util.wait_any
async
¶
Run async callables concurrently, return when the first completes.
All remaining tasks are cancelled after the first one finishes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args
|
Callable[[], Awaitable[Any]]
|
Zero-argument async callables. |
()
|
Source code in src/asyncio_util/_awaitables.py
asyncio_util.wait_all
async
¶
Run async callables concurrently, return when all complete.
If any task raises, all remaining tasks are cancelled and the first exception is re-raised.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args
|
Callable[[], Awaitable[Any]]
|
Zero-argument async callables. |
()
|
Source code in src/asyncio_util/_awaitables.py
asyncio_util.wait_any_map
async
¶
Like :func:wait_any, but captures return values from named callables.
Positional callables are fire-and-forget (return values discarded). Keyword callables have their return values captured in a namedtuple.
Returns:
| Type | Description |
|---|---|
Any
|
A namedtuple with fields matching the keyword argument names. |
Any
|
Fields for callables that did not finish first are None. |
Usage::
result = await wait_any_map(
timeout=lambda: asyncio.sleep(5),
value=lambda: some_value.wait_value(predicate),
)
if result.value is not None:
process(result.value)
Source code in src/asyncio_util/_awaitables.py
asyncio_util.move_on_when ¶
Cancel the body when fn completes.
Usage::
async with move_on_when(event.wait) as scope:
await long_running_work()
if scope.cancelled_caught:
print("interrupted by event")
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[..., Awaitable[Any]]
|
Async callable whose completion triggers cancellation. |
required |
*args
|
Any
|
Positional arguments forwarded to fn. |
()
|
**kwargs
|
Any
|
Keyword arguments forwarded to fn. |
{}
|
Returns:
| Type | Description |
|---|---|
_MoveOnWhen
|
An async context manager yielding a :class: |
_MoveOnWhen
|
|
_MoveOnWhen
|
interrupted. |
Source code in src/asyncio_util/_move_on_when.py
asyncio_util.CancelScope ¶
Lightweight cancel scope for move_on_when.
Attributes:
| Name | Type | Description |
|---|---|---|
cancelled_caught |
bool
|
True if the body was cancelled by the trigger completing (not by an external cancellation). |
Source code in src/asyncio_util/_move_on_when.py
asyncio_util.run_and_cancelling
async
¶
run_and_cancelling(fn: Callable[..., Awaitable[Any]], *args: Any, **kwargs: Any) -> AsyncIterator[None]
Run fn in the background and cancel it when the body exits.
Usage::
async with run_and_cancelling(background_worker, arg1):
await do_main_work()
# background_worker is cancelled here
If the background task fails while the body is running, the body is not interrupted; the background exception is re-raised when the block exits, unless the body raised its own exception (which takes precedence).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[..., Awaitable[Any]]
|
Async callable to run in the background. |
required |
*args
|
Any
|
Positional arguments forwarded to fn. |
()
|
**kwargs
|
Any
|
Keyword arguments forwarded to fn. |
{}
|
Source code in src/asyncio_util/_move_on_when.py
asyncio_util.start_and_cancelling
async
¶
start_and_cancelling(fn: Callable[..., Awaitable[Any]], *args: Any, **kwargs: Any) -> AsyncIterator[None]
Run fn in the background, wait for it to signal readiness, then cancel it when the body exits.
The callable fn must accept a keyword argument task_status
and call task_status.set() when it is ready.
Usage::
async def server(*, task_status):
listener = await setup_listener()
task_status.set() # signal ready
await serve_forever(listener)
async with start_and_cancelling(server):
await use_server()
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[..., Awaitable[Any]]
|
Async callable that accepts |
required |
*args
|
Any
|
Positional arguments forwarded to fn. |
()
|
**kwargs
|
Any
|
Keyword arguments forwarded to fn. |
{}
|
Source code in src/asyncio_util/_move_on_when.py
Events, streams and timing¶
asyncio_util.RepeatedEvent ¶
An event that can be set multiple times, supporting multiple listeners.
Each call to :meth:set increments an internal counter. Listeners
can either observe every firing (eventual consistency via
:meth:events) or only the latest (drop-while-busy via
:meth:unqueued_events).
Source code in src/asyncio_util/_repeated_event.py
set ¶
wait
async
¶
unqueued_events
async
¶
Yield on each firing, dropping events that arrive during processing.
Events that occur while the consumer is processing the previous one are silently dropped.
Source code in src/asyncio_util/_repeated_event.py
events
async
¶
Yield on each firing with eventual-consistency guarantee.
The iterator is guaranteed to yield at least once for the most recent event, even if intermediate events were missed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
repeat_last
|
bool
|
If True, yield immediately for the current state before waiting for new events. |
False
|
Source code in src/asyncio_util/_repeated_event.py
asyncio_util.MulticastQueue ¶
Bases: Generic[T]
A broadcast queue that delivers each item to every active listener.
Note
This is a lossy queue: if a listener's internal buffer is full, new messages for that listener are silently dropped. Increase queue_size if you need larger buffers, or consume items faster.
Usage::
mq = MulticastQueue()
async with mq.listen() as q:
async for item in q:
process(item)
# In another task:
await mq.broadcast("hello")
Source code in src/asyncio_util/_multicast_queue.py
broadcast
async
¶
listen
async
¶
Register a listener and yield an async iterable of broadcast values.
Source code in src/asyncio_util/_multicast_queue.py
asyncio_util.periodic
async
¶
Yield (elapsed, delta) tuples at regular intervals.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
period
|
float
|
Interval in seconds between iterations. |
required |
Yields:
| Type | Description |
|---|---|
AsyncIterator[tuple[float, float | None]]
|
A tuple of (elapsed_since_start, delta_since_last). |
AsyncIterator[tuple[float, float | None]]
|
delta is None on the first iteration. |
Iterations are scheduled on the absolute start + k * period
grid, so per-iteration latency does not accumulate as drift. If
the loop body takes longer than period, the next iteration starts
(almost) immediately at the next free grid slot; missed slots are
skipped, not queued.
Source code in src/asyncio_util/_periodic.py
asyncio_util.azip
async
¶
Async zip: yield tuples from multiple async iterators in parallel.
Stops when the shortest iterator is exhausted. Advances all iterators concurrently.
Source code in src/asyncio_util/_async_itertools.py
asyncio_util.azip_longest
async
¶
azip_longest(*aiterables: AsyncIterator[T], fillvalue: T | None = None) -> AsyncIterator[tuple[T | None, ...]]
Async zip_longest: yield tuples until all iterators are exhausted.
Exhausted iterators are replaced with fillvalue. Advances all iterators concurrently.
Source code in src/asyncio_util/_async_itertools.py
asyncio_util.iter_move_on_after ¶
Bases: _IterWithTimeout[T]
Wrap an async iterator with a per-item timeout.
If fetching the next item takes longer than timeout seconds, iteration stops silently.
Usage::
async for item in iter_move_on_after(1.0, some_aiter):
process(item)
Source code in src/asyncio_util/_iterators.py
asyncio_util.iter_fail_after ¶
Bases: _IterWithTimeout[T]
Wrap an async iterator with a per-item timeout.
If fetching the next item takes longer than timeout seconds,
raises :class:asyncio.TimeoutError.
Usage::
async for item in iter_fail_after(1.0, some_aiter):
process(item)