decorators.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. # -*- coding: utf-8 -*-
  2. """
  3. Dekoratoren für das Event-System.
  4. Ermöglicht die einfache Registrierung von Event-Handlern
  5. über Dekoratoren auf Methoden.
  6. """
  7. from typing import Callable, Any, Type
  8. from functools import wraps
  9. from trixy_core.events.enums import EventPriority
  10. from trixy_core.events.event_data.base import EventData
  11. # Attribut-Name für registrierte Event-Handler
  12. _EVENT_HANDLERS_ATTR = "_trixy_event_handlers"
  13. def TrixyEvent(
  14. events: list[str],
  15. priority: EventPriority = EventPriority.NORMAL
  16. ) -> Callable[[Callable], Callable]:
  17. """
  18. Dekorator zur Registrierung einer Methode als Event-Handler.
  19. Verwendung:
  20. @TrixyEvent(["wakeword_received", "satellite_connected"])
  21. def on_events(self, event_name: str, event_data: EventData):
  22. pass
  23. Args:
  24. events: Liste der Event-Namen, auf die reagiert werden soll
  25. priority: Handler-Priorität
  26. Returns:
  27. Dekorator-Funktion
  28. """
  29. def decorator(func: Callable) -> Callable:
  30. # Speichere Handler-Metadaten direkt auf der Funktion
  31. # Kein Wrapper nötig - bewahrt async-Erkennung via iscoroutinefunction()
  32. if not hasattr(func, _EVENT_HANDLERS_ATTR):
  33. setattr(func, _EVENT_HANDLERS_ATTR, [])
  34. handlers = getattr(func, _EVENT_HANDLERS_ATTR)
  35. for event_name in events:
  36. handlers.append({
  37. "event_name": event_name,
  38. "priority": priority,
  39. "handler": func,
  40. })
  41. return func
  42. return decorator
  43. def event_handler(
  44. event_class: Type[EventData],
  45. priority: EventPriority = EventPriority.NORMAL
  46. ) -> Callable[[Callable], Callable]:
  47. """
  48. Dekorator zur Registrierung einer Funktion als typisierter Event-Handler.
  49. Verwendung:
  50. @event_handler(WakewordReceived)
  51. async def on_wakeword(self, event: WakewordReceived):
  52. pass
  53. Args:
  54. event_class: Die Event-Datenklasse, auf die reagiert werden soll
  55. priority: Handler-Priorität
  56. Returns:
  57. Dekorator-Funktion
  58. """
  59. event_name = event_class.event_name()
  60. def decorator(func: Callable) -> Callable:
  61. if not hasattr(func, _EVENT_HANDLERS_ATTR):
  62. setattr(func, _EVENT_HANDLERS_ATTR, [])
  63. handlers = getattr(func, _EVENT_HANDLERS_ATTR)
  64. handlers.append({
  65. "event_name": event_name,
  66. "event_class": event_class,
  67. "priority": priority,
  68. "handler": func,
  69. })
  70. return func
  71. return decorator
  72. def get_event_handlers(obj: Any) -> list[dict[str, Any]]:
  73. """
  74. Extrahiert alle Event-Handler aus einem Objekt.
  75. Durchsucht alle Methoden des Objekts nach dem Event-Handler-Attribut.
  76. Args:
  77. obj: Das zu durchsuchende Objekt
  78. Returns:
  79. Liste der Handler-Metadaten
  80. """
  81. handlers = []
  82. for attr_name in dir(obj):
  83. if attr_name.startswith("_"):
  84. continue
  85. try:
  86. attr = getattr(obj, attr_name)
  87. if callable(attr) and hasattr(attr, _EVENT_HANDLERS_ATTR):
  88. for handler_info in getattr(attr, _EVENT_HANDLERS_ATTR):
  89. handlers.append({
  90. **handler_info,
  91. "instance": obj,
  92. "method": attr,
  93. })
  94. except Exception:
  95. continue
  96. return handlers