refactor: Extract WaitingSpinner to its own module

This commit is contained in:
Paul Gauthier (aider) 2025-05-08 15:33:19 -07:00
parent 80f78ee85d
commit 21a05ead4e
2 changed files with 65 additions and 15 deletions

View file

@ -13,7 +13,7 @@ from rich.syntax import Syntax
from rich.text import Text
from aider.dump import dump # noqa: F401
from aider.utils import Spinner
from aider.waiting import WaitingSpinner
_text_prefix = """
# Header
@ -117,12 +117,11 @@ class MarkdownStream:
else:
self.mdargs = dict()
# Defer Live creation until the first update so the Spinner can be shown.
# Defer Live creation until the first update so the spinner can be shown.
self.live = None
self.spinner = Spinner("Waiting for LLM")
self._spinner_stop_event = threading.Event()
self._spinner_thread = threading.Thread(target=self._spin, daemon=True)
self._spinner_thread.start()
self.waiting_spinner = WaitingSpinner("Waiting for LLM")
self.waiting_spinner.start()
self._spinner_running = True
self._live_started = False
def _spin(self):
@ -159,12 +158,10 @@ class MarkdownStream:
except Exception:
pass # Ignore any errors during cleanup
# Ensure the spinner thread is properly shut down
if hasattr(self, "_spinner_stop_event"):
self._spinner_stop_event.set()
if hasattr(self, "_spinner_thread") and self._spinner_thread.is_alive():
# Ensure the spinner is properly shut down
if getattr(self, "_spinner_running", False):
try:
self._spinner_thread.join(timeout=0.1)
self.waiting_spinner.stop()
except Exception:
pass
@ -187,10 +184,9 @@ class MarkdownStream:
"""
# On the first call, stop the spinner and start the Live renderer
if not getattr(self, "_live_started", False):
if hasattr(self, "_spinner_stop_event"):
self._spinner_stop_event.set()
if hasattr(self, "_spinner_thread"):
self._spinner_thread.join()
if getattr(self, "_spinner_running", False):
self.waiting_spinner.stop()
self._spinner_running = False
self.live = Live(Text(""), refresh_per_second=1.0 / self.min_delay)
self.live.start()
self._live_started = True

View file

@ -0,0 +1,54 @@
#!/usr/bin/env python
"""
Thread-based, killable spinner utility.
Use it like:
from aider.waiting import WaitingSpinner
spinner = WaitingSpinner("Waiting for LLM")
spinner.start()
... # long task
spinner.stop()
"""
import threading
import time
from aider.utils import Spinner
class WaitingSpinner:
"""Background spinner that can be started/stopped safely."""
def __init__(self, text: str = "Waiting for LLM", delay: float = 0.15):
self.spinner = Spinner(text)
self.delay = delay
self._stop_event = threading.Event()
self._thread = threading.Thread(target=self._spin, daemon=True)
def _spin(self):
while not self._stop_event.is_set():
time.sleep(self.delay)
self.spinner.step()
self.spinner.end()
def start(self):
"""Start the spinner in a background thread."""
if not self._thread.is_alive():
self._thread.start()
def stop(self):
"""Request the spinner to stop and wait briefly for the thread to exit."""
self._stop_event.set()
if self._thread.is_alive():
self._thread.join(timeout=0.1)
# Allow use as a context-manager
def __enter__(self):
self.start()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.stop()