From f9bc2204c7acb8c791dab79363f75c0a019997fd Mon Sep 17 00:00:00 2001 From: Vitali Graf Date: Wed, 8 Apr 2026 10:28:52 +0200 Subject: [PATCH] refactor: convert hotkey module to package with evdev backend Co-Authored-By: Claude Sonnet 4.6 --- tests/test_hotkey.py | 7 +++--- whisper_local/hotkey/__init__.py | 24 +++++++++++++++++++ whisper_local/{hotkey.py => hotkey/_evdev.py} | 10 ++++---- 3 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 whisper_local/hotkey/__init__.py rename whisper_local/{hotkey.py => hotkey/_evdev.py} (93%) diff --git a/tests/test_hotkey.py b/tests/test_hotkey.py index ad130d6..12ea5a9 100644 --- a/tests/test_hotkey.py +++ b/tests/test_hotkey.py @@ -1,14 +1,16 @@ import asyncio +import sys from unittest.mock import AsyncMock, MagicMock, patch import pytest -from whisper_local.hotkey import HotkeyListener +pytestmark = pytest.mark.skipif(sys.platform != "linux", reason="evdev only available on Linux") @pytest.fixture def listener(): - return HotkeyListener(key_name="KEY_F12") + from whisper_local.hotkey._evdev import EvdevHotkeyListener + return EvdevHotkeyListener(key_name="KEY_F12") class TestHotkeyListener: @@ -33,6 +35,5 @@ class TestHotkeyListener: @pytest.mark.asyncio async def test_no_callback_no_error(self, listener): - # Kein Callback gesetzt — soll nicht crashen await listener._handle_key_event(key_down=True) await listener._handle_key_event(key_down=False) diff --git a/whisper_local/hotkey/__init__.py b/whisper_local/hotkey/__init__.py new file mode 100644 index 0000000..d6f6aa4 --- /dev/null +++ b/whisper_local/hotkey/__init__.py @@ -0,0 +1,24 @@ +"""Hotkey-Listener — plattformspezifische Backends hinter gemeinsamem Interface.""" + +import sys +from typing import Callable, Coroutine, Protocol, runtime_checkable + +AsyncCallback = Callable[[], Coroutine] + + +@runtime_checkable +class HotkeyListener(Protocol): + on_press: AsyncCallback | None + on_release: AsyncCallback | None + + async def listen(self) -> None: ... + + +def create_listener(key_name: str = "KEY_F12") -> HotkeyListener: + """Erstellt den plattformspezifischen HotkeyListener.""" + if sys.platform == "linux": + from whisper_local.hotkey._evdev import EvdevHotkeyListener + return EvdevHotkeyListener(key_name) + else: + from whisper_local.hotkey._pynput import PynputHotkeyListener + return PynputHotkeyListener(key_name) diff --git a/whisper_local/hotkey.py b/whisper_local/hotkey/_evdev.py similarity index 93% rename from whisper_local/hotkey.py rename to whisper_local/hotkey/_evdev.py index 24d8505..da0b9fb 100644 --- a/whisper_local/hotkey.py +++ b/whisper_local/hotkey/_evdev.py @@ -1,16 +1,14 @@ -"""Hotkey-Listener via evdev für Push-to-Talk.""" +"""Hotkey-Listener via evdev für Push-to-Talk (Linux).""" import asyncio import logging -from pathlib import Path -from typing import Callable, Coroutine import evdev from evdev import InputDevice, categorize, ecodes -logger = logging.getLogger(__name__) +from whisper_local.hotkey import AsyncCallback -AsyncCallback = Callable[[], Coroutine] +logger = logging.getLogger(__name__) def find_keyboard_devices(key_name: str) -> list[InputDevice]: @@ -31,7 +29,7 @@ def find_keyboard_devices(key_name: str) -> list[InputDevice]: return matches -class HotkeyListener: +class EvdevHotkeyListener: def __init__(self, key_name: str = "KEY_F12"): self.key_name = key_name self.key_code = ecodes.ecodes.get(key_name)