Add filters and class based handler for errors

This commit is contained in:
Alex Root Junior 2020-04-12 23:20:44 +03:00
parent 9e673998f0
commit 0fbd2819f9
9 changed files with 167 additions and 1 deletions

View file

@ -3,6 +3,7 @@ from typing import Dict, Tuple, Type
from .base import BaseFilter
from .command import Command, CommandObject
from .content_types import ContentTypesFilter
from .exception import ExceptionMessageFilter, ExceptionTypeFilter
from .text import Text
__all__ = (
@ -12,6 +13,8 @@ __all__ = (
"Command",
"CommandObject",
"ContentTypesFilter",
"ExceptionMessageFilter",
"ExceptionTypeFilter",
)
BUILTIN_FILTERS: Dict[str, Tuple[Type[BaseFilter], ...]] = {
@ -27,5 +30,5 @@ BUILTIN_FILTERS: Dict[str, Tuple[Type[BaseFilter], ...]] = {
"pre_checkout_query": (),
"poll": (),
"poll_answer": (),
"errors": (),
"error": (ExceptionMessageFilter, ExceptionTypeFilter),
}

View 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}

View file

@ -1,6 +1,7 @@
from .base import BaseHandler, BaseHandlerMixin
from .callback_query import CallbackQueryHandler
from .chosen_inline_result import ChosenInlineResultHandler
from .error import ErrorHandler
from .inline_query import InlineQueryHandler
from .message import MessageHandler, MessageHandlerCommandMixin
from .poll import PollHandler
@ -12,6 +13,7 @@ __all__ = (
"BaseHandlerMixin",
"CallbackQueryHandler",
"ChosenInlineResultHandler",
"ErrorHandler",
"InlineQueryHandler",
"MessageHandler",
"MessageHandlerCommandMixin",

View 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
"""

View 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)

View 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`

View file

@ -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)
### Errors
```python3
@router.errors_handler()
async def error_handler(exception: Exception) -> Any: pass
```
Is useful for handling errors from other handlers
## Nested routers

View file

@ -240,6 +240,7 @@ nav:
- dispatcher/filters/text.md
- dispatcher/filters/command.md
- dispatcher/filters/content_types.md
- dispatcher/filters/exception.md
- Class based handlers:
- dispatcher/class_based_handlers/basics.md
- dispatcher/class_based_handlers/message.md
@ -249,6 +250,7 @@ nav:
- dispatcher/class_based_handlers/poll.md
- dispatcher/class_based_handlers/pre_checkout_query.md
- dispatcher/class_based_handlers/shipping_query.md
- dispatcher/class_based_handlers/error.md
- Middlewares:
- dispatcher/middlewares/index.md
- dispatcher/middlewares/basics.md

View 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