- commit
- fc000f4a6a23c0aa964652959f2296015a985d76
- parent
- 62f05c12a608b69bda7bb5eff30a0be859df96a4
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2026-02-26 22:16
add futures If multiple coroutines are waiting for a message on the same socket, it can be helpful to have a mechanism to wake up the right one. Futures are a pure python abstraction that does exactly that.
Diffstat
| M | tests.py | 25 | +++++++++++++++++++++++++ |
| M | xiio.py | 27 | ++++++++++++++++++++++++++- |
2 files changed, 51 insertions, 1 deletions
diff --git a/tests.py b/tests.py
@@ -1,4 +1,6 @@ 1 1 import contextlib -1 2 import functools -1 3 import inspect 2 4 import time 3 5 import unittest 4 6 from unittest import mock @@ -7,6 +9,15 @@ import xiio 7 9 8 10 9 11 class XiioTestCase(unittest.TestCase): -1 12 def _callTestMethod(self, method): # noqa -1 13 if not inspect.iscoroutinefunction(method): -1 14 return super()._callTestMethod(method) -1 15 -1 16 @functools.wraps(method) -1 17 def wrapper(*args, **kwargs): -1 18 xiio.run(method(*args, **kwargs)) -1 19 return super()._callTestMethod(wrapper) -1 20 10 21 @contextlib.contextmanager 11 22 def assert_duration(self, expected, *, places=2): 12 23 start = time.monotonic() @@ -17,6 +28,20 @@ class XiioTestCase(unittest.TestCase): 17 28 self.assertAlmostEqual(actual, expected, places=places) 18 29 19 30 -1 31 class TestFuture(XiioTestCase): -1 32 async def test_set_result(self): -1 33 future = xiio.Future() -1 34 future.set_result('test') -1 35 result = await future -1 36 self.assertEqual(result, 'test') -1 37 -1 38 async def test_set_exception(self): -1 39 future = xiio.Future() -1 40 future.set_exception(TypeError) -1 41 with self.assertRaises(TypeError): -1 42 await future -1 43 -1 44 20 45 class TestRun(XiioTestCase): 21 46 def test_sleep(self): 22 47 async def foo():
diff --git a/xiio.py b/xiio.py
@@ -7,8 +7,9 @@ from selectors import EVENT_WRITE as WRITE 7 7 8 8 9 9 class Condition:10 -1 def __init__(self, *, files={}, time=math.inf):-1 10 def __init__(self, *, files={}, futures=set(), time=math.inf): 11 11 self.files = files or {} -1 12 self.futures = futures or set() 12 13 self.time = time 13 14 14 15 def __await__(self): @@ -16,6 +17,8 @@ class Condition: 16 17 17 18 def select(self): 18 19 timeout = self.time - time.monotonic() -1 20 if any(future.done for future in self.futures): -1 21 timeout = 0 19 22 with selectors.DefaultSelector() as sel: 20 23 for fileno, events in self.files.items(): 21 24 sel.register(fileno, events) @@ -44,6 +47,28 @@ async def writeall(file, data): 44 47 data = data[size:] 45 48 46 49 -1 50 class Future: -1 51 def __init__(self): -1 52 self.result = None -1 53 self.exc = None -1 54 self.done = False -1 55 -1 56 def set_result(self, value): -1 57 self.result = value -1 58 self.done = True -1 59 -1 60 def set_exception(self, exc): -1 61 self.exc = exc -1 62 self.done = True -1 63 -1 64 def __await__(self): -1 65 yield Condition(futures={self}) -1 66 if self.exc: -1 67 raise self.exc -1 68 else: -1 69 return self.result -1 70 -1 71 47 72 def run(coro): 48 73 gen = coro.__await__() 49 74 try: