Extended MagicFilter with aiogram-specific operation (#759)

* Extend MagicFilter with aiogram-specific operation

* Added tests

* Added changes annotation and update docs
This commit is contained in:
Alex Root Junior 2021-11-24 06:00:37 +02:00 committed by GitHub
parent 092b3f06f2
commit d7be55bc58
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 85 additions and 11 deletions

13
CHANGES/759.feature Normal file
View file

@ -0,0 +1,13 @@
Added new custom operation for MagicFilter named :code:`as_`
Now you can use it to get magic filter result as handler argument
.. code-block:: python
from aiogram import F
...
@router.message(F.text.regexp(r"^(\d+)$").as_("digits"))
async def any_digits_handler(message: Message, digits: Match[str]):
await message.answer(html.quote(str(digits)))

View file

@ -1,11 +1,10 @@
from magic_filter import MagicFilter
from .client import session
from .client.bot import Bot
from .dispatcher import filters, handler
from .dispatcher.dispatcher import Dispatcher
from .dispatcher.middlewares.base import BaseMiddleware
from .dispatcher.router import Router
from .utils.magic_filter import MagicFilter
from .utils.text_decorations import html_decoration as _html_decoration
from .utils.text_decorations import markdown_decoration as _markdown_decoration

View file

@ -0,0 +1,21 @@
from typing import Any
from magic_filter import MagicFilter as _MagicFilter
from magic_filter import MagicT as _MagicT
from magic_filter.operations import BaseOperation
class AsFilterResultOperation(BaseOperation):
__slots__ = ("name",)
def __init__(self, name: str) -> None:
self.name = name
def resolve(self, value: Any, initial_value: Any) -> Any:
if value:
return {self.name: value}
class MagicFilter(_MagicFilter):
def as_(self: _MagicT, name: str) -> _MagicT:
return self._extend(AsFilterResultOperation(name=name))

View file

@ -16,7 +16,11 @@ That's mean you can install it and use with any other libraries and in own proje
Usage
=====
The **magic_filter** package implements class shortly named :class:`magic_filter.F` that's mean :code:`F` can be imported from :code:`magic_filter`. :class:`F` is alias for :class:`MagicFilter`.
The **magic_filter** package implements class shortly named :class:`magic_filter.F` that's mean :code:`F` can be imported from :code:`aiogram` or :code:`magic_filter`. :class:`F` is alias for :class:`MagicFilter`.
.. note::
Note that *aiogram* has an small extension over magic-filter and if you want to use this extension you should import magic from *aiogram* instead of *magic_filter* package
The :class:`MagicFilter` object is callable, supports :ref:`some actions <magic-filter-possible-actions>`
and memorize the attributes chain and the action which should be checked on demand.
@ -130,6 +134,21 @@ Can be used only with string attributes.
F.text.len() == 5 # lambda message: len(message.text) == 5
Get filter result as handler argument
-------------------------------------
This part is not available in *magic-filter* directly but can be used with *aiogram*
.. code-block:: python
from aiogram import F
...
@router.message(F.text.regexp(r"^(\d+)$").as_("digits"))
async def any_digits_handler(message: Message, digits: Match[str]):
await message.answer(html.quote(str(digits)))
Usage in *aiogram*
==================

15
poetry.lock generated
View file

@ -513,7 +513,7 @@ tornado = {version = "*", markers = "python_version > \"2.7\""}
[[package]]
name = "magic-filter"
version = "1.0.3"
version = "1.0.4"
description = "This package provides magic filter based on dynamic attribute getter"
category = "main"
optional = false
@ -917,7 +917,7 @@ pytest = ">=3.5"
[[package]]
name = "python-socks"
version = "1.2.4"
version = "2.0.0"
description = "Core proxy (SOCKS4, SOCKS5, HTTP tunneling) functionality for Python"
category = "main"
optional = true
@ -927,6 +927,7 @@ python-versions = "*"
async-timeout = {version = ">=3.0.1", optional = true, markers = "extra == \"asyncio\""}
[package.extras]
anyio = ["anyio (>=3.3.4)"]
asyncio = ["async-timeout (>=3.0.1)"]
curio = ["curio (>=1.4)"]
trio = ["trio (>=0.16.0)"]
@ -1323,7 +1324,7 @@ redis = ["aioredis"]
[metadata]
lock-version = "1.1"
python-versions = "^3.8"
content-hash = "de8ae82fb5f86da3377cc87aab35239ec5baddac4e0151595b5c4f6152d95ac8"
content-hash = "27d602728b6dab256d184fbd44953030676edf320652ad828c6e1b4beaa80f8b"
[metadata.files]
aiofiles = [
@ -1681,8 +1682,8 @@ livereload = [
{file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"},
]
magic-filter = [
{file = "magic-filter-1.0.3.tar.gz", hash = "sha256:f39c1a27bdbdf5bec2e9ccfc4269557f9812345559ddd656926220f28921492b"},
{file = "magic_filter-1.0.3-py3-none-any.whl", hash = "sha256:e1139dad0b0f6426894d3fd214fb7d5bb1f964e8748141cb902414020e5ceb71"},
{file = "magic-filter-1.0.4.tar.gz", hash = "sha256:2b77de98bfef16a990c22b2a68a904b4a99913b656d48cf877367da8ae5f5c84"},
{file = "magic_filter-1.0.4-py3-none-any.whl", hash = "sha256:daf869025b9490c4438f1c2d3f4f9bcb8295fa0ffa7fec643bc20486f519f349"},
]
markdown = [
{file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"},
@ -2002,8 +2003,8 @@ pytest-mypy = [
{file = "pytest_mypy-0.8.1-py3-none-any.whl", hash = "sha256:6e68e8eb7ceeb7d1c83a1590912f784879f037b51adfb9c17b95c6b2fc57466b"},
]
python-socks = [
{file = "python-socks-1.2.4.tar.gz", hash = "sha256:7d0ef2578cead9f762b71317d25a6c118fabaf79535555e75b3e102f5158ddd8"},
{file = "python_socks-1.2.4-py3-none-any.whl", hash = "sha256:9f12e8fe78629b87543fad0e4ea0ccf103a4fad6a7872c5d0ecb36d9903fa548"},
{file = "python-socks-2.0.0.tar.gz", hash = "sha256:7944dad882846ac73e5f79e180c841e3895ee058e16855b7e8fff24f4cd0b90b"},
{file = "python_socks-2.0.0-py3-none-any.whl", hash = "sha256:aac65671cbd3b0eb55b20f8558c8de3894a315536aaab3ec0a7b9d46ff89c1bf"},
]
pytz = [
{file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"},

View file

@ -38,7 +38,7 @@ classifiers = [
[tool.poetry.dependencies]
python = "^3.8"
magic-filter = "^1.0.3"
magic-filter = "^1.0.4"
aiohttp = "^3.8.0"
pydantic = "^1.8.2"
aiofiles = "^0.7.0"

View file

@ -0,0 +1,21 @@
from dataclasses import dataclass
from re import Match
from aiogram import F
from aiogram.utils.magic_filter import MagicFilter
@dataclass
class MyObject:
text: str
class TestMagicFilter:
def test_operation_as(self):
magic: MagicFilter = F.text.regexp(r"^(\d+)$").as_("match")
assert not magic.resolve(MyObject(text="test"))
result = magic.resolve(MyObject(text="123"))
assert isinstance(result, dict)
assert isinstance(result["match"], Match)