Merge branch 'dev-3.x-sphinx' into dev-3.x-api-5.0

# Conflicts:
#	aiogram/client/bot.py
#	poetry.lock
#	pyproject.toml
This commit is contained in:
Alex Root Junior 2020-11-16 23:35:05 +02:00
commit 9539c4e2fd
206 changed files with 6420 additions and 242 deletions

View file

@ -140,13 +140,20 @@ T = TypeVar("T")
class Bot(ContextInstanceMixin["Bot"]):
"""
Main bot class
"""
def __init__(
self, token: str, session: Optional[BaseSession] = None, parse_mode: Optional[str] = None,
) -> None:
"""
Bot class
:param token: Telegram Bot token `Obtained from @BotFather <https://t.me/BotFather>`_
:param session: HTTP Client session (For example AiohttpSession).
If not specified it will be automatically created.
:param parse_mode: Default parse mode.
If specified it will be propagated into the API methods at runtime.
:raise TokenValidationError: When token has invalid format this exception will be raised
"""
validate_token(token)
if session is None:

View file

@ -32,35 +32,63 @@ _JsonDumps = Callable[..., str]
class BaseSession(abc.ABC):
default_timeout: ClassVar[float] = 60.0
api: Default[TelegramAPIServer] = Default(PRODUCTION)
"""Telegra 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"""
@classmethod
def raise_for_status(cls, response: Response[T]) -> None:
"""
Check response status
:param response: Response instance
"""
if response.ok:
return
raise TelegramAPIError(response.description)
@abc.abstractmethod
async def close(self) -> None: # pragma: no cover
"""
Close client session
"""
pass
@abc.abstractmethod
async def make_request(
self, bot: Bot, method: TelegramMethod[T], timeout: Optional[int] = UNSET
) -> T: # pragma: no cover
"""
Make request to Telegram Bot API
:param bot: Bot instance
:param method: Method instance
:param timeout: Request timeout
:return:
:raise TelegramApiError:
"""
pass
@abc.abstractmethod
async def stream_content(
self, url: str, timeout: int, chunk_size: int
) -> AsyncGenerator[bytes, None]: # pragma: no cover
"""
Stream reader
"""
yield b""
def prepare_value(self, value: Any) -> Union[str, int, bool]:
"""
Prepare value before send
"""
if isinstance(value, str):
return value
if isinstance(value, (list, dict)):
@ -74,6 +102,9 @@ class BaseSession(abc.ABC):
return str(value)
def clean_json(self, value: Any) -> Any:
"""
Clean data before send
"""
if isinstance(value, list):
return [self.clean_json(v) for v in value if v is not None]
elif isinstance(value, dict):

View file

@ -8,6 +8,19 @@ from .handler import CallbackType, HandlerObject, HandlerType
class EventObserver:
"""
Simple events observer
Is used for managing events is not related with Telegram (For example startup/shutdown processes)
Handlers can be registered via decorator or method
.. code-block:: python
<observer>.register(my_handler)
.. code-block:: python
@<observer>()
async def my_handler(*args, **kwargs): ...
"""
def __init__(self) -> None:

View file

@ -18,6 +18,9 @@ if TYPE_CHECKING: # pragma: no cover
class TelegramEventObserver:
"""
Event observer for Telegram events
Here you can register handler with filters or bounded filters which can be used as keyword arguments instead of writing full references when you register new handlers.
This observer will stops event propagation when first handler is pass.
"""
def __init__(self, router: Router, event_name: str) -> None:
@ -153,10 +156,19 @@ class TelegramEventObserver:
Decorator for registering inner middlewares
Usage:
>>> @<event>.middleware() # via decorator (variant 1)
>>> @<event>.middleware # via decorator (variant 2)
>>> async def my_middleware(handler, event, data): ...
>>> <event>.middleware(middleware) # via method
.. code-block:: python
@<event>.middleware() # via decorator (variant 1)
.. code-block:: python
@<event>.middleware # via decorator (variant 2)
.. code-block:: python
async def my_middleware(handler, event, data): ...
<event>.middleware(my_middleware) # via method
"""
def wrapper(m: MiddlewareType) -> MiddlewareType:
@ -174,10 +186,19 @@ class TelegramEventObserver:
Decorator for registering outer middlewares
Usage:
>>> @<event>.outer_middleware() # via decorator (variant 1)
>>> @<event>.outer_middleware # via decorator (variant 2)
>>> async def my_middleware(handler, event, data): ...
>>> <event>.outer_middleware(my_middleware) # via method
.. code-block:: python
@<event>.outer_middleware() # via decorator (variant 1)
.. code-block:: python
@<event>.outer_middleware # via decorator (variant 2)
.. code-block:: python
async def my_middleware(handler, event, data): ...
<event>.outer_middleware(my_middleware) # via method
"""
def wrapper(m: MiddlewareType) -> MiddlewareType:

View file

@ -15,10 +15,22 @@ from .middlewares.error import ErrorsMiddleware
class Router:
"""
Events router
Router can route update and it nested update types like messages, callback query, polls and all other event types.
Here is used event-observer pattern.
Event handlers can be registered in observer by two ways:
- By observer method - :obj:`router.<event_type>.register(handler, <filters, ...>)`
- By decorator - :obj:`@router.<event_type>(<filters, ...>)`
"""
def __init__(self, use_builtin_filters: bool = True) -> None:
"""
:param use_builtin_filters: `aiogram` has many builtin filters and you can controll automatic registration of this filters in factory
"""
self.use_builtin_filters = use_builtin_filters
self._parent_router: Optional[Router] = None

View file

@ -1,4 +1,7 @@
from typing_extensions import Protocol
try:
from typing import Protocol
except ImportError: # pragma: no cover
from typing_extensions import Protocol # type: ignore
class Downloadable(Protocol):

View file

@ -38,6 +38,13 @@ class InputFile(ABC):
class BufferedInputFile(InputFile):
def __init__(self, file: bytes, filename: str, chunk_size: int = DEFAULT_CHUNK_SIZE):
"""
Represents object for uploading files from filesystem
:param file: Bytes
:param filename: Filename to be propagated to telegram.
:param chunk_size: Uploading chunk size
"""
super().__init__(filename=filename, chunk_size=chunk_size)
self.data = file
@ -49,6 +56,15 @@ class BufferedInputFile(InputFile):
filename: Optional[str] = None,
chunk_size: int = DEFAULT_CHUNK_SIZE,
) -> BufferedInputFile:
"""
Create buffer from file
:param path: Path to file
:param filename: Filename to be propagated to telegram.
By default will be parsed from path
:param chunk_size: Uploading chunk size
:return: instance of :obj:`BufferedInputFile`
"""
if filename is None:
filename = os.path.basename(path)
with open(path, "rb") as f:
@ -70,6 +86,14 @@ class FSInputFile(InputFile):
filename: Optional[str] = None,
chunk_size: int = DEFAULT_CHUNK_SIZE,
):
"""
Represents object for uploading files from filesystem
:param path: Path to file
:param filename: Filename to be propagated to telegram.
By default will be parsed from path
:param chunk_size: Uploading chunk size
"""
if filename is None:
filename = os.path.basename(path)
super().__init__(filename=filename, chunk_size=chunk_size)
@ -92,6 +116,13 @@ class URLInputFile(InputFile):
chunk_size: int = DEFAULT_CHUNK_SIZE,
timeout: int = 30,
):
"""
Represents object for streaming files from internet
:param url: URL in internet
:param filename: Filename to be propagated to telegram.
:param chunk_size: Uploading chunk size
"""
super().__init__(filename=filename, chunk_size=chunk_size)
self.url = url