@@ -20,9 +20,10 @@ This document assumes familiarity with `asyncio`. See [official docs](http://doc
20
20
5.1 [ Use of Delay_ms] ( ./EVENTS.md#51-use-of-delay_ms ) A retriggerable delay
21
21
5.2 [ Long and very long button press] ( ./EVENTS.md#52-long-and-very-long-button-press )
22
22
5.3 [ Application example] ( ./EVENTS.md#53-application-example )
23
- 6 . [ Drivers] ( ./EVENTS.md#6-drivers ) Minimal Event-based drivers
24
- 6.1 [ ESwitch] ( ./EVENTS.md#61-eswitch ) Debounced switch
25
- 6.2 [ EButton] ( ./EVENTS.md#62-ebutton ) Debounced pushbutton with double and long press events
23
+ 6 . [ ELO class] ( ./EVENTS.md#6-elo-class ) Convert a coroutine or task to an event-like object.
24
+ 7 . [ Drivers] ( ./EVENTS.md#7-drivers ) Minimal Event-based drivers
25
+ 7.1 [ ESwitch] ( ./EVENTS.md#71-eswitch ) Debounced switch
26
+ 7.2 [ EButton] ( ./EVENTS.md#72-ebutton ) Debounced pushbutton with double and long press events
26
27
27
28
[ Appendix 1 Polling] ( ./EVENTS.md#100-appendix-1-polling )
28
29
@@ -61,6 +62,11 @@ Users only need to know the names of the bound `Event` instances. By contast
61
62
there is no standard way to specify callbacks, to define the passing of
62
63
callback arguments or to define how to retrieve their return values.
63
64
65
+ There are other ways to define an API without callbacks, notably the stream
66
+ mechanism and the use of asynchronous iterators with ` async for ` . This doc
67
+ discusses the ` Event ` based approach which is ideal for sporadic occurrences
68
+ such as responding to user input.
69
+
64
70
###### [ Contents] ( ./EVENTS.md#0-contents )
65
71
66
72
# 2. Rationale
@@ -135,6 +141,10 @@ ELO examples are:
135
141
| [ Delay_ms] [ 2m ] | Y | Y | Y | Self-setting |
136
142
| [ WaitAll] ( ./EVENTS.md#42-waitall ) | Y | Y | N | See below |
137
143
| [ WaitAny] ( ./EVENTS.md#41-waitany ) | Y | Y | N | |
144
+ | [ ELO instances] ( ./EVENTS.md#44-elo-class ) | Y | N | N | |
145
+
146
+ The ` ELO ` class converts coroutines or ` Task ` instances to event-like objects,
147
+ allowing them to be included in the arguments of event based primitives.
138
148
139
149
Drivers exposing ` Event ` instances include:
140
150
@@ -316,19 +326,118 @@ async def foo():
316
326
else :
317
327
# Normal outcome, process readings
318
328
```
329
+ ###### [ Contents] ( ./EVENTS.md#0-contents )
330
+
331
+ # 6. ELO class
332
+
333
+ This converts a task to an "event-like object", enabling tasks to be included in
334
+ ` WaitAll ` and ` WaitAny ` arguments. An ` ELO ` instance is a wrapper for a ` Task `
335
+ instance and its lifetime is that of its ` Task ` . The constructor can take a
336
+ coroutine or a task as its first argument; in the former case the coro is
337
+ converted to a ` Task ` .
338
+
339
+ #### Constructor args
340
+
341
+ 1 . ` coro ` This may be a coroutine or a ` Task ` instance.
342
+ 2 . ` *args ` Positional args for a coroutine (ignored if a ` Task ` is passed).
343
+ 3 . ` **kwargs ` Keyword args for a coroutine (ignored if a ` Task ` is passed).
344
+
345
+ If a coro is passed it is immediately converted to a ` Task ` and scheduled for
346
+ execution.
347
+
348
+ #### Asynchronous method
349
+
350
+ 1 . ` wait ` Pauses until the ` Task ` is complete or is cancelled. In the latter
351
+ case no exception is thrown.
352
+
353
+ #### Synchronous method
354
+
355
+ 1 . ` __call__ ` Returns the instance's ` Task ` . If the instance's ` Task ` was
356
+ cancelled the ` CancelledError ` exception is returned. The function call operator
357
+ allows a running task to be accessed, e.g. for cancellation. It also enables return values to be
358
+ retrieved.
359
+
360
+ #### Usage example
361
+
362
+ In most use cases an ` ELO ` instance is a throw-away object which allows a coro
363
+ to participate in an event-based primitive:
364
+ ``` python
365
+ evt = asyncio.Event()
366
+ async def my_coro (t ):
367
+ await asyncio.wait(t)
368
+
369
+ async def foo (): # Puase until the event has been triggered and coro has completed
370
+ await WaitAll((evt, ELO(my_coro, 5 ))).wait() # Note argument passing
371
+ ```
372
+ #### Retrieving results
373
+
374
+ A task may return a result on completion. This may be accessed by awaiting the
375
+ ` ELO ` instance's ` Task ` . A reference to the ` Task ` may be acquired with function
376
+ call syntax. The following code fragment illustrates usage. It assumes that
377
+ ` task ` has already been created, and that ` my_coro ` is a coroutine taking an
378
+ integer arg. There is an ` EButton ` instance ` ebutton ` and execution pauses until
379
+ tasks have run to completion and the button has been pressed.
380
+ ``` python
381
+ async def foo ():
382
+ elos = (ELO(my_coro, 5 ), ELO(task))
383
+ events = (ebutton.press,)
384
+ await WaitAll(elos + events).wait()
385
+ for e in elos: # Retrieve results from each task
386
+ r = await e() # Works even though task has already completed
387
+ print (r)
388
+ ```
389
+ This works because it is valid to ` await ` a task which has already completed.
390
+ The ` await ` returns immediately with the result. If ` WaitAny ` were used an ` ELO `
391
+ instance might contain a running task. In this case the line
392
+ ``` python
393
+ r = await e()
394
+ ```
395
+ would pause before returning the result.
396
+
397
+ #### Cancellation
398
+
399
+ The ` Task ` in ` ELO ` instance ` elo ` may be retrieved by issuing ` elo() ` . For
400
+ example the following will subject an ` ELO ` instance to a timeout:
401
+ ``` python
402
+ async def elo_timeout (elo , t ):
403
+ await asyncio.sleep(t)
404
+ elo().cancel() # Retrieve the Task and cancel it
405
+
406
+ async def foo ():
407
+ elo = ELO(my_coro, 5 )
408
+ asyncio.create_task(elo_timeout(2 ))
409
+ await WaitAll((elo, ebutton.press)).wait() # Until button press and ELO either finished or timed out
410
+ ```
411
+ If the ` ELO ` task is cancelled, ` .wait ` terminates; the exception is retained.
412
+ Thus ` WaitAll ` or ` WaitAny ` behaves as if the task had terminated normally. A
413
+ subsequent call to ` elo() ` will return the exception. In an application
414
+ where the task might return a result or be cancelled, the following may be used:
415
+ ``` python
416
+ async def foo ():
417
+ elos = (ELO(my_coro, 5 ), ELO(task))
418
+ events = (ebutton.press,)
419
+ await WaitAll(elos + events).wait()
420
+ for e in elos: # Check each task
421
+ t = e()
422
+ if isinstance (t, asyncio.CancelledError):
423
+ # Handle exception
424
+ else : # Retrieve results
425
+ r = await t # Works even though task has already completed
426
+ print (r)
427
+ ```
319
428
320
429
###### [ Contents] ( ./EVENTS.md#0-contents )
321
430
322
- # 6 . Drivers
431
+ # 7 . Drivers
323
432
324
433
The following device drivers provide an ` Event ` based interface for switches and
325
434
pushbuttons.
326
435
327
- ## 6 .1 ESwitch
436
+ ## 7 .1 ESwitch
328
437
329
438
This is now documented [ here] ( ./DRIVERS.md#31-eswitch-class ) .
330
439
331
- ## 6 .2 EButton
440
+ ## 7 .2 EButton
332
441
333
442
This is now documented [ here] ( ./DRIVERS.md#41-ebutton-class ) .
334
443
0 commit comments