Skip to content

Commit a5c91c4

Browse files
committed
Update streampair to use RingIO and add async compatibility tests
- Replace deprecated 'ringbuffer' with 'RingIO' name - Remove unused 'collections.deque' import - Add test_select_poll_compatibility() to verify select.poll() works - Add test_streamreader_direct_usage() to verify asyncio.StreamReader compatibility The existing ioctl implementation already correctly returns -1 for unsupported operations, which enables select.poll() to work with the StreamPair Python object. This in turn allows asyncio.StreamReader to work since it's built on top of select.poll(). These tests demonstrate that pure Python stream objects with proper ioctl() implementation can work seamlessly with both select.poll() and asyncio without needing C-level integration.
1 parent 9e7db99 commit a5c91c4

File tree

2 files changed

+55
-5
lines changed

2 files changed

+55
-5
lines changed

micropython/streampair/streampair.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import io
22

3-
from collections import deque
4-
from micropython import ringbuffer, const
3+
from micropython import RingIO, const
54

65
try:
76
from typing import Union, Tuple
@@ -28,14 +27,14 @@ def streampair(buffer_size: Union[int, Tuple[int, int]]=256):
2827
except TypeError:
2928
size_a = size_b = buffer_size
3029

31-
a = ringbuffer(size_a)
32-
b = ringbuffer(size_b)
30+
a = RingIO(size_a)
31+
b = RingIO(size_b)
3332
return StreamPair(a, b), StreamPair(b, a)
3433

3534

3635
class StreamPair(io.IOBase):
3736

38-
def __init__(self, own: ringbuffer, other: ringbuffer):
37+
def __init__(self, own: RingIO, other: RingIO):
3938
self.own = own
4039
self.other = other
4140
super().__init__()

micropython/streampair/test_streampair.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import asyncio
22
import unittest
3+
import select
34
from streampair import streampair
45

56
def async_test(f):
@@ -49,6 +50,56 @@ async def test_async_streampair(self):
4950
assert not b.any()
5051
assert not a.any()
5152

53+
def test_select_poll_compatibility(self):
54+
"""Test that streampair works with select.poll()"""
55+
a, b = streampair()
56+
57+
# Register stream with poll
58+
poller = select.poll()
59+
poller.register(a, select.POLLIN)
60+
61+
# No data available initially
62+
events = poller.poll(0)
63+
assert len(events) == 0, f"Expected no events, got {events}"
64+
65+
# Write data to b, should be readable from a
66+
b.write(b"test data")
67+
68+
# Should now poll as readable
69+
events = poller.poll(0)
70+
assert len(events) == 1, f"Expected 1 event, got {events}"
71+
assert events[0][0] == a, "Event should be for stream a"
72+
assert events[0][1] & select.POLLIN, "Should be readable"
73+
74+
# Read the data
75+
data = a.read()
76+
assert data == b"test data", f"Expected b'test data', got {data}"
77+
78+
# Should no longer poll as readable
79+
events = poller.poll(0)
80+
assert len(events) == 0, f"Expected no events after read, got {events}"
81+
82+
poller.unregister(a)
83+
84+
@async_test
85+
async def test_streamreader_direct_usage(self):
86+
"""Test that streampair can be used directly with asyncio.StreamReader"""
87+
a, b = streampair()
88+
89+
# Create StreamReader directly on the streampair object
90+
reader = asyncio.StreamReader(a)
91+
92+
# Write data in background task
93+
async def write_delayed():
94+
await asyncio.sleep_ms(10)
95+
b.write(b"async test\n")
96+
97+
asyncio.create_task(write_delayed())
98+
99+
# Should be able to read via StreamReader
100+
data = await asyncio.wait_for(reader.readline(), 1.0)
101+
assert data == b"async test\n", f"Expected b'async test\\n', got {data}"
102+
52103

53104
if __name__ == "__main__":
54105
unittest.main()

0 commit comments

Comments
 (0)