This commit is contained in:
yumoqing 2026-05-15 16:55:01 +08:00
parent fb25b4a28d
commit 6cf4dbb8cc

View File

@ -3,196 +3,197 @@ import inspect
import traceback import traceback
import weakref import weakref
from typing import Callable, Any from typing import Callable, Any
from appPublic.log import debug, error, exception, warning
class WeakCallback: class WeakCallback:
def __init__(self, func: Callable): def __init__(self, func: Callable):
self._is_coroutine = inspect.iscoroutinefunction(func) self._is_coroutine = inspect.iscoroutinefunction(func)
if inspect.ismethod(func): if inspect.ismethod(func):
self._ref = weakref.WeakMethod(func) self._ref = weakref.WeakMethod(func)
else: else:
self._ref = weakref.ref(func) self._ref = weakref.ref(func)
self._hash = hash(func) self._hash = hash(func)
@property @property
def is_coroutine(self): def is_coroutine(self):
return self._is_coroutine return self._is_coroutine
def get(self): def get(self):
return self._ref() return self._ref()
def __eq__(self, other): def __eq__(self, other):
return isinstance(other, WeakCallback) and self._hash == other._hash return isinstance(other, WeakCallback) and self._hash == other._hash
def __hash__(self): def __hash__(self):
return self._hash return self._hash
class EventDispatcher: class EventDispatcher:
def __init__( def __init__(
self, self,
*, *,
continue_on_error=True, continue_on_error=True,
log_traceback=True, log_traceback=True,
handler_timeout=None, handler_timeout=None,
error_handler=None, error_handler=None,
): ):
self._events = {} self._events = {}
self.continue_on_error = continue_on_error self.continue_on_error = continue_on_error
self.log_traceback = log_traceback self.log_traceback = log_traceback
self.handler_timeout = handler_timeout self.handler_timeout = handler_timeout
self.error_handler = error_handler self.error_handler = error_handler
def bind(self, event_name: str, func: Callable): def bind(self, event_name: str, func: Callable):
if event_name not in self._events: if event_name not in self._events:
self._events[event_name] = set() self._events[event_name] = set()
self._events[event_name].add(WeakCallback(func)) self._events[event_name].add(WeakCallback(func))
def unbind(self, event_name: str, func: Callable): def unbind(self, event_name: str, func: Callable):
if event_name not in self._events: if event_name not in self._events:
return return
target = WeakCallback(func) target = WeakCallback(func)
self._events[event_name] = { self._events[event_name] = {
cb for cb in self._events[event_name] cb for cb in self._events[event_name]
if cb != target if cb != target
} }
if not self._events[event_name]: if not self._events[event_name]:
del self._events[event_name] del self._events[event_name]
async def _run_error_handler( async def _run_error_handler(
self, self,
event_name, event_name,
func, func,
exc, exc,
): ):
if not self.error_handler: if not self.error_handler:
return return
try: try:
if inspect.iscoroutinefunction(self.error_handler): if inspect.iscoroutinefunction(self.error_handler):
await self.error_handler( await self.error_handler(
event_name, event_name,
func, func,
exc, exc,
) )
else: else:
self.error_handler( self.error_handler(
event_name, event_name,
func, func,
exc, exc,
) )
except Exception as e: except Exception as e:
print(f"[EventDispatcher] error_handler failed: {e}") print(f"[EventDispatcher] error_handler failed: {e}")
async def _execute_handler( async def _execute_handler(
self, self,
cb, cb,
event_name, event_name,
data, data,
): ):
func = cb.get() func = cb.get()
if func is None: if func is None:
return False return False
try: try:
if cb.is_coroutine: if cb.is_coroutine:
coro = func(data) coro = func(data)
if self.handler_timeout: if self.handler_timeout:
await asyncio.wait_for( await asyncio.wait_for(
coro, coro,
timeout=self.handler_timeout, timeout=self.handler_timeout,
) )
else: else:
await coro await coro
else: else:
if self.handler_timeout: if self.handler_timeout:
await asyncio.wait_for( await asyncio.wait_for(
asyncio.to_thread(func, data), asyncio.to_thread(func, data),
timeout=self.handler_timeout, timeout=self.handler_timeout,
) )
else: else:
func(data) func(data)
return True return True
except Exception as e: except Exception as e:
debug(
f"[EventDispatcher] "
f"handler failed: "
f"event={event_name}, "
f"handler={func}"
)
print( if self.log_traceback:
f"[EventDispatcher] " debug(f'{trackback.format_exc()}')
f"handler failed: " # traceback.print_exc()
f"event={event_name}, "
f"handler={func}"
)
if self.log_traceback: await self._run_error_handler(
traceback.print_exc() event_name,
func,
e,
)
await self._run_error_handler( if not self.continue_on_error:
event_name, raise
func,
e,
)
if not self.continue_on_error: return True
raise
return True async def dispatch(
self,
event_name: str,
data: Any = None,
):
async def dispatch( if event_name not in self._events:
self, return
event_name: str,
data: Any = None,
):
if event_name not in self._events: dead_callbacks = []
return
dead_callbacks = [] for cb in list(self._events[event_name]):
for cb in list(self._events[event_name]): func = cb.get()
func = cb.get() if func is None:
dead_callbacks.append(cb)
continue
if func is None: await self._execute_handler(
dead_callbacks.append(cb) cb,
continue event_name,
data,
)
await self._execute_handler( for cb in dead_callbacks:
cb, self._events[event_name].discard(cb)
event_name,
data,
)
for cb in dead_callbacks: if (
self._events[event_name].discard(cb) event_name in self._events
and not self._events[event_name]
if ( ):
event_name in self._events del self._events[event_name]
and not self._events[event_name]
):
del self._events[event_name]