feat(timeout):

implement (class-bound, instance-bound, request-bound) session timeout
for requests. fix docs config, fix aiohttp session docs links.
This commit is contained in:
mpa 2020-05-06 02:42:54 +04:00
parent 2adc19724d
commit df4ba87dfc
6 changed files with 55 additions and 16 deletions

View file

@ -132,7 +132,9 @@ class AiohttpSession(BaseSession):
url = self.api.api_url(token=token, method=request.method) url = self.api.api_url(token=token, method=request.method)
form = self.build_form_data(request) form = self.build_form_data(request)
async with session.post(url, data=form, timeout=call.request_timeout) as resp: async with session.post(
url, data=form, timeout=call.request_timeout or self.timeout
) as resp:
raw_result = await resp.json(loads=self.json_loads) raw_result = await resp.json(loads=self.json_loads)
response = call.build_response(raw_result) response = call.build_response(raw_result)

View file

@ -4,7 +4,7 @@ import abc
import datetime import datetime
import json import json
from types import TracebackType from types import TracebackType
from typing import Any, AsyncGenerator, Callable, Optional, Type, TypeVar, Union from typing import Any, AsyncGenerator, Callable, ClassVar, Optional, Type, TypeVar, Union
from aiogram.utils.exceptions import TelegramAPIError from aiogram.utils.exceptions import TelegramAPIError
@ -12,14 +12,18 @@ from ...methods import Response, TelegramMethod
from ..telegram import PRODUCTION, TelegramAPIServer from ..telegram import PRODUCTION, TelegramAPIServer
T = TypeVar("T") T = TypeVar("T")
_JSON_LOADS = Callable[..., Any] _JsonLoads = Callable[..., Any]
_JSON_DUMPS = Callable[..., str] _JsonDumps = Callable[..., str]
class BaseSession(abc.ABC): class BaseSession(abc.ABC):
# global session timeout
default_timeout: ClassVar[float] = 60.0
_api: TelegramAPIServer _api: TelegramAPIServer
_json_loads: _JSON_LOADS _json_loads: _JsonLoads
_json_dumps: _JSON_DUMPS _json_dumps: _JsonDumps
_timeout: float
@property @property
def api(self) -> TelegramAPIServer: def api(self) -> TelegramAPIServer:
@ -30,21 +34,33 @@ class BaseSession(abc.ABC):
self._api = value self._api = value
@property @property
def json_loads(self) -> _JSON_LOADS: def json_loads(self) -> _JsonLoads:
return getattr(self, "_json_loads", json.loads) # type: ignore return getattr(self, "_json_loads", json.loads) # type: ignore
@json_loads.setter @json_loads.setter
def json_loads(self, value: _JSON_LOADS) -> None: def json_loads(self, value: _JsonLoads) -> None:
self._json_loads = value # type: ignore self._json_loads = value # type: ignore
@property @property
def json_dumps(self) -> _JSON_DUMPS: def json_dumps(self) -> _JsonDumps:
return getattr(self, "_json_dumps", json.dumps) # type: ignore return getattr(self, "_json_dumps", json.dumps) # type: ignore
@json_dumps.setter @json_dumps.setter
def json_dumps(self, value: _JSON_DUMPS) -> None: def json_dumps(self, value: _JsonDumps) -> None:
self._json_dumps = value # type: ignore self._json_dumps = value # type: ignore
@property
def timeout(self) -> float:
return getattr(self, "_timeout", self.__class__.default_timeout) # type: ignore
@timeout.setter
def timeout(self, value: float) -> None:
self._timeout = value
@timeout.deleter
def timeout(self) -> None:
del self._timeout
@classmethod @classmethod
def raise_for_status(cls, response: Response[T]) -> None: def raise_for_status(cls, response: Response[T]) -> None:
if response.ok: if response.ok:

View file

@ -13,7 +13,6 @@ if TYPE_CHECKING: # pragma: no cover
from ..client.bot import Bot from ..client.bot import Bot
T = TypeVar("T") T = TypeVar("T")
DEFAULT_REQUEST_TIMEOUT_SECONDS = 60.0
class Request(BaseModel): class Request(BaseModel):
@ -56,7 +55,7 @@ class TelegramMethod(abc.ABC, BaseModel, Generic[T]):
def build_request(self) -> Request: # pragma: no cover def build_request(self) -> Request: # pragma: no cover
pass pass
request_timeout: float = DEFAULT_REQUEST_TIMEOUT_SECONDS request_timeout: Optional[float] = None
def dict(self, **kwargs: Any) -> Any: def dict(self, **kwargs: Any) -> Any:
# override dict of pydantic.BaseModel to overcome exporting request_timeout field # override dict of pydantic.BaseModel to overcome exporting request_timeout field

View file

@ -1,6 +1,6 @@
# Aiohttp session # Aiohttp session
AiohttpSession represents a wrapper-class around `ClientSession` from [aiohttp]('https://pypi.org/project/aiohttp/') AiohttpSession represents a wrapper-class around `ClientSession` from [aiohttp](https://pypi.org/project/aiohttp/ "PyPi repository"){target=_blank}
Currently `AiohttpSession` is a default session used in `aiogram.Bot` Currently `AiohttpSession` is a default session used in `aiogram.Bot`
@ -17,7 +17,7 @@ Bot('token', session=session)
## Proxy requests in AiohttpSession ## Proxy requests in AiohttpSession
In order to use AiohttpSession with proxy connector you have to install [aiohttp-socks]('https://pypi.org/project/aiohttp-socks/') In order to use AiohttpSession with proxy connector you have to install [aiohttp-socks](https://pypi.org/project/aiohttp-socks/ "PyPi repository"){target=_blank}
Binding session to bot: Binding session to bot:
```python ```python

View file

@ -35,6 +35,7 @@ markdown_extensions:
- pymdownx.inlinehilite - pymdownx.inlinehilite
- markdown_include.include: - markdown_include.include:
base_path: docs base_path: docs
- attr_list
nav: nav:
- index.md - index.md

View file

@ -54,12 +54,33 @@ class TestBaseSession:
assert session.json_loads == custom_loads == session._json_loads assert session.json_loads == custom_loads == session._json_loads
different_session = CustomSession() different_session = CustomSession()
assert all(not hasattr(different_session, attr) for attr in ("_json_loads", "_json_dumps", "_api")) assert all(
not hasattr(different_session, attr) for attr in ("_json_loads", "_json_dumps", "_api")
)
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): def test_init_custom_api(self):
api = TelegramAPIServer( api = TelegramAPIServer(
base="http://example.com/{token}/{method}", base="http://example.com/{token}/{method}",
file="http://example.com/{token}/file/{path{", file="http://example.com/{token}/file/{path}",
) )
session = CustomSession() session = CustomSession()
session.api = api session.api = api