2024-08-01 22:57:35 +08:00
|
|
|
import logging
|
|
|
|
from asyncio import create_task, gather, Lock as ALock
|
|
|
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
|
|
from threading import Lock
|
|
|
|
from typing import Callable, List, Dict
|
2023-04-23 22:54:32 +08:00
|
|
|
|
2024-08-01 22:57:35 +08:00
|
|
|
# Initialize logger
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
|
logger = logging.getLogger(__name__)
|
2023-04-23 22:54:32 +08:00
|
|
|
|
2024-08-01 22:57:35 +08:00
|
|
|
subscribers: Dict[str, List[Callable]] = {}
|
|
|
|
lock = Lock() # Synchronous lock
|
|
|
|
alock = ALock() # Asynchronous lock
|
2023-04-23 22:54:32 +08:00
|
|
|
|
2024-08-01 22:57:35 +08:00
|
|
|
|
|
|
|
# Synchronous Event System
|
2023-04-23 22:54:32 +08:00
|
|
|
def subscribe(event_type: str, fn: Callable):
|
2024-08-01 22:57:35 +08:00
|
|
|
with lock:
|
|
|
|
if event_type not in subscribers:
|
|
|
|
subscribers[event_type] = []
|
|
|
|
subscribers[event_type].append(fn)
|
|
|
|
|
2023-04-23 22:54:32 +08:00
|
|
|
|
2024-08-01 22:57:35 +08:00
|
|
|
def unsubscribe(event_type: str, fn: Callable):
|
|
|
|
with lock:
|
|
|
|
if event_type in subscribers:
|
|
|
|
try:
|
|
|
|
subscribers[event_type].remove(fn)
|
|
|
|
except ValueError:
|
|
|
|
logger.warning(f"Function not found in subscribers for event type: {event_type}")
|
2023-04-23 22:54:32 +08:00
|
|
|
|
|
|
|
|
|
|
|
def post_event(event_type: str, **kwargs):
|
2024-08-01 22:57:35 +08:00
|
|
|
with lock:
|
|
|
|
if event_type not in subscribers:
|
|
|
|
return
|
|
|
|
current_subscribers: List[Callable] = subscribers[event_type][:]
|
|
|
|
|
|
|
|
with ThreadPoolExecutor() as executor:
|
|
|
|
futures = []
|
|
|
|
for fn in current_subscribers:
|
|
|
|
futures.append(executor.submit(safe_execute, fn, **kwargs))
|
|
|
|
|
|
|
|
for future in as_completed(futures):
|
|
|
|
if future.exception() is not None:
|
|
|
|
logger.error(f"Error executing event handler: {future.exception()}")
|
2023-04-23 22:54:32 +08:00
|
|
|
|
2024-08-01 22:57:35 +08:00
|
|
|
|
|
|
|
def safe_execute(fn: Callable, **kwargs):
|
|
|
|
try:
|
2023-04-23 22:54:32 +08:00
|
|
|
fn(**kwargs)
|
2024-08-01 22:57:35 +08:00
|
|
|
except Exception as e:
|
|
|
|
logger.error(f"Error in event subscriber {fn.__name__}: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
# Asynchronous Event System
|
|
|
|
async def asubscribe(event_type: str, fn: Callable):
|
|
|
|
async with alock:
|
|
|
|
if event_type not in subscribers:
|
|
|
|
subscribers[event_type] = []
|
|
|
|
subscribers[event_type].append(fn)
|
|
|
|
|
|
|
|
|
|
|
|
async def aunsubscribe(event_type: str, fn: Callable):
|
|
|
|
async with alock:
|
|
|
|
if event_type in subscribers:
|
|
|
|
try:
|
|
|
|
subscribers[event_type].remove(fn)
|
|
|
|
except ValueError:
|
|
|
|
logger.warning(f"Function not found in subscribers for event type: {event_type}")
|
|
|
|
|
|
|
|
|
|
|
|
async def apost_event(event_type: str, **kwargs):
|
|
|
|
async with alock:
|
|
|
|
if event_type not in subscribers:
|
|
|
|
return
|
|
|
|
current_subscribers: List[Callable] = subscribers[event_type][:]
|
|
|
|
|
|
|
|
tasks = [create_task(asafe_execute(fn, **kwargs)) for fn in current_subscribers]
|
|
|
|
await gather(*tasks)
|
|
|
|
|
|
|
|
|
|
|
|
async def asafe_execute(fn: Callable, **kwargs):
|
|
|
|
try:
|
|
|
|
if callable(fn):
|
|
|
|
if hasattr(fn, "__await__"): # Check if it's an async function
|
|
|
|
await fn(**kwargs)
|
|
|
|
else:
|
|
|
|
fn(**kwargs)
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(f"Error in async event subscriber {fn.__name__}: {e}")
|