From 6ad3001a4c7a251ca024bcc4f8a74279b596ee69 Mon Sep 17 00:00:00 2001 From: Phil Underwood Date: Wed, 2 Nov 2022 00:02:07 +0000 Subject: [PATCH 1/3] Switch `await core.sleep` to `yield` --- asyncio/stream.py | 11 ++++++----- examples/usb_cdc_boot.py | 2 ++ examples/usb_cdc_example.py | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 examples/usb_cdc_boot.py create mode 100644 examples/usb_cdc_example.py diff --git a/asyncio/stream.py b/asyncio/stream.py index 97dcf6a..2767439 100644 --- a/asyncio/stream.py +++ b/asyncio/stream.py @@ -61,7 +61,7 @@ async def read(self, n): """ core._io_queue.queue_read(self.s) - await core.sleep(0) + yield return self.s.read(n) async def readinto(self, buf): @@ -73,7 +73,7 @@ async def readinto(self, buf): """ core._io_queue.queue_read(self.s) - await core.sleep(0) + yield return self.s.readinto(buf) async def readexactly(self, n): @@ -88,7 +88,7 @@ async def readexactly(self, n): r = b"" while n: core._io_queue.queue_read(self.s) - await core.sleep(0) + yield r2 = self.s.read(n) if r2 is not None: if not len(r2): @@ -106,7 +106,7 @@ async def readline(self): l = b"" while True: core._io_queue.queue_read(self.s) - await core.sleep(0) + yield l2 = self.s.readline() # may do multiple reads but won't block l += l2 if not l2 or l[-1] == 10: # \n (check l in case l2 is str) @@ -129,7 +129,8 @@ async def drain(self): mv = memoryview(self.out_buf) off = 0 while off < len(mv): - yield core._io_queue.queue_write(self.s) + core._io_queue.queue_write(self.s) + yield ret = self.s.write(mv[off:]) if ret is not None: off += ret diff --git a/examples/usb_cdc_boot.py b/examples/usb_cdc_boot.py new file mode 100644 index 0000000..411165e --- /dev/null +++ b/examples/usb_cdc_boot.py @@ -0,0 +1,2 @@ +import usb_cdc +usb_cdc.enable(data=True, console=True) \ No newline at end of file diff --git a/examples/usb_cdc_example.py b/examples/usb_cdc_example.py new file mode 100644 index 0000000..041441a --- /dev/null +++ b/examples/usb_cdc_example.py @@ -0,0 +1,34 @@ +""" +example that reads from the cdc data serial port in groups of four and prints +to the console. The USB CDC data serial port will need enabling. This can be done +by copying examples/usb_cdc_boot.py to boot.py in the CIRCUITPY directory + +Meanwhile a simple counter counts up every second and also prints +to the console. +""" + + +import asyncio +import usb_cdc + +async def client(): + usb_cdc.data.timeout=0 + s = asyncio.StreamReader(usb_cdc.data) + while True: + text = await s.readline() + print(text) + await asyncio.sleep(0) + +async def counter(): + i = 0 + while True: + print(i) + i += 1 + await asyncio.sleep(1) + +async def main(): + client_task = asyncio.create_task(client()) + count_task = asyncio.create_task(counter()) + await asyncio.gather(client_task, count_task) + +asyncio.run(main()) From fb068e2f1bbea264dd4a69a3bb973561509d738f Mon Sep 17 00:00:00 2001 From: Phil Underwood Date: Thu, 3 Nov 2022 16:25:36 +0000 Subject: [PATCH 2/3] Convert IOQueue.read|write to coroutines that await Never before returning. Also update streams.py to reflect this and provide example code for using UART, USB_CDC and BLE --- asyncio/core.py | 8 +++- asyncio/stream.py | 20 ++++----- examples/serial_examples.py | 81 +++++++++++++++++++++++++++++++++++++ examples/usb_cdc_boot.py | 9 ++++- examples/usb_cdc_example.py | 34 ---------------- 5 files changed, 102 insertions(+), 50 deletions(-) create mode 100644 examples/serial_examples.py delete mode 100644 examples/usb_cdc_example.py diff --git a/asyncio/core.py b/asyncio/core.py index dcd4b24..149b46f 100644 --- a/asyncio/core.py +++ b/asyncio/core.py @@ -148,11 +148,15 @@ def _dequeue(self, s): del self.map[id(s)] self.poller.unregister(s) - def queue_read(self, s): + async def queue_read(self, s): self._enqueue(s, 0) + _never.state = False + await _never - def queue_write(self, s): + async def queue_write(self, s): self._enqueue(s, 1) + _never.state = False + await _never def remove(self, task): while True: diff --git a/asyncio/stream.py b/asyncio/stream.py index 2767439..7bf32cb 100644 --- a/asyncio/stream.py +++ b/asyncio/stream.py @@ -60,8 +60,7 @@ async def read(self, n): This is a coroutine. """ - core._io_queue.queue_read(self.s) - yield + await core._io_queue.queue_read(self.s) return self.s.read(n) async def readinto(self, buf): @@ -72,8 +71,7 @@ async def readinto(self, buf): This is a coroutine, and a MicroPython extension. """ - core._io_queue.queue_read(self.s) - yield + await core._io_queue.queue_read(self.s) return self.s.readinto(buf) async def readexactly(self, n): @@ -87,8 +85,7 @@ async def readexactly(self, n): r = b"" while n: - core._io_queue.queue_read(self.s) - yield + await core._io_queue.queue_read(self.s) r2 = self.s.read(n) if r2 is not None: if not len(r2): @@ -105,8 +102,7 @@ async def readline(self): l = b"" while True: - core._io_queue.queue_read(self.s) - yield + await core._io_queue.queue_read(self.s) l2 = self.s.readline() # may do multiple reads but won't block l += l2 if not l2 or l[-1] == 10: # \n (check l in case l2 is str) @@ -129,8 +125,7 @@ async def drain(self): mv = memoryview(self.out_buf) off = 0 while off < len(mv): - core._io_queue.queue_write(self.s) - yield + await core._io_queue.queue_write(self.s) ret = self.s.write(mv[off:]) if ret is not None: off += ret @@ -167,8 +162,7 @@ async def open_connection(host, port): except OSError as er: if er.errno != EINPROGRESS: raise er - core._io_queue.queue_write(s) - await core.sleep(0) + await core._io_queue.queue_write(s) return ss, ss @@ -202,7 +196,7 @@ async def _serve(self, s, cb): # Accept incoming connections while True: try: - yield core._io_queue.queue_read(s) + await core._io_queue.queue_read(s) except core.CancelledError: # Shutdown server s.close() diff --git a/examples/serial_examples.py b/examples/serial_examples.py new file mode 100644 index 0000000..8e8648e --- /dev/null +++ b/examples/serial_examples.py @@ -0,0 +1,81 @@ +# SPDX-FileCopyrightText: 2022 Phil Underwood +# +# SPDX-License-Identifier: Unlicense +""" +example that reads from the cdc data serial port in groups of four and prints +to the console. The USB CDC data serial port will need enabling. This can be done +by copying examples/usb_cdc_boot.py to boot.py in the CIRCUITPY directory + +Meanwhile a simple counter counts up every second and also prints +to the console. +""" + + +import asyncio + +USE_USB = True +USE_UART = True +USE_BLE = True + + +if USE_USB: + import usb_cdc + + async def usb_client(): + + usb_cdc.data.timeout = 0 + s = asyncio.StreamReader(usb_cdc.data) + while True: + text = await s.readline() + print("USB: ", text) + + +if USE_UART: + import board + + async def uart_client(): + + uart = board.UART() + uart.timeout = 0 + s = asyncio.StreamReader(board.UART()) + while True: + text = await s.readexactly(4) + print("UART: ", text) + + +if USE_BLE: + from adafruit_ble import BLERadio + from adafruit_ble.advertising.standard import ProvideServicesAdvertisement + from adafruit_ble.services.nordic import UARTService + + async def ble_client(): + ble = BLERadio() + uart = UARTService() + advertisement = ProvideServicesAdvertisement(uart) + ble.start_advertising(advertisement) + s = asyncio.StreamReader(uart._rx) # pylint: disable=protected-access + while True: + text = await s.read(6) + print("BLE: ", text) + + +async def counter(): + i = 0 + while True: + print(i) + i += 1 + await asyncio.sleep(1) + + +async def main(): + clients = [asyncio.create_task(counter())] + if USE_USB: + clients.append(asyncio.create_task(usb_client())) + if USE_UART: + clients.append(asyncio.create_task(uart_client())) + if USE_BLE: + clients.append(asyncio.create_task(ble_client())) + await asyncio.gather(*clients) + + +asyncio.run(main()) diff --git a/examples/usb_cdc_boot.py b/examples/usb_cdc_boot.py index 411165e..6cd5593 100644 --- a/examples/usb_cdc_boot.py +++ b/examples/usb_cdc_boot.py @@ -1,2 +1,9 @@ +# SPDX-FileCopyrightText: 2022 Phil Underwood +# +# SPDX-License-Identifier: Unlicense +""" +Save this file as boot.py on CIRCUITPY to enable the usb_cdc.data serial device +""" import usb_cdc -usb_cdc.enable(data=True, console=True) \ No newline at end of file + +usb_cdc.enable(data=True, console=True) diff --git a/examples/usb_cdc_example.py b/examples/usb_cdc_example.py deleted file mode 100644 index 041441a..0000000 --- a/examples/usb_cdc_example.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -example that reads from the cdc data serial port in groups of four and prints -to the console. The USB CDC data serial port will need enabling. This can be done -by copying examples/usb_cdc_boot.py to boot.py in the CIRCUITPY directory - -Meanwhile a simple counter counts up every second and also prints -to the console. -""" - - -import asyncio -import usb_cdc - -async def client(): - usb_cdc.data.timeout=0 - s = asyncio.StreamReader(usb_cdc.data) - while True: - text = await s.readline() - print(text) - await asyncio.sleep(0) - -async def counter(): - i = 0 - while True: - print(i) - i += 1 - await asyncio.sleep(1) - -async def main(): - client_task = asyncio.create_task(client()) - count_task = asyncio.create_task(counter()) - await asyncio.gather(client_task, count_task) - -asyncio.run(main()) From 5b516cd43461f3524cc14324cb15fe352aa91674 Mon Sep 17 00:00:00 2001 From: Phil Underwood Date: Thu, 3 Nov 2022 17:03:05 +0000 Subject: [PATCH 3/3] Fix failing pylint on python 3.11 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3343606..003c85d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,7 +18,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/pycqa/pylint - rev: v2.11.1 + rev: v2.13.9 hooks: - id: pylint name: pylint (library code)