Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve event handler #6

Merged
merged 4 commits into from
Feb 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 38 additions & 18 deletions pyflowlauncher/event.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,56 @@
import asyncio
from typing import Any, Callable, Iterable, Type
from typing import Any, Callable, Iterable, Type, Union


class EventNotFound(Exception):

def __init__(self, event: str):
self.event = event
super().__init__(f"Event '{event}' not found.")


class EventHandler:

def __init__(self):
self._methods = {}
self._events = {}
self._handlers = {}

def _get_callable_name(self, method: Callable[..., Any]):
def _get_callable_name(self, method: Union[Callable[..., Any], Exception]):
return getattr(method, '__name__', method.__class__.__name__).lower()

def add_method(self, method: Callable[..., Any], *, name=None) -> str:
key = name or self._get_callable_name(method)
self._methods[key] = method
def add_event(self, event: Callable[..., Any], *, name=None) -> str:
key = name or self._get_callable_name(event)
self._events[key] = event
return key

def add_methods(self, methods: Iterable[Callable[..., Any]]):
for method in methods:
self.add_method(method)
def add_events(self, events: Iterable[Callable[..., Any]]):
for event in events:
self.add_event(event)

def add_exception_handler(self, exception: Type[Exception], handler: Callable[..., Any]):
self._handlers[exception] = handler

async def __call__(self, method: str, *args, **kwargs):
def get_event(self, event: str) -> Callable[..., Any]:
try:
return self._events[event]
except KeyError:
raise EventNotFound(event)

async def _await_maybe(self, result: Any) -> Any:
if asyncio.iscoroutine(result):
return await result
return result

async def trigger_exception_handler(self, exception: Exception) -> Any:
try:
handler = self._handlers[exception.__class__]
return await self._await_maybe(handler(exception))
except KeyError:
raise exception

async def trigger_event(self, event: str, *args, **kwargs) -> Any:
try:
result = self._methods[method](*args, **kwargs)
if asyncio.iscoroutine(result):
return await result
return result
result = self.get_event(event)(*args, **kwargs)
return await self._await_maybe(result)
except Exception as e:
handler = self._handlers.get(type(e), None)
if handler:
return handler(e)
raise e
return await self.trigger_exception_handler(e)
10 changes: 5 additions & 5 deletions pyflowlauncher/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ def __init__(self, methods: list[Method] | None = None) -> None:

def add_method(self, method: Method) -> str:
"""Add a method to the event handler."""
return self._event_handler.add_method(method)
return self._event_handler.add_event(method)

def add_methods(self, methods: Iterable[Method]) -> None:
self._event_handler.add_methods(methods)
self._event_handler.add_events(methods)

def on_method(self, method: Method) -> Method:
@wraps(method)
def wrapper(*args, **kwargs):
return method(*args, **kwargs)
self._event_handler.add_method(wrapper)
self._event_handler.add_event(wrapper)
return wrapper

def method(self, method: Method) -> Method:
Expand Down Expand Up @@ -73,10 +73,10 @@ def run(self) -> None:
method = request["method"]
parameters = request.get('parameters', [])
if sys.version_info >= (3, 10, 0):
feedback = asyncio.run(self._event_handler(method, *parameters))
feedback = asyncio.run(self._event_handler.trigger_event(method, *parameters))
else:
loop = asyncio.get_event_loop()
feedback = loop.run_until_complete(self._event_handler(method, *parameters))
feedback = loop.run_until_complete(self._event_handler.trigger_event(method, *parameters))
if not feedback:
return
self._client.send(feedback)
Expand Down
20 changes: 10 additions & 10 deletions tests/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,28 @@ def except_method():

def test_add_method():
handler = EventHandler()
handler.add_method(temp_method1)
assert handler._methods == {"temp_method1": temp_method1}
handler.add_event(temp_method1)
assert handler._events == {"temp_method1": temp_method1}


def test_add_methods():
handler = EventHandler()
handler.add_methods([temp_method1, temp_method2])
assert handler._methods == {"temp_method1": temp_method1, "temp_method2": temp_method2}
handler.add_events([temp_method1, temp_method2])
assert handler._events == {"temp_method1": temp_method1, "temp_method2": temp_method2}


@pytest.mark.asyncio
async def test_call():
handler = EventHandler()
handler.add_method(temp_method1)
assert await handler("temp_method1") is None
handler.add_event(temp_method1)
assert await handler.trigger_event("temp_method1") is None


@pytest.mark.asyncio
async def test_call_async():
handler = EventHandler()
handler.add_method(async_temp_method3)
assert await handler("async_temp_method3") is None
handler.add_event(async_temp_method3)
assert await handler.trigger_event("async_temp_method3") is None


def test_add_exception_handler():
Expand All @@ -53,6 +53,6 @@ def test_add_exception_handler():
@pytest.mark.asyncio
async def test_call_exception():
handler = EventHandler()
handler.add_method(except_method)
handler.add_event(except_method)
with pytest.raises(Exception):
await handler("except_method")
await handler.trigger_event("except_method")
4 changes: 2 additions & 2 deletions tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ def query(query: str):
def test_add_method():
plugin = Plugin()
plugin.add_method(temp_method1)
assert plugin._event_handler._methods == {'temp_method1': temp_method1}
assert plugin._event_handler._events == {'temp_method1': temp_method1}


def test_add_methods():
plugin = Plugin()
plugin.add_methods([temp_method1, temp_method2])
assert plugin._event_handler._methods == {'temp_method1': temp_method1, 'temp_method2': temp_method2}
assert plugin._event_handler._events == {'temp_method1': temp_method1, 'temp_method2': temp_method2}


def test_settings():
Expand Down
Loading