# Non blocking file reader for python asyncio · GitHub Gist ## Metadata | Field | Value | |---|---| | Institute | Molodetz Institute — Research & Retrieval Division | | Subject | async-pty-process-testing | | Document type | Source Dossier | | Tier | 1 | | Source title | Non blocking file reader for python asyncio | | Source URL | https://gist.github.com/nilp0inter/5f400b6afb6df7c0b553 | | Source publisher / author | GitHub user nilp0inter | | Source type | repository (gist) | | Source date (published) | Unknown — not stated | | Date researched | 2026-10-09 | | Researched by | tai (agent), model: Claude Sonnet 5, on behalf of retoor | | Research method | web search synthesis | | Confidence | High | | Status | Active | ## Summary A GitHub gist demonstrating the correct way to read stdin (or any fd) without blocking the asyncio event loop: use `loop.add_reader(fd, callback)` where the callback does a raw `os.read(fd, n)`, never a call through Python's buffered `sys.stdin` object, which blocks regardless of `add_reader`. ## Key findings - Calling `sys.stdin.read()` inside an `add_reader` callback still blocks, because Python's buffered stdin object waits for more data internally — `add_reader` only tells the event loop the fd is readable, it does not change how a buffered read call behaves. - The correct pattern is `os.read(fileno, n)` on the raw file descriptor inside the callback. - On EOF (an empty read), the callback should call `loop.remove_reader(fileno)` to stop watching the fd. ## Pros / strengths - Identifies a genuine, easy-to-make mistake (buffered read inside a non-blocking callback) with a precise fix, backed by a reported working example. - The pattern is minimal and requires no third-party dependency — pure stdlib (`os`, `asyncio`). ## Cons / limitations / caveats - A gist, not an authoritative reference — no discussion of edge cases like partial reads mid-line or how this interacts with terminal raw/cooked mode. - Only covers the read side; doesn't address writing back to the child or coordinating with a separate output stream. ## Relevance to tai This is the exact pattern `tai_test.py`'s `StdinBridge._on_readable()` implements: `os.read(self._fd, 4096)` inside an `add_reader` callback, buffering until a newline, and calling `self.stop()` (which internally calls `loop.remove_reader`) on an empty read. Using `sys.stdin.read()` instead would have silently reintroduced blocking behavior that defeats the entire point of the non-blocking design. ## Changelog | Date | Change | |---|---| | 2026-10-09 | Initial dossier created. |