mirror of
https://github.com/aiogram/aiogram.git
synced 2026-04-08 16:37:47 +00:00
Add filters and class based handler for errors
This commit is contained in:
parent
9e673998f0
commit
0fbd2819f9
9 changed files with 167 additions and 1 deletions
|
|
@ -3,6 +3,7 @@ from typing import Dict, Tuple, Type
|
||||||
from .base import BaseFilter
|
from .base import BaseFilter
|
||||||
from .command import Command, CommandObject
|
from .command import Command, CommandObject
|
||||||
from .content_types import ContentTypesFilter
|
from .content_types import ContentTypesFilter
|
||||||
|
from .exception import ExceptionMessageFilter, ExceptionTypeFilter
|
||||||
from .text import Text
|
from .text import Text
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
|
@ -12,6 +13,8 @@ __all__ = (
|
||||||
"Command",
|
"Command",
|
||||||
"CommandObject",
|
"CommandObject",
|
||||||
"ContentTypesFilter",
|
"ContentTypesFilter",
|
||||||
|
"ExceptionMessageFilter",
|
||||||
|
"ExceptionTypeFilter",
|
||||||
)
|
)
|
||||||
|
|
||||||
BUILTIN_FILTERS: Dict[str, Tuple[Type[BaseFilter], ...]] = {
|
BUILTIN_FILTERS: Dict[str, Tuple[Type[BaseFilter], ...]] = {
|
||||||
|
|
@ -27,5 +30,5 @@ BUILTIN_FILTERS: Dict[str, Tuple[Type[BaseFilter], ...]] = {
|
||||||
"pre_checkout_query": (),
|
"pre_checkout_query": (),
|
||||||
"poll": (),
|
"poll": (),
|
||||||
"poll_answer": (),
|
"poll_answer": (),
|
||||||
"errors": (),
|
"error": (ExceptionMessageFilter, ExceptionTypeFilter),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
36
aiogram/dispatcher/filters/exception.py
Normal file
36
aiogram/dispatcher/filters/exception.py
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import re
|
||||||
|
from typing import Any, Dict, Pattern, Tuple, Type, Union, cast
|
||||||
|
|
||||||
|
from pydantic import validator
|
||||||
|
|
||||||
|
from aiogram.dispatcher.filters import BaseFilter
|
||||||
|
|
||||||
|
|
||||||
|
class ExceptionTypeFilter(BaseFilter):
|
||||||
|
exception: Union[Type[Exception], Tuple[Type[Exception]]]
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
arbitrary_types_allowed = True
|
||||||
|
|
||||||
|
async def __call__(self, exception: Exception) -> Union[bool, Dict[str, Any]]:
|
||||||
|
return isinstance(exception, self.exception)
|
||||||
|
|
||||||
|
|
||||||
|
class ExceptionMessageFilter(BaseFilter):
|
||||||
|
match: Union[str, Pattern[str]]
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
arbitrary_types_allowed = True
|
||||||
|
|
||||||
|
@validator("match")
|
||||||
|
def _validate_match(cls, value: Union[str, Pattern[str]]) -> Union[str, Pattern[str]]:
|
||||||
|
if isinstance(value, str):
|
||||||
|
return re.compile(value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
async def __call__(self, exception: Exception) -> Union[bool, Dict[str, Any]]:
|
||||||
|
pattern = cast(Pattern[str], self.match)
|
||||||
|
result = pattern.match(str(exception))
|
||||||
|
if not result:
|
||||||
|
return False
|
||||||
|
return {"match_exception": result}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
from .base import BaseHandler, BaseHandlerMixin
|
from .base import BaseHandler, BaseHandlerMixin
|
||||||
from .callback_query import CallbackQueryHandler
|
from .callback_query import CallbackQueryHandler
|
||||||
from .chosen_inline_result import ChosenInlineResultHandler
|
from .chosen_inline_result import ChosenInlineResultHandler
|
||||||
|
from .error import ErrorHandler
|
||||||
from .inline_query import InlineQueryHandler
|
from .inline_query import InlineQueryHandler
|
||||||
from .message import MessageHandler, MessageHandlerCommandMixin
|
from .message import MessageHandler, MessageHandlerCommandMixin
|
||||||
from .poll import PollHandler
|
from .poll import PollHandler
|
||||||
|
|
@ -12,6 +13,7 @@ __all__ = (
|
||||||
"BaseHandlerMixin",
|
"BaseHandlerMixin",
|
||||||
"CallbackQueryHandler",
|
"CallbackQueryHandler",
|
||||||
"ChosenInlineResultHandler",
|
"ChosenInlineResultHandler",
|
||||||
|
"ErrorHandler",
|
||||||
"InlineQueryHandler",
|
"InlineQueryHandler",
|
||||||
"MessageHandler",
|
"MessageHandler",
|
||||||
"MessageHandlerCommandMixin",
|
"MessageHandlerCommandMixin",
|
||||||
|
|
|
||||||
9
aiogram/dispatcher/handler/error.py
Normal file
9
aiogram/dispatcher/handler/error.py
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
from abc import ABC
|
||||||
|
|
||||||
|
from aiogram.dispatcher.handler.base import BaseHandler
|
||||||
|
|
||||||
|
|
||||||
|
class ErrorHandler(BaseHandler[Exception], ABC):
|
||||||
|
"""
|
||||||
|
Base class for errors handlers
|
||||||
|
"""
|
||||||
29
docs/dispatcher/class_based_handlers/error.md
Normal file
29
docs/dispatcher/class_based_handlers/error.md
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# ErrorHandler
|
||||||
|
|
||||||
|
There is base class for error handlers.
|
||||||
|
|
||||||
|
## Simple usage:
|
||||||
|
```pyhton3
|
||||||
|
from aiogram.handlers import ErrorHandler
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
@router.errors_handler()
|
||||||
|
class MyHandler(ErrorHandler):
|
||||||
|
async def handle(self) -> Any:
|
||||||
|
log.exception(
|
||||||
|
"Cause unexpected exception %s: %s",
|
||||||
|
self.event.__class__.__name__,
|
||||||
|
self.event
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Extension
|
||||||
|
|
||||||
|
This base handler is subclass of [BaseHandler](basics.md#basehandler)
|
||||||
|
|
||||||
|
## Related pages
|
||||||
|
|
||||||
|
- [BaseHandler](basics.md#basehandler)
|
||||||
|
- [Router.errors_handler](../router.md#errors)
|
||||||
|
- [Filters](../filters/exception.md)
|
||||||
27
docs/dispatcher/filters/exception.md
Normal file
27
docs/dispatcher/filters/exception.md
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Exceptions
|
||||||
|
This filters can be helpful for handling errors from the text messages.
|
||||||
|
|
||||||
|
## ExceptionTypeFilter
|
||||||
|
|
||||||
|
Allow to match exception by type
|
||||||
|
|
||||||
|
### Specification
|
||||||
|
| Argument | Type | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `exception` | `#!python3 Union[Type[Exception], Tuple[Type[Exception]]]` | Exception type(s) |
|
||||||
|
|
||||||
|
|
||||||
|
## ExceptionMessageFilter
|
||||||
|
|
||||||
|
Allow to match exception by message
|
||||||
|
|
||||||
|
### Specification
|
||||||
|
| Argument | Type | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `match` | `#!python3 Union[str, Pattern[str]]` | Regexp pattern |
|
||||||
|
|
||||||
|
## Allowed handlers
|
||||||
|
|
||||||
|
Allowed update types for this filters:
|
||||||
|
|
||||||
|
- `error`
|
||||||
|
|
@ -116,6 +116,13 @@ async def poll_answer_handler(poll_answer: types.PollAnswer) -> Any: pass
|
||||||
```
|
```
|
||||||
Is useful for handling [polls answers](../api/types/poll_answer.md)
|
Is useful for handling [polls answers](../api/types/poll_answer.md)
|
||||||
|
|
||||||
|
### Errors
|
||||||
|
```python3
|
||||||
|
@router.errors_handler()
|
||||||
|
async def error_handler(exception: Exception) -> Any: pass
|
||||||
|
```
|
||||||
|
Is useful for handling errors from other handlers
|
||||||
|
|
||||||
|
|
||||||
## Nested routers
|
## Nested routers
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -240,6 +240,7 @@ nav:
|
||||||
- dispatcher/filters/text.md
|
- dispatcher/filters/text.md
|
||||||
- dispatcher/filters/command.md
|
- dispatcher/filters/command.md
|
||||||
- dispatcher/filters/content_types.md
|
- dispatcher/filters/content_types.md
|
||||||
|
- dispatcher/filters/exception.md
|
||||||
- Class based handlers:
|
- Class based handlers:
|
||||||
- dispatcher/class_based_handlers/basics.md
|
- dispatcher/class_based_handlers/basics.md
|
||||||
- dispatcher/class_based_handlers/message.md
|
- dispatcher/class_based_handlers/message.md
|
||||||
|
|
@ -249,6 +250,7 @@ nav:
|
||||||
- dispatcher/class_based_handlers/poll.md
|
- dispatcher/class_based_handlers/poll.md
|
||||||
- dispatcher/class_based_handlers/pre_checkout_query.md
|
- dispatcher/class_based_handlers/pre_checkout_query.md
|
||||||
- dispatcher/class_based_handlers/shipping_query.md
|
- dispatcher/class_based_handlers/shipping_query.md
|
||||||
|
- dispatcher/class_based_handlers/error.md
|
||||||
- Middlewares:
|
- Middlewares:
|
||||||
- dispatcher/middlewares/index.md
|
- dispatcher/middlewares/index.md
|
||||||
- dispatcher/middlewares/basics.md
|
- dispatcher/middlewares/basics.md
|
||||||
|
|
|
||||||
51
tests/test_dispatcher/test_filters/test_exception.py
Normal file
51
tests/test_dispatcher/test_filters/test_exception.py
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import re
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from aiogram.dispatcher.filters import ExceptionMessageFilter, ExceptionTypeFilter
|
||||||
|
|
||||||
|
|
||||||
|
class TestExceptionMessageFilter:
|
||||||
|
@pytest.mark.parametrize("value", ["value", re.compile("value")])
|
||||||
|
def test_converter(self, value):
|
||||||
|
obj = ExceptionMessageFilter(match=value)
|
||||||
|
assert isinstance(obj.match, re.Pattern)
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_match(self):
|
||||||
|
obj = ExceptionMessageFilter(match="KABOOM")
|
||||||
|
|
||||||
|
result = await obj(Exception())
|
||||||
|
assert not result
|
||||||
|
|
||||||
|
result = await obj(Exception("KABOOM"))
|
||||||
|
assert isinstance(result, dict)
|
||||||
|
assert "match_exception" in result
|
||||||
|
|
||||||
|
|
||||||
|
class MyException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MyAnotherException(MyException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TestExceptionTypeFilter:
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"exception,value",
|
||||||
|
[
|
||||||
|
[Exception(), False],
|
||||||
|
[ValueError(), False],
|
||||||
|
[TypeError(), False],
|
||||||
|
[MyException(), True],
|
||||||
|
[MyAnotherException(), True],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_check(self, exception: Exception, value: bool):
|
||||||
|
obj = ExceptionTypeFilter(exception=MyException)
|
||||||
|
|
||||||
|
result = await obj(exception)
|
||||||
|
|
||||||
|
assert result == value
|
||||||
Loading…
Add table
Add a link
Reference in a new issue