8000 extmod: add new implementation of uasyncio by dpgeorge · Pull Request #5332 · micropython/micropython · GitHub
[go: up one dir, main page]

Skip to content

extmod: add new implementation of uasyncio #5332

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Mar 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f8fc786
py/mpconfig.h: Enable MICROPY_MODULE_GETATTR by default.
dpgeorge Feb 29, 2020
ab00f4c
qemu-arm: Set default board as mps2-an385 to get more flash for tests.
dpgeorge Mar 2, 2020
98ab764
travis: Print errors out for OSX job.
dpgeorge Mar 2, 2020
c47a3dd
py/pairheap: Properly unlink node on pop and delete.
dpgeorge Mar 13, 2020
6c7e78d
py/pairheap: Add helper function to initialise a new node.
dpgeorge Mar 16, 2020
f9741d1
unix/coverage: Init all pairheap test nodes before using them.
dpgeorge Mar 13, 2020
f05ae41
stm32/softtimer: Initialise pairing-heap node before pushing to heap.
dpgeorge Mar 16, 2020
63b9944
extmod/uasyncio: Add new implementation of uasyncio module.
dpgeorge Nov 13, 2019
c4935f3
tests/extmod: Add uasyncio tests.
dpgeorge Nov 13, 2019
5d09a40
tests/run-tests: Skip uasyncio if no async, and skip one test on native.
dpgeorge Jan 23, 2020
3667eff
travis: Exclude some uasyncio tests on OSX.
dpgeorge Mar 3, 2020
18fa65e
tests: Make default MICROPYPATH include extmod to find uasyncio.
dpgeorge Mar 9, 2020
38904b8
tests/multi_net: Add uasyncio test for TCP server and client.
dpgeorge Mar 9, 2020
081d067
tests/net_inet: Add uasyncio internet tests.
dpgeorge Mar 12, 2020
bc009fd
extmod/uasyncio: Add optional implementation of core uasyncio in C.
dpgeorge Mar 12, 2020
91dd394
unix: Enable uasyncio C helper module on coverage build.
dpgeorge Mar 12, 2020
c99322f
docs/library: Add initial docs for uasyncio module.
dpgeorge Mar 20, 2020
3b68f36
extmod/uasyncio: Add manifest.py for freezing uasyncio Py files.
dpgeorge Mar 21, 2020
35e2dd0
stm32: Enable and freeze uasyncio.
dpgeorge Mar 21, 2020
1d4d688
esp8266: Enable and freeze uasyncio.
dpgeorge Mar 22, 2020
ad004db
esp32: Enable and freeze uasyncio.
dpgeorge Mar 22, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ jobs:

# qemu-arm port
- stage: test
dist: bionic # needed for more recent version of qemu-system-arm with mps2-an385 target
env: NAME="qemu-arm port build and tests"
install:
- sudo apt-get install gcc-arm-none-eabi
Expand Down Expand Up @@ -212,7 +213,10 @@ jobs:
- make ${MAKEOPTS} -C ports/unix submodules
- make ${MAKEOPTS} -C ports/unix deplibs
- make ${MAKEOPTS} -C ports/unix
- make ${MAKEOPTS} -C ports/unix test
# OSX has poor time resolution and the following tests do not have the correct output
- (cd tests && ./run-tests --exclude 'uasyncio_(basic|heaplock|lock|wait_task)')
after_failure:
- (cd tests && for exp in *.exp; do testbase=$(basename $exp .exp); echo -e "\nFAILURE $testbase"; diff -u $testbase.exp $testbase.out; done)

# windows port via mingw
- stage: test
Expand Down
1 change: 1 addition & 0 deletions docs/library/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ it will fallback to loading the built-in ``ujson`` module.
math.rst
sys.rst
uarray.rst
uasyncio.rst
ubinascii.rst
ucollections.rst
uerrno.rst
Expand Down
263 changes: 263 additions & 0 deletions docs/library/uasyncio.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
:mod:`uasyncio` --- asynchronous I/O scheduler
==============================================

.. module:: uasyncio
:synopsis: asynchronous I/O scheduler for writing concurrent code

|see_cpython_module|
`asyncio <https://docs.python.org/3.8/library/asyncio.html>`_

Example::

import uasyncio

async def blink(led, period_ms):
while True:
led.on()
await uasyncio.sleep_ms(5)
led.off()
await uasyncio.sleep_ms(period_ms)

async def main(led1, led2):
uasyncio.create_task(blink(led1, 700))
uasyncio.create_task(blink(led2, 400))
await uasyncio.sleep_ms(10_000)

# Running on a pyboard
from pyb import LED
uasyncio.run(main(LED(1), LED(2)))

# Running on a generic board
from machine import Pin
uasyncio.run(main(Pin(1), Pin(2)))

Core functions
--------------

.. function:: create_task(coro)

Create a new task from the given coroutine and schedule it to run.

Returns the corresponding `Task` object.

.. function:: run(coro)

Create a new task from the given coroutine and run it until it completes.

