Source code for flexget.event
"""Provides small event framework."""
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from loguru import logger
if TYPE_CHECKING:
from collections.abc import Callable
logger = logger.bind(name='event')
[docs]
class Event:
"""Represents one registered event."""
def __init__(self, name: str, func: Callable, priority: int = 128) -> None:
self.name = name
self.func = func
self.priority = priority
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
def __eq__(self, other):
return self.priority == other.priority
def __lt__(self, other):
return self.priority < other.priority
def __gt__(self, other):
return self.priority > other.priority
def __str__(self):
return f'<Event(name={self.name},func={self.func.__name__},priority={self.priority})>'
__repr__ = __str__
def __hash__(self):
return hash((self.name, self.func, self.priority))
_events: dict[str, list[Event]] = {}
[docs]
def event(name: str, priority: int = 128) -> Callable[[Callable], Callable]:
"""Register event to function with a decorator."""
def decorator(func: Callable) -> Callable:
add_event_handler(name, func, priority)
return func
return decorator
[docs]
def get_events(name: str) -> list[Event]:
"""Return list of :class:`Event` for *name* ordered by priority.
:param String name: event name
"""
if name not in _events:
raise KeyError(f'No such event {name}')
_events[name].sort(reverse=True)
return _events[name]
[docs]
def add_event_handler(name: str, func: Callable, priority: int = 128) -> Event:
"""Return event created.
:param string name: Event name
:param function func: Function that acts as event handler
:param priority: Priority for this hook
:return: Event created
:rtype: Event
:raises Exception: If *func* is already registered in an event
"""
events = _events.setdefault(name, [])
for event in events:
if event.func == func:
raise ValueError(
f'{func.__name__} has already been registered as event listener under name {name}'
)
logger.trace('registered function {} to event {}', func.__name__, name)
event = Event(name, func, priority)
events.append(event)
return event
[docs]
def remove_event_handlers(name: str) -> None:
"""Remove all handlers for given event `name`."""
_events.pop(name, None)
[docs]
def remove_event_handler(name: str, func: Callable) -> None:
"""Remove `func` from the handlers for event `name`."""
for e in list(_events.get(name, [])):
if e.func is func:
_events[name].remove(e)
[docs]
def fire_event(name: str, *args, **kwargs) -> Any:
"""Trigger an event with *name*.
If event is not hooked by anything nothing happens. If a function that hooks an event
returns a value, it will replace the first argument when calling next function.
:param name: Name of event to be called
:param args: List of arguments passed to handler function
:param kwargs: Key Value arguments passed to handler function
"""
if name in _events:
for event in get_events(name):
result = event(*args, **kwargs)
if result is not None:
args = result, *args[1:]
return args and args[0]