@@ -325,8 +325,9 @@ is next scheduled. This mechanism works with nested coros. However there is a
325
325
limitation. If a coro issues ` await uasyncio.sleep(secs) ` or
326
326
` uasyncio.sleep_ms(ms) ` scheduling will not occur until the time has elapsed.
327
327
This introduces latency into cancellation which matters in some use-cases.
328
- Crucially there is no inbuilt mechanism for verifying when cancellation has
329
- actually occurred. This library provides solutions.
328
+ There are other potential sources of latency in the form of slow code.
329
+ ` uasyncio ` has no mechanism for verifying when cancellation has actually
330
+ occurred. This library provides solutions.
330
331
331
332
Cancellation is supported by two classes, ` Cancellable ` and ` NamedTask ` . The
332
333
` Cancellable ` class allows the creation of named groups of anonymous tasks
@@ -347,7 +348,7 @@ latency.
347
348
348
349
The asynchronous ` sleep ` function takes two args:
349
350
* ` t ` Mandatory. Time in seconds. May be integer or float.
350
- * ` granularity ` Optional. Integer >= 0, units ms. Default 100ms. Defines the
351
+ * ` granularity ` Optional integer >= 0, units ms. Default 100ms. Defines the
351
352
maximum latency. Small values reduce latency at cost of increased scheduler
352
353
workload.
353
354
@@ -374,12 +375,12 @@ async def comms(): # Perform some communications task
374
375
375
376
A ` Cancellable ` task is declared with the ` @cancellable ` decorator. When
376
377
scheduled it will receive an initial arg which is a ` TaskId ` instance followed
377
- by any user-defined args. The ` TaskId ` instance can be ignored unless custom
378
- cleanup is required (see below) .
378
+ by any user-defined args. The ` TaskId ` instance is normally ignored however it
379
+ can be useful for debugging - task_id() produces a unique integer task ID .
379
380
380
381
``` python
381
382
@cancellable
382
- async def print_nums (task_id , num ):
383
+ async def print_nums (_ , num ): # Discard task_id
383
384
while True :
384
385
print (num)
385
386
num += 1
@@ -410,9 +411,8 @@ Constructor optional positional args:
410
411
Constructor optional keyword arg:
411
412
* ` group ` Integer or string. Default 0. See Groups below.
412
413
413
- Class public methods:
414
- * ` cancel_all ` Asynchronous. In practice this is the only method required by
415
- user code.
414
+ Public class method:
415
+ * ` cancel_all ` Asynchronous.
416
416
Optional args ` group ` default 0, ` nowait ` default ` False ` .
417
417
The ` nowait ` arg is for use by the ` NamedTask ` derived class. The default
418
418
value is assumed below.
@@ -424,14 +424,10 @@ Class public methods:
424
424
the coro is written using the ` @cancellable ` decorator this is handled
425
425
automatically.
426
426
It is possible to trap the ` StopTask ` exception: see 'Custom cleanup' below.
427
- * ` end ` Synchronous. Arg: The coro task number. Informs the class that a
428
- ` Cancellable ` instance has ended, either normally or by cancellation.
429
- * ` stopped ` Synchronous. Arg: The coro task number. Informs the class that a
430
- Cancellable instance has been cancelled.
431
427
432
- Bound method:
428
+ Public bound method:
433
429
* ` __call__ ` This returns the coro and is used to schedule the task using the
434
- event loop ` create_task() ` method.
430
+ event loop ` create_task() ` method using function call syntax .
435
431
436
432
### 4.2.1 Groups
437
433
@@ -449,30 +445,27 @@ exception to perform custom cleanup operations. This may be done as below:
449
445
``` python
450
446
@cancellable
451
447
async def foo (task_id , arg ):
452
- try :
453
- await sleep(1 ) # Main body of task
454
- except StopTask:
455
- # perform custom cleanup
456
- raise # Propagate exception to closure
448
+ while True :
449
+ try :
450
+ await sleep(1 ) # Main body of task
451
+ except StopTask:
452
+ # perform custom cleanup
453
+ return # Respond by quitting
457
454
```
458
455
459
- Where full control is required a cancellable task should be written without the
460
- decorator. The following example returns ` True ` if it ends normally or ` False `
461
- if cancelled.
456
+ The following example returns ` True ` if it ends normally or ` False ` if
457
+ cancelled.
462
458
463
459
``` python
460
+ @cancellable
464
461
async def bar (task_id ):
465
462
task_no = task_id() # Retrieve task no. from TaskId instance
466
463
try :
467
- await sleep(1 )
464
+ await sleep(1 ) # Main body of task
468
465
except StopTask:
469
- Cancellable.stopped(task_no)
470
466
return False
471
467
else :
472
- Cancellable.stopped(task_no)
473
468
return True
474
- finally :
475
- Cancellable.end(task_no)
476
469
```
477
470
478
471
###### [ Contents] ( ./PRIMITIVES.md#contents )
@@ -481,16 +474,16 @@ async def bar(task_id):
481
474
482
475
A ` NamedTask ` instance is associated with a user-defined name such that the
483
476
name may outlive the task: a coro may end but the class enables its state to be
484
- checked. It is a subclass of ` Cancellable ` and ts constructor disallows
477
+ checked. It is a subclass of ` Cancellable ` and its constructor disallows
485
478
duplicate names: each instance of a coro must be assigned a unique name.
486
479
487
- A ` NamedTask ` coro is normally defined with the ` @cancellable ` decorator. When
488
- scheduled it will receive an initial arg which is a ` TaskId ` instance followed
489
- by any user-defined args. Normally the ` task_id ` can be ignored.
480
+ A ` NamedTask ` coro is defined with the ` @cancellable ` decorator. When scheduled
481
+ it will receive an initial arg which is a ` TaskId ` instance followed by any
482
+ user-defined args. Normally the ` task_id ` is ignored as per ` Cancellable ` .
490
483
491
484
``` python
492
485
@cancellable
493
- async def foo (task_id , arg1 , arg2 ):
486
+ async def foo (_ , arg1 , arg2 ):
494
487
await asyn.sleep(1 )
495
488
print (' Task foo has ended.' , arg1, arg2)
496
489
```
@@ -524,29 +517,25 @@ Mandatory args:
524
517
* Any further positional args are passed to the coro.
525
518
526
519
Optional keyword only arg:
527
- * ` barrier ` A ` Barrier ` instance may be passed if the cancelling task needs to
528
- wait for confirmation of successful cancellation.
520
+ * ` barrier ` A ` Barrier ` instance may be passed. See below.
529
521
530
- Class methods:
522
+ Public class methods:
531
523
* ` cancel ` Asynchronous. ** [ API change: was synchronous] **
532
524
Mandatory arg: a coro name.
533
525
Optional boolean arg ` nowait ` default ` True `
534
526
By default it will return soon. If ` nowait ` is ` False ` it will pause until the
535
527
coro has completed cancellation.
536
- The named coro will receive a ` CancelError ` exception the next time it is
537
- scheduled. The coro should trap this, call the ` end ` method and return. The
538
- ` @namedtask ` decorator handles this, ensuring ` end ` is called under all
539
- circumstances.
528
+ The named coro will receive a ` StopTask ` exception the next time it is
529
+ scheduled. If the ` @namedtask ` decorator is used this is transparent to the
530
+ user but the exception may be trapped for custom cleanup (see below).
540
531
` cancel ` will return ` True ` if the coro was cancelled. It will return ` False `
541
532
if the coro has already ended or been cancelled.
542
533
* ` is_running ` Synchronous. Arg: A coro name. Returns ` True ` if coro is queued
543
534
for scheduling, ` False ` if it has ended or been cancelled.
544
- * ` end ` Synchronous. Arg: A coro name. Run by the ` NamedTask ` instance to
545
- inform the class that the instance has ended. Completes quickly.
546
535
547
- Bound method:
536
+ Public bound method:
548
537
* ` __call__ ` This returns the coro and is used to schedule the task using the
549
- event loop ` create_task() ` method.
538
+ event loop ` create_task() ` method using function call syntax .
550
539
551
540
### 4.3.1 Latency and Barrier objects
552
541
@@ -570,39 +559,30 @@ See examples in `cantest.py` e.g. `cancel_test2()`.
570
559
571
560
A coroutine to be used as a ` NamedTask ` can intercept the ` StopTask ` exception
572
561
if necessary. This might be done for cleanup or to return a 'cancelled' status.
573
- To do this, do not use the ` @cancellable ` decorator. The coro should have the
574
- following form:
562
+ The coro should have the following form:
575
563
576
564
``` python
565
+ @cancellable
577
566
async def foo (task_id ):
578
567
try :
579
568
await asyncio.sleep(1 ) # User code here
580
- NamedTask.stopped(task_id) # Inform class that it has stopped
581
- return True
582
569
except StopTask:
583
- return False
584
- finally :
585
- # Inform class that it has stopped or been cancelled
586
- NamedTask.end(task_id)
570
+ return False # Cleanup code
571
+ else :
572
+ return True # Normal exit
587
573
```
588
574
589
575
### 4.3.3 Changes
590
576
591
577
The ` NamedTask ` class has been rewritten as a subclass of ` Cancellable ` . This
592
578
is to simplify the code and to ensure accuracy of the ` is_running ` method. The
593
579
latest API changes are:
594
- * ` Cancellable.stopped() ` is now synchronous .
580
+ * ` Cancellable.stopped() ` is no longer a public method .
595
581
* ` NamedTask.cancel() ` is now asynchronous.
596
582
* ` NamedTask ` coros now receive a ` TaskId ` instance as their 1st arg.
597
- * The ` @namedtask ` works but is now an alias for ` @cancellable ` .
583
+ * ` @namedtask ` still works but is now an alias for ` @cancellable ` .
598
584
599
585
The drive to simplify code comes from the fact that ` uasyncio ` is itself under
600
586
development. Tracking changes is an inevitable headache.
601
587
602
588
###### [ Contents] ( ./PRIMITIVES.md#contents )
603
-
604
- #### ExitGate (obsolete)
605
-
606
- This was a nasty hack to fake task cancellation at a time when uasyncio did not
607
- support it. The code remains in the module to avoid breaking existing
608
- applications but it will be removed.
0 commit comments