Returns the value returned by *coro*.

.. function:: sleep(t)

Sleep for *t* seconds (can be a float).

This is a coroutine.

.. function:: sleep_ms(t)

Sleep for *t* milliseconds.

This is a coroutine, and a MicroPython extension.

Additional functions
--------------------

.. function:: wait_for(awaitable, timeout)

Wait for the *awaitable* to complete, but cancel it if it takes longer
that *timeout* seconds. If *awaitable* is not a task then a task will be
created from it.

Returns the return value of *awaitable*.

This is a coroutine.

.. function:: gather(\*awaitables, return_exceptions=False)

Run all *awaitables* concurrently. Any *awaitables* that are not tasks are
promoted to tasks.

Returns a list of return values of all *awaitables*.

This is a coroutine.

class Task
----------

.. class:: Task()

This object wraps a coroutine into a running task. Tasks can be waited on
using ``await task``, which will wait for the task to complete and return
the return value of the task.

Tasks should not be created directly, rather use `create_task` to create them.

.. method:: Task.cancel()

Cancel the task by injecting a ``CancelledError`` into it. The task may
or may not ignore this exception.

class Event
-----------

.. class:: Event()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason you omitted is_set() ?
I'm using it to avoid task creation if the event has already been set in a construct like this:

if not ev.is_set():
    await asyncio.wait_for(ev.wait(), timeout)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've now implemented is_set(), to mirror the fact Lock.locked() exists


Create a new event which can be used to synchronise tasks. Events start
in the cleared state.

.. method:: Event.is_set()

Returns ``True`` if the event is set, ``False`` otherwise.

.. method:: Event.set()

Set the event. Any tasks waiting on the event will be scheduled to run.

.. method:: Event.clear()

Clear the event.

.. method:: Event.wait()

Wait for the event to be set. If the event is already set then it returns
immediately.

This is a coroutine.

class Lock
----------

.. class:: Lock()

Create a new lock which can be used to coordinate tasks. Locks start in
the unlocked state.

In addition to the methods below, locks can be used in an ``async with`` statement.

.. method:: Lock.locked()

Returns ``True`` if the lock is locked, otherwise ``False``.

.. method:: Lock.acquire()

Wait for the lock to be in the unlocked state and then lock it in an atomic
way. Only one task can acquire the lock at any one time.

This is a coroutine.

.. method:: Lock.release()

Release the lock. If any tasks are waiting on the lock then the next one in the
queue is scheduled to run and the lock remains locked. Otherwise, no tasks are
waiting an the lock becomes unlocked.

TCP stream connections
----------------------

.. function:: open_connection(host, port)

Open a TCP connection to the given *host* and *port*. The *host* address will be
resolved using `socket.getaddrinfo`, which is currently a blocking call.

Returns a pair of streams: a reader and a writer stream.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it return errors or raise?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It raises. I've added bits to the docs to note this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/raise an/raise a/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, fixed

Will raise a socket-specific ``OSError`` if the host could not be resolved or if
the connection could not be made.

This is a coroutine.

.. function:: start_server(callback, host, port, backlog=5)

Start a TCP server on the given *host* and *port*. The *callback* will be
called with incoming, accepted connections, and be passed 2 arguments: reader
and writer streams for the connection.

Returns a `Server` object.

This is a coroutine.

.. class:: Stream()

This represents a TCP stream connection. To minimise code this class implements
both a reader and a writer.

.. method:: Stream.get_extra_info(v)

Get extra information about the stream, given by *v*. The valid values for *v* are:
``peername``.

.. method:: Stream.close()

Close the stream.

.. method:: Stream.wait_closed()

Wait for the stream to close.

This is a coroutine.

.. method:: Stream.read(n)

Read up to *n* bytes and return them.

This is a coroutine.

.. method:: Stream.readline()

Read a line and return it.

This is a coroutine.

.. method:: Stream.write(buf)

Accumulated *buf* to the output buffer. The data is only flushed when
`Stream.drain` is called. It is recommended to call `Stream.drain` immediately
after calling this function.

.. method:: Stream.drain()

Drain (write) all buffered output data out to the stream.

This is a coroutine.

.. class:: Server()

This represents the server class returned from `start_server`. It can be used
in an ``async with`` statement to close the server upon exit.

.. method:: Server.close()

Close the server.

.. method:: Server.wait_closed()

Wait for the server to close.

This is a coroutine.

Event Loop
----------

.. function:: get_event_loop()

Return the event loop used to schedule and run tasks. See `Loop`.

.. class:: Loop()

This represents the object which schedules and runs tasks. It cannot be
created, use `get_event_loop` instead.

.. method:: Loop.create_task(coro)

Create a task from the given *coro* and return the new `Task` object.

.. method:: Loop.run_forever()

Run the event loop forever.

.. method:: Loop.run_until_complete(awaitable)

Run the given *awaitable* until it completes. If *awaitable* is not a task
then it will be promoted to one.

.. method:: Loop.close()

Close the event loop.
Loading
0