From 1cc6e78b2a1c4d80c47b0cc4a85ea0993f94c75e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 15 Mar 2026 13:47:45 +0000 Subject: [PATCH] fix: inherit _FileChangeHandler from FileSystemEventHandler (#67) - _FileChangeHandler now inherits from watchdog.events.FileSystemEventHandler - Added super().__init__() call in __init__ - Removed # noqa: ANN001 suppression from dispatch - Removed # type: ignore[arg-type] from observer.schedule() - Added test verifying inheritance and dispatch behavior Closes #67 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/copilot_usage/cli.py | 8 +++++--- tests/copilot_usage/test_cli.py | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/copilot_usage/cli.py b/src/copilot_usage/cli.py index 329af2f..9a59e6a 100644 --- a/src/copilot_usage/cli.py +++ b/src/copilot_usage/cli.py @@ -16,6 +16,7 @@ import click from rich.console import Console from rich.table import Table +from watchdog.events import FileSystemEventHandler # type: ignore[import-untyped] from watchdog.observers import Observer # type: ignore[import-untyped] from copilot_usage.models import SessionSummary @@ -114,14 +115,15 @@ def _read_line_nonblocking(timeout: float = 0.5) -> str | None: return None -class _FileChangeHandler: +class _FileChangeHandler(FileSystemEventHandler): # type: ignore[misc] """Watchdog handler that triggers refresh on any session-state change.""" def __init__(self, change_event: threading.Event) -> None: + super().__init__() self._change_event = change_event self._last_trigger = 0.0 - def dispatch(self, event: object) -> None: # noqa: ANN001 + def dispatch(self, event: object) -> None: now = time.monotonic() if now - self._last_trigger > 2.0: # debounce 2s self._last_trigger = now @@ -132,7 +134,7 @@ def _start_observer(session_path: Path, change_event: threading.Event) -> object """Start a watchdog observer monitoring *session_path* for changes.""" handler = _FileChangeHandler(change_event) observer = Observer() - observer.schedule(handler, str(session_path), recursive=True) # type: ignore[arg-type] + observer.schedule(handler, str(session_path), recursive=True) observer.daemon = True observer.start() return observer diff --git a/tests/copilot_usage/test_cli.py b/tests/copilot_usage/test_cli.py index b16543b..2471ac5 100644 --- a/tests/copilot_usage/test_cli.py +++ b/tests/copilot_usage/test_cli.py @@ -586,3 +586,19 @@ def test_dispatch_fires_again_after_debounce_gap(self) -> None: handler._last_trigger = _time.monotonic() - 3.0 # pyright: ignore[reportPrivateUsage] handler.dispatch(object()) assert event.is_set() + + def test_inherits_from_filesystemeventhandler(self) -> None: + """_FileChangeHandler inherits from watchdog FileSystemEventHandler.""" + from watchdog.events import ( + FileSystemEventHandler, # type: ignore[import-untyped] + ) + + from copilot_usage.cli import ( + _FileChangeHandler, # pyright: ignore[reportPrivateUsage] + ) + + event = threading.Event() + handler = _FileChangeHandler(event) + assert isinstance(handler, FileSystemEventHandler) + handler.dispatch(object()) + assert event.is_set()