mirror of
https://github.com/aiogram/aiogram.git
synced 2025-12-12 10:11:52 +00:00
Add possibility to include router via string
This commit is contained in:
parent
b943ea2207
commit
dadedc80a9
6 changed files with 86 additions and 3 deletions
2
Makefile
2
Makefile
|
|
@ -104,7 +104,7 @@ test:
|
||||||
.PHONY: test-coverage
|
.PHONY: test-coverage
|
||||||
test-coverage:
|
test-coverage:
|
||||||
mkdir -p reports/tests/
|
mkdir -p reports/tests/
|
||||||
$(py) pytest --cov=aiogram --cov-config .coveragerc --html=reports/tests/index.html tests/
|
$(py) pytest --cov=aiogram --cov-config .coveragerc --html=reports/tests/index.html -p no:warnings tests/
|
||||||
$(py) coverage html -d reports/coverage
|
$(py) coverage html -d reports/coverage
|
||||||
|
|
||||||
# =================================================================================================
|
# =================================================================================================
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional, Union
|
||||||
|
|
||||||
from ..api.types import Chat, Update, User
|
from ..api.types import Chat, Update, User
|
||||||
|
from ..utils.imports import import_module
|
||||||
from .event.observer import EventObserver, SkipHandler, TelegramEventObserver
|
from .event.observer import EventObserver, SkipHandler, TelegramEventObserver
|
||||||
from .filters import BUILTIN_FILTERS
|
from .filters import BUILTIN_FILTERS
|
||||||
|
|
||||||
|
|
@ -76,7 +77,13 @@ class Router:
|
||||||
|
|
||||||
self._parent_router = router
|
self._parent_router = router
|
||||||
|
|
||||||
def include_router(self, router: Router) -> Router:
|
def include_router(self, router: Union[Router, str]) -> Router:
|
||||||
|
if isinstance(router, str):
|
||||||
|
router = import_module(router)
|
||||||
|
if not isinstance(router, Router):
|
||||||
|
raise ValueError(
|
||||||
|
f"router should be instance of Router not {type(router).__class__.__name__}"
|
||||||
|
)
|
||||||
router.parent_router = self
|
router.parent_router = self
|
||||||
self.sub_routers.append(router)
|
self.sub_routers.append(router)
|
||||||
return router
|
return router
|
||||||
|
|
|
||||||
23
aiogram/utils/imports.py
Normal file
23
aiogram/utils/imports.py
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import importlib
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
def import_module(target: str) -> Any:
|
||||||
|
if not isinstance(target, str):
|
||||||
|
raise ValueError(f"Target should be string not {type(target).__class__.__name__}")
|
||||||
|
|
||||||
|
module_name, _, attr_name = target.partition(":")
|
||||||
|
if not module_name or not attr_name:
|
||||||
|
raise ValueError(f'Import string "{target}" must be in format "<module>:<attribute>"')
|
||||||
|
|
||||||
|
try:
|
||||||
|
module = importlib.import_module(module_name)
|
||||||
|
except ImportError:
|
||||||
|
raise ValueError(f'Could not import module "{module_name}".')
|
||||||
|
|
||||||
|
try:
|
||||||
|
attribute = getattr(module, attr_name)
|
||||||
|
except AttributeError:
|
||||||
|
raise ValueError(f'Module "{module_name}" has no attribute "{attr_name}"')
|
||||||
|
|
||||||
|
return attribute
|
||||||
13
handlers.py
Normal file
13
handlers.py
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from aiogram import Router
|
||||||
|
from aiogram.api.methods import SendMessage
|
||||||
|
from aiogram.dispatcher.handler.message import MessageHandler
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
|
||||||
|
@router.message_handler(commands=["test"])
|
||||||
|
class MyHandler(MessageHandler):
|
||||||
|
async def handle(self) -> Any:
|
||||||
|
return SendMessage(chat_id=self.chat.id, text="<b>PASS</b>")
|
||||||
|
|
@ -20,6 +20,8 @@ from aiogram.api.types import (
|
||||||
from aiogram.dispatcher.event.observer import SkipHandler
|
from aiogram.dispatcher.event.observer import SkipHandler
|
||||||
from aiogram.dispatcher.router import Router
|
from aiogram.dispatcher.router import Router
|
||||||
|
|
||||||
|
importable_router = Router()
|
||||||
|
|
||||||
|
|
||||||
class TestRouter:
|
class TestRouter:
|
||||||
def test_including_routers(self):
|
def test_including_routers(self):
|
||||||
|
|
@ -50,6 +52,15 @@ class TestRouter:
|
||||||
assert router3.parent_router is router2
|
assert router3.parent_router is router2
|
||||||
assert router3.sub_routers == []
|
assert router3.sub_routers == []
|
||||||
|
|
||||||
|
def test_include_router_by_string(self):
|
||||||
|
router = Router()
|
||||||
|
router.include_router("tests.test_dispatcher.test_router:importable_router")
|
||||||
|
|
||||||
|
def test_include_router_by_string_bad_type(self):
|
||||||
|
router = Router()
|
||||||
|
with pytest.raises(ValueError, match=r"router should be instance of Router"):
|
||||||
|
router.include_router("tests.test_dispatcher.test_router:TestRouter")
|
||||||
|
|
||||||
def test_observers_config(self):
|
def test_observers_config(self):
|
||||||
router = Router()
|
router = Router()
|
||||||
assert router.update_handler.handlers
|
assert router.update_handler.handlers
|
||||||
|
|
|
||||||
29
tests/test_utils/test_imports.py
Normal file
29
tests/test_utils/test_imports.py
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import aiogram
|
||||||
|
from aiogram.utils.imports import import_module
|
||||||
|
|
||||||
|
|
||||||
|
class TestImports:
|
||||||
|
def test_bad_type(self):
|
||||||
|
with pytest.raises(ValueError, match=r"Target should be string not"):
|
||||||
|
import_module(42)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("value", ["module", "module:", ":attribute"])
|
||||||
|
def test_bad_format(self, value):
|
||||||
|
with pytest.raises(ValueError, match='must be in format "<module>:<attribute>"'):
|
||||||
|
import_module(value)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("value", ["module", "aiogram.KABOOM", "aiogram.KABOOM.TEST"])
|
||||||
|
def test_bad_value(self, value):
|
||||||
|
with pytest.raises(ValueError, match="Could not import module"):
|
||||||
|
import_module(f"{value}:attribute")
|
||||||
|
|
||||||
|
def test_has_no_attribute(self):
|
||||||
|
with pytest.raises(ValueError, match="has no attribute"):
|
||||||
|
import_module("aiogram:KABOOM")
|
||||||
|
|
||||||
|
def test_imported(self):
|
||||||
|
value = import_module("aiogram:__version__")
|
||||||
|
isinstance(value, str)
|
||||||
|
assert value == aiogram.__version__
|
||||||
Loading…
Add table
Add a link
Reference in a new issue