From 9622a00ea1888de54939be6d9c917268de82e8f8 Mon Sep 17 00:00:00 2001 From: Smittix Date: Thu, 26 Feb 2026 10:00:38 +0000 Subject: [PATCH] Fix Morse reader to bypass BufferedReader via os.read on raw fd BufferedReader.read(n) on non-interactive streams (Python 3.14) blocks until the full n bytes accumulate, starving the decoder of real-time PCM data. Use os.read() on the raw file descriptor instead, which returns as soon as any data is available. Falls back to .read() for file-like objects without fileno() (e.g. BytesIO in tests). Co-Authored-By: Claude Opus 4.6 --- utils/morse.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/utils/morse.py b/utils/morse.py index 51d9c31..509a73a 100644 --- a/utils/morse.py +++ b/utils/morse.py @@ -7,6 +7,7 @@ from __future__ import annotations import contextlib import math +import os import queue import struct import threading @@ -258,10 +259,22 @@ class MorseDecoder: def _stdout_reader(stdout, data_queue: queue.Queue, stop_event: threading.Event) -> None: - """Blocking reader — pushes raw PCM chunks to queue, None on EOF.""" + """Blocking reader — pushes raw PCM chunks to queue, None on EOF. + + Uses os.read() on the raw fd when available to bypass BufferedReader, + which on Python 3.14 may block trying to fill its entire buffer before + returning. Falls back to .read() for objects without fileno() (tests). + """ + try: + fd = stdout.fileno() + except Exception: + fd = None try: while not stop_event.is_set(): - data = stdout.read(4096) + if fd is not None: + data = os.read(fd, 4096) + else: + data = stdout.read(4096) if not data: break data_queue.put(data)