Rework session DI and files path wrapper

This commit is contained in:
Alex Root Junior 2021-12-12 18:15:36 +02:00
parent 1357a7a91b
commit 2620a6547c
9 changed files with 98 additions and 40 deletions

1
CHANGES/776.misc Normal file
View file

@ -0,0 +1 @@
Check :code:`destiny` in case of no :code:`with_destiny` enabled in RedisStorage key builder

1
CHANGES/778.misc Normal file
View file

@ -0,0 +1 @@
Stop using feature from #336. From now settings of client-session should be placed as initializer arguments instead of changing instance attributes.

2
CHANGES/779.misc Normal file
View file

@ -0,0 +1,2 @@
Make TelegramAPIServer files wrapper in local mode bi-directional (server-client, client-server)
Now you can convert local path to server path and server path to local path.

View file

@ -270,7 +270,7 @@ class Bot(ContextInstanceMixin["Bot"]):
close_stream = False
if self.session.api.is_local:
stream = self.__aiofiles_reader(
self.session.api.wrap_local_file(file_path), chunk_size=chunk_size
str(self.session.api.wrap_local_file.to_local(file_path)), chunk_size=chunk_size
)
close_stream = True
else:

View file

@ -76,8 +76,8 @@ def _prepare_connector(chain_or_plain: _ProxyType) -> Tuple[Type["TCPConnector"]
class AiohttpSession(BaseSession):
def __init__(self, proxy: Optional[_ProxyType] = None):
super().__init__()
def __init__(self, proxy: Optional[_ProxyType] = None, **kwargs: Any) -> None:
super().__init__(**kwargs)
self._session: Optional[ClientSession] = None
self._connector_type: Type[TCPConnector] = TCPConnector

View file

@ -12,7 +12,7 @@ from typing import (
AsyncGenerator,
Awaitable,
Callable,
ClassVar,
Final,
List,
Optional,
Type,
@ -33,7 +33,6 @@ from aiogram.exceptions import (
TelegramServerError,
TelegramUnauthorizedError,
)
from aiogram.utils.helper import Default
from ...methods import Response, TelegramMethod
from ...methods.base import TelegramType
@ -58,20 +57,29 @@ RequestMiddlewareType = Union[
],
]
DEFAULT_TIMEOUT: Final[float] = 60.0
class BaseSession(abc.ABC):
api: Default[TelegramAPIServer] = Default(PRODUCTION)
"""Telegram Bot API URL patterns"""
json_loads: Default[_JsonLoads] = Default(json.loads)
"""JSON loader"""
json_dumps: Default[_JsonDumps] = Default(json.dumps)
"""JSON dumper"""
default_timeout: ClassVar[float] = 60.0
"""Default timeout"""
timeout: Default[float] = Default(fget=lambda self: float(self.__class__.default_timeout))
"""Session scope request timeout"""
def __init__(
self,
api: TelegramAPIServer = PRODUCTION,
json_loads: _JsonLoads = json.loads,
json_dumps: _JsonDumps = json.dumps,
timeout: float = DEFAULT_TIMEOUT,
) -> None:
"""
:param api: Telegram Bot API URL patterns
:param json_loads: JSON loader
:param json_dumps: JSON dumper
:param timeout: Session scope request timeout
"""
self.api = api
self.json_loads = json_loads
self.json_dumps = json_dumps
self.timeout = timeout
def __init__(self) -> None:
self.middlewares: List[RequestMiddlewareType[TelegramObject]] = []
def check_response(

View file

@ -1,11 +1,45 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Any, Protocol
from pathlib import Path
from typing import Any, Union
class WrapLocalFileCallbackCallbackProtocol(Protocol): # pragma: no cover
def __call__(self, value: str) -> str:
class FilesPathWrapper(ABC):
@abstractmethod
def to_local(self, path: Union[Path, str]) -> Union[Path, str]:
pass
@abstractmethod
def to_server(self, path: Union[Path, str]) -> Union[Path, str]:
pass
class BareFilesPathWrapper(FilesPathWrapper):
def to_local(self, path: Union[Path, str]) -> Union[Path, str]:
return path
def to_server(self, path: Union[Path, str]) -> Union[Path, str]:
return path
class SimpleFilesPathWrapper(FilesPathWrapper):
def __init__(self, server_path: Path, local_path: Path) -> None:
self.server_path = server_path
self.local_path = local_path
@classmethod
def _resolve(
cls, base1: Union[Path, str], base2: Union[Path, str], value: Union[Path, str]
) -> Path:
relative = Path(value).relative_to(base1)
return base2 / relative
def to_local(self, path: Union[Path, str]) -> Union[Path, str]:
return self._resolve(base1=self.server_path, base2=self.local_path, value=path)
def to_server(self, path: Union[Path, str]) -> Union[Path, str]:
return self._resolve(base1=self.local_path, base2=self.server_path, value=path)
@dataclass(frozen=True)
class TelegramAPIServer:
@ -19,7 +53,7 @@ class TelegramAPIServer:
"""Files URL"""
is_local: bool = False
"""Mark this server is in `local mode <https://core.telegram.org/bots/api#using-a-local-bot-api-server>`_."""
wrap_local_file: WrapLocalFileCallbackCallbackProtocol = lambda v: v
wrap_local_file: FilesPathWrapper = BareFilesPathWrapper()
"""Callback to wrap files path in local mode"""
def api_url(self, token: str, method: str) -> str:

View file

@ -1,4 +1,11 @@
from aiogram.client.telegram import PRODUCTION, TelegramAPIServer
from pathlib import Path
from aiogram.client.telegram import (
PRODUCTION,
BareFilesPathWrapper,
SimpleFilesPathWrapper,
TelegramAPIServer,
)
class TestAPIServer:
@ -19,3 +26,27 @@ class TestAPIServer:
assert method_url == "http://localhost:8081/bot42:TEST/apiMethod"
assert file_url == "http://localhost:8081/file/bot42:TEST/path"
assert local_server.is_local
class TestBareFilesPathWrapper:
def test_to_local(self):
wrapper = BareFilesPathWrapper()
assert wrapper.to_local("/path/to/file.dat") == "/path/to/file.dat"
def test_to_server(self):
wrapper = BareFilesPathWrapper()
assert wrapper.to_server("/path/to/file.dat") == "/path/to/file.dat"
class TestSimpleFilesPathWrapper:
def test_to_local(self):
wrapper = SimpleFilesPathWrapper(Path("/etc/telegram-bot-api/data"), Path("/opt/app/data"))
assert wrapper.to_local("/etc/telegram-bot-api/data/documents/file.dat") == Path(
"/opt/app/data/documents/file.dat"
)
def test_to_server(self):
wrapper = SimpleFilesPathWrapper(Path("/etc/telegram-bot-api/data"), Path("/opt/app/data"))
assert wrapper.to_server("/opt/app/data/documents/file.dat") == Path(
"/etc/telegram-bot-api/data/documents/file.dat"
)

View file

@ -74,25 +74,6 @@ class TestBaseSession:
session.json_loads = custom_loads
assert session.json_loads == custom_loads
def test_timeout(self):
session = CustomSession()
assert session.timeout == session.default_timeout == CustomSession.default_timeout
session.default_timeout = float(65.0_0) # mypy will complain
assert session.timeout != session.default_timeout
CustomSession.default_timeout = float(68.0_0)
assert session.timeout == CustomSession.default_timeout
session.timeout = float(71.0_0)
assert session.timeout != session.default_timeout
del session.timeout
CustomSession.default_timeout = session.default_timeout + 100
assert (
session.timeout != BaseSession.default_timeout
and session.timeout == CustomSession.default_timeout
)
def test_init_custom_api(self):
api = TelegramAPIServer(
base="http://example.com/{token}/{method}",