557eb22e30
It accumulates the exceptions and raises an ExceptionGroup exception if there were any failures. Rationale: Event handlers should not be dependent on the order in which they are executed. If a handler fails the remaining handlers are not executed. This can result in non-deterministic behavior. This commit fixes that my allowing all handlers to be executed. To make this work we use the (backported) ExceptionGroup from Python 3.11.
53 lines
1.2 KiB
Python
53 lines
1.2 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Callable
|
|
|
|
import pytest
|
|
from exceptiongroup import ExceptionGroup
|
|
|
|
from generic.event import Event, Manager
|
|
|
|
|
|
@pytest.fixture
|
|
def events():
|
|
return Manager()
|
|
|
|
|
|
def make_handler(effect: object) -> Callable[[Event], None]:
|
|
def handler(e):
|
|
e.effects.append(effect)
|
|
raise ValueError(effect)
|
|
|
|
return handler
|
|
|
|
|
|
def test_handle_all_subscribers(events):
|
|
events.subscribe(make_handler("handler1"), MyEvent)
|
|
events.subscribe(make_handler("handler2"), MyEvent)
|
|
e = MyEvent()
|
|
with pytest.raises(ExceptionGroup):
|
|
events.handle(e)
|
|
|
|
assert len(e.effects) == 2
|
|
assert "handler1" in e.effects
|
|
assert "handler2" in e.effects
|
|
|
|
|
|
def test_collect_all_exceptions(events):
|
|
events.subscribe(make_handler("handler1"), MyEvent)
|
|
events.subscribe(make_handler("handler2"), MyEvent)
|
|
e = MyEvent()
|
|
with pytest.raises(ExceptionGroup) as excinfo:
|
|
events.handle(e)
|
|
|
|
exc = excinfo.value
|
|
nested_exc = [str(e) for e in exc.exceptions]
|
|
assert len(exc.exceptions) == 2
|
|
assert "handler1" in nested_exc
|
|
assert "handler2" in nested_exc
|
|
|
|
|
|
class MyEvent:
|
|
def __init__(self) -> None:
|
|
self.effects: list[object] = []
|