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