xiio

really simple async runtime
git clone https://git.ce9e.org/xiio.git

commit
3261d16114cc24b5d12975c008a3a87cf7f98b7c
parent
fc000f4a6a23c0aa964652959f2296015a985d76
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2026-02-26 21:25
add type annotations

Diffstat

M xiio.py 50 ++++++++++++++++++++++++++++++++------------------

1 files changed, 32 insertions, 18 deletions


diff --git a/xiio.py b/xiio.py

@@ -2,20 +2,34 @@ import math
    2     2 import os
    3     3 import selectors
    4     4 import time
   -1     5 import typing
   -1     6 from collections.abc import Coroutine
   -1     7 from collections.abc import Generator
    5     8 from selectors import EVENT_READ as READ
    6     9 from selectors import EVENT_WRITE as WRITE
    7    10 
   -1    11 T = typing.TypeVar('T')
   -1    12 Files = dict[int, int]
   -1    13 Gen = Generator['Condition', None, T]
   -1    14 Coro = Coroutine['Condition', None, T]
   -1    15 
    8    16 
    9    17 class Condition:
   10    -1     def __init__(self, *, files={}, futures=set(), time=math.inf):
   -1    18     def __init__(
   -1    19         self,
   -1    20         *,
   -1    21         files: Files = {},
   -1    22         futures: set['Future[typing.Any]'] = set(),
   -1    23         time: float = math.inf,
   -1    24     ):
   11    25         self.files = files or {}
   12    26         self.futures = futures or set()
   13    27         self.time = time
   14    28 
   15    -1     def __await__(self):
   -1    29     def __await__(self) -> Gen[None]:
   16    30         yield self
   17    31 
   18    -1     def select(self):
   -1    32     def select(self) -> None:
   19    33         timeout = self.time - time.monotonic()
   20    34         if any(future.done for future in self.futures):
   21    35             timeout = 0
@@ -25,51 +39,51 @@ class Condition:
   25    39             sel.select(None if timeout == math.inf else timeout)
   26    40 
   27    41 
   28    -1 async def sleep(seconds):
   -1    42 async def sleep(seconds: float) -> None:
   29    43     await Condition(time=time.monotonic() + seconds)
   30    44 
   31    45 
   32    -1 async def read(file, size):
   -1    46 async def read(file, size: int) -> bytes:
   33    47     fileno = file if isinstance(file, int) else file.fileno()
   34    48     await Condition(files={fileno: READ})
   35    49     return os.read(fileno, size)
   36    50 
   37    51 
   38    -1 async def write(file, data):
   -1    52 async def write(file, data: bytes) -> int:
   39    53     fileno = file if isinstance(file, int) else file.fileno()
   40    54     await Condition(files={fileno: WRITE})
   41    55     return os.write(fileno, data)
   42    56 
   43    57 
   44    -1 async def writeall(file, data):
   -1    58 async def writeall(file, data: bytes) -> None:
   45    59     while data:
   46    60         size = await write(file, data)
   47    61         data = data[size:]
   48    62 
   49    63 
   50    -1 class Future:
   51    -1     def __init__(self):
   52    -1         self.result = None
   53    -1         self.exc = None
   54    -1         self.done = False
   -1    64 class Future(typing.Generic[T]):
   -1    65     def __init__(self) -> None:
   -1    66         self.result: T | None = None
   -1    67         self.exc: BaseException | None = None
   -1    68         self.done: bool = False
   55    69 
   56    -1     def set_result(self, value):
   -1    70     def set_result(self, value: T) -> None:
   57    71         self.result = value
   58    72         self.done = True
   59    73 
   60    -1     def set_exception(self, exc):
   -1    74     def set_exception(self, exc: BaseException) -> None:
   61    75         self.exc = exc
   62    76         self.done = True
   63    77 
   64    -1     def __await__(self):
   -1    78     def __await__(self) -> Gen[T]:
   65    79         yield Condition(futures={self})
   66    80         if self.exc:
   67    81             raise self.exc
   68    82         else:
   69    -1             return self.result
   -1    83             return typing.cast(T, self.result)
   70    84 
   71    85 
   72    -1 def run(coro):
   -1    86 def run(coro: Coro[T]) -> T:
   73    87     gen = coro.__await__()
   74    88     try:
   75    89         condition = next(gen)
@@ -81,4 +95,4 @@ def run(coro):
   81    95             else:
   82    96                 condition = next(gen)
   83    97     except StopIteration as e:
   84    -1         return e.value
   -1    98         return typing.cast(T, e.value)