Spaces:
Sleeping
Sleeping
text-generation-webui
/
installer_files
/env
/lib
/python3.11
/site-packages
/aiogram
/dispatcher
/handler.py
import inspect | |
import typing | |
from contextvars import ContextVar | |
from dataclasses import dataclass | |
ctx_data = ContextVar('ctx_handler_data') | |
current_handler = ContextVar('current_handler') | |
class FilterObj: | |
filter: callable | |
kwargs: dict | |
is_async: bool | |
class SkipHandler(Exception): | |
pass | |
class CancelHandler(Exception): | |
pass | |
def _get_spec(func: callable): | |
while hasattr(func, '__wrapped__'): # Try to resolve decorated callbacks | |
func = func.__wrapped__ | |
return inspect.getfullargspec(func) | |
def _check_spec(spec: inspect.FullArgSpec, kwargs: dict): | |
if spec.varkw: | |
return kwargs | |
return {k: v for k, v in kwargs.items() if k in set(spec.args + spec.kwonlyargs)} | |
class Handler: | |
def __init__(self, dispatcher, once=True, middleware_key=None): | |
self.dispatcher = dispatcher | |
self.once = once | |
self.handlers: typing.List[Handler.HandlerObj] = [] | |
self.middleware_key = middleware_key | |
def register(self, handler, filters=None, index=None): | |
""" | |
Register callback | |
Filters can be awaitable or not. | |
:param handler: coroutine | |
:param filters: list of filters | |
:param index: you can reorder handlers | |
""" | |
from .filters import get_filters_spec | |
spec = _get_spec(handler) | |
if filters and not isinstance(filters, (list, tuple, set)): | |
filters = [filters] | |
filters = get_filters_spec(self.dispatcher, filters) | |
record = Handler.HandlerObj(handler=handler, spec=spec, filters=filters) | |
if index is None: | |
self.handlers.append(record) | |
else: | |
self.handlers.insert(index, record) | |
def unregister(self, handler): | |
""" | |
Remove handler | |
:param handler: callback | |
:return: | |
""" | |
for handler_obj in self.handlers: | |
if handler == handler_obj or handler == handler_obj.handler: | |
self.handlers.remove(handler_obj) | |
return True | |
raise ValueError('This handler is not registered!') | |
async def notify(self, *args): | |
""" | |
Notify handlers | |
:param args: | |
:return: | |
""" | |
from .filters import check_filters, FilterNotPassed | |
results = [] | |
data = {} | |
ctx_data.set(data) | |
if self.middleware_key: | |
try: | |
await self.dispatcher.middleware.trigger(f"pre_process_{self.middleware_key}", args + (data,)) | |
except CancelHandler: # Allow to cancel current event | |
return results | |
try: | |
for handler_obj in self.handlers: | |
try: | |
data.update(await check_filters(handler_obj.filters, args)) | |
except FilterNotPassed: | |
continue | |
else: | |
ctx_token = current_handler.set(handler_obj.handler) | |
try: | |
if self.middleware_key: | |
await self.dispatcher.middleware.trigger(f"process_{self.middleware_key}", args + (data,)) | |
partial_data = _check_spec(handler_obj.spec, data) | |
response = await handler_obj.handler(*args, **partial_data) | |
if response is not None: | |
results.append(response) | |
if self.once: | |
break | |
except SkipHandler: | |
continue | |
except CancelHandler: | |
break | |
finally: | |
current_handler.reset(ctx_token) | |
finally: | |
if self.middleware_key: | |
await self.dispatcher.middleware.trigger(f"post_process_{self.middleware_key}", | |
args + (results, data,)) | |
return results | |
class HandlerObj: | |
handler: callable | |
spec: inspect.FullArgSpec | |
filters: typing.Optional[typing.Iterable[FilterObj]] = None | |