micropython-samples/uasyncio_iostream
Peter Hinch ee36d4525c uasyncio README.md note re awaitable classes. 2019-12-04 10:54:36 +00:00
..
poll Library installation README added, code updated. 2019-03-26 09:59:13 +00:00
primitives Address CPython compatibility. 2019-11-21 11:29:16 +00:00
tests uasyncio - add ms_timer demo of fast I/O. 2019-11-16 11:36:22 +00:00
uasyncio lock.py: fix bug with _awt 2019-12-02 18:22:14 +00:00
README.md uasyncio README.md note re awaitable classes. 2019-12-04 10:54:36 +00:00
moduselect.c Add temporary dir uasyncio_iostream. 2018-06-10 11:24:08 +01:00
ms_timer.py uasyncio: implement as package, add primitives. 2019-11-20 11:18:38 +00:00
ms_timer_test.py uasyncio: implement as package, add primitives. 2019-11-20 11:18:38 +00:00
prim_test.py Simplify queue.py, prim_test.py tests queue cancellation. 2019-12-02 08:22:52 +00:00
test_fast_scheduling.py Add test for fast scheduling. 2019-11-16 06:28:23 +00:00

README.md

Changes to usayncio

This archive contains suggestions for changes to new uasyncio. Item 3 below added 2 Dec, task queue name reverted to _queue as this can now be private.

  1. Implement as a Python package.
  2. Implement synchronisation primitives as package modules to conserve RAM.
  3. Primitive class has methods common to most synchronisation primitives. Avoids the need for primitives to access the task queue directly.
  4. Add .priority method to Stream class. Enables I/O to be handled at high priority on a per-device basis.
  5. Rename task queue class TQueue to avoid name clash with Queue primitive.

Minor changes

  1. Move StreamReader and StreamWriter assignments out of legacy section of code: these classes exist in asyncio 3.8.
  2. .CreateTask produces an assertion fail if called with a generator function. Avoids obscure traceback if someone omits the parens.
  3. Add machine readable version info. Useful in testing.

CPython-compatible synchronisation primitives

These aim to work efficiently with the new version. All are separate modules to conserve RAM. Items 1-4 use classes based on uasyncio.Primitive.

  1. Event: Moved to separate module.
  2. Lock: Kevin Köck's solution.
  3. Queue: Paul's solution adapted for efficiency.
  4. Semaphore: Also implements BoundedSemaphore.
  5. Condition.

Other primitives

Included as examples of user-contributed primitives - see final section.

  1. Message: Awaitable Event subclass with a data payload.
  2. Barrier: Multiple tasks wait until all are either waiting on a Barrier instance or have triggered the instance without waiting. Similar to gather without the controlling coro: a barrier is shared between peers and may be used in loops.

Test scripts

Hopefully these are self-documenting on import.

  1. prim_test.py Tests for synchronisation primitives.
  2. test_fast_scheduling.py Demonstrates difference between normal and priority I/O scheduling. Runs on Pyboard.
  3. ms_timer.py and ms_timer_test.py A practical use of priority scheduling to implement a timer with higher precision than asyncio.sleep_ms. Runs on Pyboard.

CPython compatibility

prim_test.py runs on MicroPython or CPython 3.8, demonstrating that MicroPython primitives behave similarly to the native CPython ones.

Message is common to CPython and MicroPython. There are two implementations of Barrier with the same functionality: a CPython version and a MicroPython version with specific optimisations. The Barrier class is loosely based on a Microsoft concept.

Directory structure

MicroPython optimised primitives are in uasyncio/. Primitives compatible with asyncio are in primitives/.

Future uasyncio implementations

If part of uasyncio is to be implemented in C, it would be good if the following capabilities were retained to facilitate writing efficient add-on modules along the lines of the Message and Barrier classes:

  1. The ability to subclass the asyncio compatible primitives.
  2. The ability to subclass uasyncio.Primitive (if you implement it).
  3. Some means of creating awaitable classes (e.g. __iter__).

The mechanism for doing these things might change, but it would be a shame to lose the capability.

Suggestion

Implement wait_for_ms as per V2.

Awaitable classes

I reviewed the code samples in my tutorial: with minor changes to remove event loop methods they will run under CPython 3.8 and the new uasyncio without version-specific code. The one exception remains awaitable classes. The Foo class works under both versions but isn't pretty. I guess this is a minor problem: a similar hack is required for V2 and nobody has complained.

up = False  # Running under MicroPython?
try:
    import uasyncio as asyncio
    up = True  # Or can use sys.implementation.name
except ImportError:
    import asyncio

async def times_two(n):  # Coro to await
    await asyncio.sleep(1)
    return 2 * n

class Foo():
    def __await__(self):
        res = 1
        for n in range(5):
            print('__await__ called')
            if up:  # MicroPython
                res = yield from times_two(res)
            else:  # CPython
                res = yield from times_two(res).__await__()
        return res

    __iter__ = __await__  # MicroPython compatibility

async def bar():
    foo = Foo()  # foo is awaitable
    print('waiting for foo')
    res = await foo  # Retrieve value
    print('done', res)

asyncio.run(bar())