mirror of
https://github.com/aiogram/aiogram.git
synced 2025-12-06 07:50:32 +00:00
💩 First iteration
This commit is contained in:
parent
0bd7fc2c7e
commit
fac69e52b7
25 changed files with 427 additions and 273 deletions
|
|
@ -1,9 +1,17 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import Any, Optional, TypeVar
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Optional,
|
||||
TypeVar,
|
||||
)
|
||||
|
||||
from ...utils.mixins import ContextInstanceMixin, DataMixin
|
||||
from ...utils.mixins import (
|
||||
ContextInstance,
|
||||
ContextInstanceMixin,
|
||||
)
|
||||
from ...utils.token import extract_bot_id, validate_token
|
||||
from ..methods import TelegramMethod
|
||||
from .session.aiohttp import AiohttpSession
|
||||
|
|
@ -12,13 +20,13 @@ from .session.base import BaseSession
|
|||
T = TypeVar("T")
|
||||
|
||||
|
||||
class BaseBot(ContextInstanceMixin, DataMixin):
|
||||
class BaseBot(ContextInstanceMixin[ContextInstance]):
|
||||
"""
|
||||
Base class for bots
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, token: str, session: BaseSession = None, parse_mode: Optional[str] = None
|
||||
self, token: str, session: Optional[BaseSession] = None, parse_mode: Optional[str] = None
|
||||
) -> None:
|
||||
validate_token(token)
|
||||
|
||||
|
|
@ -54,14 +62,15 @@ class BaseBot(ContextInstanceMixin, DataMixin):
|
|||
await self.session.close()
|
||||
|
||||
@asynccontextmanager
|
||||
async def context(self, auto_close: bool = True):
|
||||
async def context(self, auto_close: bool = True) -> AsyncIterator["BaseBot[ContextInstance]"]:
|
||||
"""
|
||||
Generate bot context
|
||||
|
||||
:param auto_close:
|
||||
:return:
|
||||
"""
|
||||
token = self.set_current(self)
|
||||
# TODO: because set_current expects Bot, not BaseBot — this check fails
|
||||
token = self.set_current(self) # type: ignore
|
||||
try:
|
||||
yield self
|
||||
finally:
|
||||
|
|
|
|||
|
|
@ -101,12 +101,12 @@ from ..types import (
|
|||
from .base import BaseBot
|
||||
|
||||
|
||||
class Bot(BaseBot):
|
||||
class Bot(BaseBot["Bot"]):
|
||||
"""
|
||||
Class where located all API methods
|
||||
"""
|
||||
|
||||
@alru_cache()
|
||||
@alru_cache() # type: ignore
|
||||
async def me(self) -> User:
|
||||
return await self.get_me()
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ class AiohttpSession(BaseSession):
|
|||
def __init__(
|
||||
self,
|
||||
api: TelegramAPIServer = PRODUCTION,
|
||||
json_loads: Optional[Callable] = None,
|
||||
json_dumps: Optional[Callable] = None,
|
||||
json_loads: Optional[Callable[..., str]] = None,
|
||||
json_dumps: Optional[Callable[..., str]] = None,
|
||||
):
|
||||
super(AiohttpSession, self).__init__(api=api, json_loads=json_loads, json_dumps=json_dumps)
|
||||
self._session: Optional[ClientSession] = None
|
||||
|
|
@ -27,11 +27,11 @@ class AiohttpSession(BaseSession):
|
|||
|
||||
return self._session
|
||||
|
||||
async def close(self):
|
||||
async def close(self) -> None:
|
||||
if self._session is not None and not self._session.closed:
|
||||
await self._session.close()
|
||||
|
||||
def build_form_data(self, request: Request):
|
||||
def build_form_data(self, request: Request) -> FormData:
|
||||
form = FormData(quote_fields=False)
|
||||
for key, value in request.data.items():
|
||||
if value is None:
|
||||
|
|
|
|||
|
|
@ -3,7 +3,16 @@ from __future__ import annotations
|
|||
import abc
|
||||
import datetime
|
||||
import json
|
||||
from typing import Any, AsyncGenerator, Callable, Optional, TypeVar, Union
|
||||
from types import TracebackType
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncGenerator,
|
||||
Callable,
|
||||
Optional,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
)
|
||||
|
||||
from aiogram.utils.exceptions import TelegramAPIError
|
||||
|
||||
|
|
@ -17,8 +26,8 @@ class BaseSession(abc.ABC):
|
|||
def __init__(
|
||||
self,
|
||||
api: Optional[TelegramAPIServer] = None,
|
||||
json_loads: Optional[Callable[[Any], Any]] = None,
|
||||
json_dumps: Optional[Callable[[Any], Any]] = None,
|
||||
json_loads: Optional[Callable[..., str]] = None,
|
||||
json_dumps: Optional[Callable[..., str]] = None,
|
||||
) -> None:
|
||||
if api is None:
|
||||
api = PRODUCTION
|
||||
|
|
@ -37,7 +46,7 @@ class BaseSession(abc.ABC):
|
|||
raise TelegramAPIError(response.description)
|
||||
|
||||
@abc.abstractmethod
|
||||
async def close(self): # pragma: no cover
|
||||
async def close(self) -> None: # pragma: no cover
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
|
|
@ -73,5 +82,10 @@ class BaseSession(abc.ABC):
|
|||
async def __aenter__(self) -> BaseSession:
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_value: Optional[BaseException],
|
||||
traceback: Optional[TracebackType],
|
||||
) -> None:
|
||||
await self.close()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,16 @@ from __future__ import annotations
|
|||
|
||||
import abc
|
||||
import secrets
|
||||
from typing import TYPE_CHECKING, Any, Dict, Generic, Optional, Type, TypeVar, Union
|
||||
from typing import (
|
||||
Generator,
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Dict,
|
||||
Generic,
|
||||
Optional,
|
||||
TypeVar,
|
||||
Union,
|
||||
)
|
||||
|
||||
from pydantic import BaseConfig, BaseModel, Extra
|
||||
from pydantic.generics import GenericModel
|
||||
|
|
@ -24,7 +33,7 @@ class Request(BaseModel):
|
|||
class Config(BaseConfig):
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
def render_webhook_request(self):
|
||||
def render_webhook_request(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"method": self.method,
|
||||
**{key: value for key, value in self.data.items() if value is not None},
|
||||
|
|
@ -48,7 +57,7 @@ class TelegramMethod(abc.ABC, BaseModel, Generic[T]):
|
|||
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def __returning__(self) -> Type: # pragma: no cover
|
||||
def __returning__(self) -> type: # pragma: no cover
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
|
|
@ -62,14 +71,14 @@ class TelegramMethod(abc.ABC, BaseModel, Generic[T]):
|
|||
async def emit(self, bot: Bot) -> T:
|
||||
return await bot(self)
|
||||
|
||||
def __await__(self):
|
||||
def __await__(self) -> Generator[Any, None, T]:
|
||||
from aiogram.api.client.bot import Bot
|
||||
|
||||
bot = Bot.get_current(no_error=False)
|
||||
return self.emit(bot).__await__()
|
||||
|
||||
|
||||
def prepare_file(name: str, value: Any, data: Dict[str, Any], files: Dict[str, Any]):
|
||||
def prepare_file(name: str, value: Any, data: Dict[str, Any], files: Dict[str, Any]) -> None:
|
||||
if not value:
|
||||
return
|
||||
if name == "thumb":
|
||||
|
|
@ -101,7 +110,7 @@ def prepare_media_file(data: Dict[str, Any], files: Dict[str, InputFile]) -> Non
|
|||
and isinstance(data["media"]["media"], InputFile)
|
||||
):
|
||||
tag = secrets.token_urlsafe(10)
|
||||
files[tag] = data["media"].pop("media") # type: ignore
|
||||
files[tag] = data["media"].pop("media")
|
||||
data["media"]["media"] = f"attach://{tag}"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from pydantic import BaseModel, Extra
|
|||
from aiogram.utils.mixins import ContextInstanceMixin
|
||||
|
||||
|
||||
class TelegramObject(ContextInstanceMixin, BaseModel):
|
||||
class TelegramObject(ContextInstanceMixin["TelegramObject"], BaseModel):
|
||||
class Config:
|
||||
use_enum_values = True
|
||||
orm_mode = True
|
||||
|
|
|
|||
|
|
@ -4,7 +4,13 @@ import io
|
|||
import os
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
from typing import AsyncGenerator, Optional, Union
|
||||
from typing import (
|
||||
AsyncGenerator,
|
||||
AsyncIterator,
|
||||
Iterator,
|
||||
Optional,
|
||||
Union,
|
||||
)
|
||||
|
||||
import aiofiles as aiofiles
|
||||
|
||||
|
|
@ -24,14 +30,14 @@ class InputFile(ABC):
|
|||
self.chunk_size = chunk_size
|
||||
|
||||
@classmethod
|
||||
def __get_validators__(cls):
|
||||
yield
|
||||
def __get_validators__(cls) -> Iterator[None]:
|
||||
yield None
|
||||
|
||||
@abstractmethod
|
||||
async def read(self, chunk_size: int) -> AsyncGenerator[bytes, None]: # pragma: no cover
|
||||
yield b""
|
||||
|
||||
async def __aiter__(self):
|
||||
async def __aiter__(self) -> AsyncIterator[bytes]:
|
||||
async for chunk in self.read(self.chunk_size):
|
||||
yield chunk
|
||||
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ class Message(TelegramObject):
|
|||
buttons."""
|
||||
|
||||
@property
|
||||
def content_type(self):
|
||||
def content_type(self) -> str:
|
||||
if self.text:
|
||||
return ContentType.TEXT
|
||||
if self.audio:
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class User(TelegramObject):
|
|||
"""True, if the bot supports inline queries. Returned only in getMe."""
|
||||
|
||||
@property
|
||||
def full_name(self):
|
||||
def full_name(self) -> str:
|
||||
if self.last_name:
|
||||
return f"{self.first_name} {self.last_name}"
|
||||
return self.first_name
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import contextvars
|
||||
import warnings
|
||||
|
|
@ -96,7 +98,7 @@ class Dispatcher(Router):
|
|||
update_id = update.update_id + 1
|
||||
|
||||
@classmethod
|
||||
async def _silent_call_request(cls, bot: Bot, result: TelegramMethod) -> None:
|
||||
async def _silent_call_request(cls, bot: Bot, result: TelegramMethod[Any]) -> None:
|
||||
"""
|
||||
Simulate answer into WebHook
|
||||
|
||||
|
|
@ -172,7 +174,7 @@ class Dispatcher(Router):
|
|||
raise
|
||||
|
||||
async def feed_webhook_update(
|
||||
self, bot: Bot, update: Union[Update, Dict[str, Any]], _timeout: int = 55, **kwargs
|
||||
self, bot: Bot, update: Union[Update, Dict[str, Any]], _timeout: int = 55, **kwargs: Any
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
if not isinstance(update, Update): # Allow to use raw updates
|
||||
update = Update(**update)
|
||||
|
|
@ -181,18 +183,18 @@ class Dispatcher(Router):
|
|||
loop = asyncio.get_running_loop()
|
||||
waiter = loop.create_future()
|
||||
|
||||
def release_waiter(*args: Any):
|
||||
def release_waiter(*args: Any) -> None:
|
||||
if not waiter.done():
|
||||
waiter.set_result(None)
|
||||
|
||||
timeout_handle = loop.call_later(_timeout, release_waiter)
|
||||
|
||||
process_updates: Future = asyncio.ensure_future(
|
||||
process_updates: Future[Any] = asyncio.ensure_future(
|
||||
self._feed_webhook_update(bot=bot, update=update, **kwargs)
|
||||
)
|
||||
process_updates.add_done_callback(release_waiter, context=ctx)
|
||||
|
||||
def process_response(task: Future):
|
||||
def process_response(task: Future[Any]) -> None:
|
||||
warnings.warn(
|
||||
f"Detected slow response into webhook.\n"
|
||||
f"Telegram is waiting for response only first 60 seconds and then re-send update.\n"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,16 @@
|
|||
import inspect
|
||||
from dataclasses import dataclass, field
|
||||
from functools import partial
|
||||
from typing import Any, Awaitable, Callable, Dict, List, Optional, Tuple, Union
|
||||
from typing import (
|
||||
Any,
|
||||
Awaitable,
|
||||
Callable,
|
||||
Dict,
|
||||
List,
|
||||
Optional,
|
||||
Tuple,
|
||||
Union,
|
||||
)
|
||||
|
||||
from aiogram.dispatcher.filters.base import BaseFilter
|
||||
from aiogram.dispatcher.handler.base import BaseHandler
|
||||
|
|
@ -19,21 +28,22 @@ class CallableMixin:
|
|||
awaitable: bool = field(init=False)
|
||||
spec: inspect.FullArgSpec = field(init=False)
|
||||
|
||||
def __post_init__(self):
|
||||
def __post_init__(self) -> None:
|
||||
callback = self.callback
|
||||
self.awaitable = inspect.isawaitable(callback) or inspect.iscoroutinefunction(callback)
|
||||
while hasattr(callback, "__wrapped__"): # Try to resolve decorated callbacks
|
||||
callback = callback.__wrapped__
|
||||
callback = callback.__wrapped__ # type: ignore
|
||||
self.spec = inspect.getfullargspec(callback)
|
||||
|
||||
def _prepare_kwargs(self, kwargs):
|
||||
def _prepare_kwargs(self, kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
||||
if self.spec.varkw:
|
||||
return kwargs
|
||||
|
||||
return {k: v for k, v in kwargs.items() if k in self.spec.args}
|
||||
|
||||
async def call(self, *args, **kwargs):
|
||||
wrapped = partial(self.callback, *args, **self._prepare_kwargs(kwargs))
|
||||
async def call(self, *args: Any, **kwargs: Any) -> Any:
|
||||
# TODO: what we should do if callback is BaseHandler?
|
||||
wrapped = partial(self.callback, *args, **self._prepare_kwargs(kwargs)) # type: ignore
|
||||
if self.awaitable:
|
||||
return await wrapped()
|
||||
return wrapped()
|
||||
|
|
@ -49,10 +59,10 @@ class HandlerObject(CallableMixin):
|
|||
callback: HandlerType
|
||||
filters: Optional[List[FilterObject]] = None
|
||||
|
||||
def __post_init__(self):
|
||||
def __post_init__(self) -> None:
|
||||
super(HandlerObject, self).__post_init__()
|
||||
|
||||
if inspect.isclass(self.callback) and issubclass(self.callback, BaseHandler):
|
||||
# TODO: by types callback must be Callable or BaseHandler, not Type[BaseHandler]
|
||||
if inspect.isclass(self.callback) and issubclass(self.callback, BaseHandler): # type: ignore
|
||||
self.awaitable = True
|
||||
|
||||
async def check(self, *args: Any, **kwargs: Any) -> Tuple[bool, Dict[str, Any]]:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
from typing import Dict, Tuple, Union
|
||||
from typing import (
|
||||
Dict,
|
||||
Tuple,
|
||||
Type,
|
||||
)
|
||||
|
||||
from .base import BaseFilter
|
||||
from .command import Command, CommandObject
|
||||
|
|
@ -14,7 +18,7 @@ __all__ = (
|
|||
"ContentTypesFilter",
|
||||
)
|
||||
|
||||
BUILTIN_FILTERS: Dict[str, Union[Tuple[BaseFilter], Tuple]] = {
|
||||
BUILTIN_FILTERS: Dict[str, Tuple[Type[BaseFilter], ...]] = {
|
||||
"update": (),
|
||||
"message": (Text, Command, ContentTypesFilter),
|
||||
"edited_message": (Text, Command, ContentTypesFilter),
|
||||
|
|
|
|||
|
|
@ -1,22 +1,26 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING, Any, Dict, Union
|
||||
from typing import (
|
||||
Awaitable,
|
||||
Callable,
|
||||
Any,
|
||||
Dict,
|
||||
Union,
|
||||
)
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
async def _call_for_override(*args: Any, **kwargs: Any) -> Union[bool, Dict[str, Any]]:
|
||||
pass
|
||||
|
||||
|
||||
class BaseFilter(ABC, BaseModel):
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
# This checking type-hint is needed because mypy checks validity of overrides and raises:
|
||||
# error: Signature of "__call__" incompatible with supertype "BaseFilter" [override]
|
||||
# https://mypy.readthedocs.io/en/latest/error_code_list.html#check-validity-of-overrides-override
|
||||
# This little hack with typehint is needed because mypy checks validity of overrides and raises:
|
||||
# error: Signature of "__call__" incompatible with supertype "BaseFilter" [override]
|
||||
# https://mypy.readthedocs.io/en/latest/error_code_list.html#check-validity-of-overrides-override
|
||||
__call__: Callable[..., Awaitable[Union[bool, Dict[str, Any]]]] = _call_for_override
|
||||
abstractmethod(__call__)
|
||||
|
||||
pass
|
||||
else: # pragma: no cover
|
||||
|
||||
@abstractmethod
|
||||
async def __call__(self, *args: Any, **kwargs: Any) -> Union[bool, Dict[str, Any]]:
|
||||
pass
|
||||
|
||||
def __await__(self): # pragma: no cover
|
||||
def __await__(self): # type: ignore # pragma: no cover
|
||||
# Is needed only for inspection and this method is never be called
|
||||
return self.__call__
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from aiogram import Bot
|
|||
from aiogram.api.types import Message
|
||||
from aiogram.dispatcher.filters import BaseFilter
|
||||
|
||||
CommandPatterType = Union[str, re.Pattern] # type: ignore
|
||||
CommandPatterType = Union[str, re.Pattern]
|
||||
|
||||
|
||||
class Command(BaseFilter):
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ class Text(BaseFilter):
|
|||
# Impossible because the validator prevents this situation
|
||||
return False # pragma: no cover
|
||||
|
||||
def prepare_text(self, text: str):
|
||||
def prepare_text(self, text: str) -> str:
|
||||
if self.text_ignore_case:
|
||||
return str(text).lower()
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING, Any, Dict, Generic, TypeVar
|
||||
from typing import (
|
||||
Optional,
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Dict,
|
||||
Generic,
|
||||
TypeVar,
|
||||
cast,
|
||||
)
|
||||
|
||||
from aiogram import Bot
|
||||
from aiogram.api.types import Update
|
||||
|
|
@ -23,18 +31,20 @@ class BaseHandler(BaseHandlerMixin[T], ABC):
|
|||
self.data: Dict[str, Any] = kwargs
|
||||
|
||||
@property
|
||||
def bot(self) -> Bot:
|
||||
def bot(self) -> Optional[Bot]:
|
||||
if "bot" in self.data:
|
||||
return self.data["bot"]
|
||||
# TODO: remove cast
|
||||
return cast(Bot, self.data["bot"])
|
||||
return Bot.get_current()
|
||||
|
||||
@property
|
||||
def update(self) -> Update:
|
||||
return self.data["update"]
|
||||
# TODO: remove cast
|
||||
return cast(Update, self.data["update"])
|
||||
|
||||
@abstractmethod
|
||||
async def handle(self) -> Any: # pragma: no cover
|
||||
pass
|
||||
|
||||
def __await__(self):
|
||||
def __await__(self) -> Any:
|
||||
return self.handle().__await__()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
from abc import ABC
|
||||
from typing import Optional
|
||||
from typing import (
|
||||
Optional,
|
||||
cast,
|
||||
)
|
||||
|
||||
from aiogram.api.types import Chat, Message, User
|
||||
from aiogram.dispatcher.filters import CommandObject
|
||||
|
|
@ -24,5 +27,6 @@ class MessageHandlerCommandMixin(BaseHandlerMixin[Message]):
|
|||
@property
|
||||
def command(self) -> Optional[CommandObject]:
|
||||
if "command" in self.data:
|
||||
return self.data["command"]
|
||||
# TODO: remove cast
|
||||
return cast(CommandObject, self.data["command"])
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -3,7 +3,12 @@ from __future__ import annotations
|
|||
import warnings
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
from ..api.types import Chat, Update, User
|
||||
from ..api.types import (
|
||||
Chat,
|
||||
TelegramObject,
|
||||
Update,
|
||||
User,
|
||||
)
|
||||
from ..utils.imports import import_module
|
||||
from ..utils.warnings import CodeHasNoEffect
|
||||
from .event.observer import EventObserver, SkipHandler, TelegramEventObserver
|
||||
|
|
@ -151,6 +156,7 @@ class Router:
|
|||
chat: Optional[Chat] = None
|
||||
from_user: Optional[User] = None
|
||||
|
||||
event: TelegramObject
|
||||
if update.message:
|
||||
update_type = "message"
|
||||
from_user = update.message.from_user
|
||||
|
|
@ -211,7 +217,7 @@ class Router:
|
|||
|
||||
raise SkipHandler
|
||||
|
||||
async def emit_startup(self, *args, **kwargs) -> None:
|
||||
async def emit_startup(self, *args: Any, **kwargs: Any) -> None:
|
||||
"""
|
||||
Recursively call startup callbacks
|
||||
|
||||
|
|
@ -225,7 +231,7 @@ class Router:
|
|||
for router in self.sub_routers:
|
||||
await router.emit_startup(*args, **kwargs)
|
||||
|
||||
async def emit_shutdown(self, *args, **kwargs) -> None:
|
||||
async def emit_shutdown(self, *args: Any, **kwargs: Any) -> None:
|
||||
"""
|
||||
Recursively call shutdown callbacks to graceful shutdown
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,15 @@ Example:
|
|||
>>> print(MyHelper.all())
|
||||
<<< ['barItem', 'bazItem', 'fooItem', 'lorem']
|
||||
"""
|
||||
from typing import List
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Iterable,
|
||||
List,
|
||||
Optional,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
|
||||
PROPS_KEYS_ATTR_NAME = "_props_keys"
|
||||
|
||||
|
|
@ -22,12 +30,12 @@ class Helper:
|
|||
mode = ""
|
||||
|
||||
@classmethod
|
||||
def all(cls):
|
||||
def all(cls) -> List[Any]:
|
||||
"""
|
||||
Get all consts
|
||||
:return: list
|
||||
"""
|
||||
result = []
|
||||
result: List[Any] = []
|
||||
for name in dir(cls):
|
||||
if not name.isupper():
|
||||
continue
|
||||
|
|
@ -49,7 +57,7 @@ class HelperMode(Helper):
|
|||
lowercase = "lowercase"
|
||||
|
||||
@classmethod
|
||||
def all(cls):
|
||||
def all(cls) -> List[str]:
|
||||
return [
|
||||
cls.SCREAMING_SNAKE_CASE,
|
||||
cls.lowerCamelCase,
|
||||
|
|
@ -59,7 +67,7 @@ class HelperMode(Helper):
|
|||
]
|
||||
|
||||
@classmethod
|
||||
def _screaming_snake_case(cls, text):
|
||||
def _screaming_snake_case(cls, text: str) -> str:
|
||||
"""
|
||||
Transform text to SCREAMING_SNAKE_CASE
|
||||
|
||||
|
|
@ -77,7 +85,7 @@ class HelperMode(Helper):
|
|||
return result
|
||||
|
||||
@classmethod
|
||||
def _snake_case(cls, text):
|
||||
def _snake_case(cls, text: str) -> str:
|
||||
"""
|
||||
Transform text to snake case (Based on SCREAMING_SNAKE_CASE)
|
||||
|
||||
|
|
@ -89,7 +97,7 @@ class HelperMode(Helper):
|
|||
return cls._screaming_snake_case(text).lower()
|
||||
|
||||
@classmethod
|
||||
def _camel_case(cls, text, first_upper=False):
|
||||
def _camel_case(cls, text: str, first_upper: bool = False) -> str:
|
||||
"""
|
||||
Transform text to camelCase or CamelCase
|
||||
|
||||
|
|
@ -113,7 +121,7 @@ class HelperMode(Helper):
|
|||
return result
|
||||
|
||||
@classmethod
|
||||
def apply(cls, text, mode):
|
||||
def apply(cls, text: str, mode: Union[str, Callable[[str], str]]) -> str:
|
||||
"""
|
||||
Apply mode for text
|
||||
|
||||
|
|
@ -136,7 +144,20 @@ class HelperMode(Helper):
|
|||
return text
|
||||
|
||||
|
||||
class Item:
|
||||
class _BaseItem:
|
||||
def __init__(self, value: Optional[str] = None):
|
||||
self._value = cast(str, value)
|
||||
|
||||
def __set_name__(self, owner: Any, name: str) -> None:
|
||||
if not name.isupper():
|
||||
raise NameError("Name for helper item must be in uppercase!")
|
||||
if not self._value:
|
||||
# TODO: а если не имеет?
|
||||
if hasattr(owner, "mode"):
|
||||
self._value = HelperMode.apply(name, getattr(owner, "mode"))
|
||||
|
||||
|
||||
class Item(_BaseItem):
|
||||
"""
|
||||
Helper item
|
||||
|
||||
|
|
@ -144,34 +165,24 @@ class Item:
|
|||
it will be automatically generated based on a variable's name
|
||||
"""
|
||||
|
||||
def __init__(self, value=None):
|
||||
self._value = value
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
def __get__(self, instance: Any, owner: Any) -> str:
|
||||
return self._value
|
||||
|
||||
def __set_name__(self, owner, name):
|
||||
if not name.isupper():
|
||||
raise NameError("Name for helper item must be in uppercase!")
|
||||
if not self._value:
|
||||
if hasattr(owner, "mode"):
|
||||
self._value = HelperMode.apply(name, getattr(owner, "mode"))
|
||||
|
||||
|
||||
class ListItem(Item):
|
||||
class ListItem(_BaseItem):
|
||||
"""
|
||||
This item is always a list
|
||||
|
||||
You can use &, | and + operators for that.
|
||||
"""
|
||||
|
||||
def add(self, other): # pragma: no cover
|
||||
def add(self, other: "ListItem") -> "ListItem": # pragma: no cover
|
||||
return self + other
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
def __get__(self, instance: Any, owner: Any) -> "ItemsList":
|
||||
return ItemsList(self._value)
|
||||
|
||||
def __getitem__(self, item): # pragma: no cover
|
||||
def __getitem__(self, item: Any) -> Any: # pragma: no cover
|
||||
# Only for IDE. This method is never be called.
|
||||
return self._value
|
||||
|
||||
|
|
@ -179,17 +190,17 @@ class ListItem(Item):
|
|||
__iadd__ = __add__ = __rand__ = __and__ = __ror__ = __or__ = add
|
||||
|
||||
|
||||
class ItemsList(list):
|
||||
class ItemsList(List[str]):
|
||||
"""
|
||||
Patch for default list
|
||||
|
||||
This class provides +, &, |, +=, &=, |= operators for extending the list
|
||||
"""
|
||||
|
||||
def __init__(self, *seq):
|
||||
def __init__(self, *seq: Any):
|
||||
super(ItemsList, self).__init__(map(str, seq))
|
||||
|
||||
def add(self, other):
|
||||
def add(self, other: Iterable[str]) -> "ItemsList":
|
||||
self.extend(other)
|
||||
return self
|
||||
|
||||
|
|
@ -197,7 +208,7 @@ class ItemsList(list):
|
|||
|
||||
|
||||
class OrderedHelperMeta(type):
|
||||
def __new__(mcs, name, bases, namespace, **kwargs):
|
||||
def __new__(mcs, name: Any, bases: Any, namespace: Any, **kwargs: Any) -> "OrderedHelperMeta":
|
||||
cls = super().__new__(mcs, name, bases, namespace)
|
||||
|
||||
props_keys = []
|
||||
|
|
@ -209,7 +220,8 @@ class OrderedHelperMeta(type):
|
|||
|
||||
setattr(cls, PROPS_KEYS_ATTR_NAME, props_keys)
|
||||
|
||||
return cls
|
||||
# ref: https://gitter.im/python/typing?at=5da98cc5fa637359fc9cbfe1
|
||||
return cast(OrderedHelperMeta, cls)
|
||||
|
||||
|
||||
class OrderedHelper(metaclass=OrderedHelperMeta):
|
||||
|
|
|
|||
|
|
@ -1,51 +1,64 @@
|
|||
from __future__ import annotations
|
||||
import contextvars
|
||||
from typing import Type, TypeVar
|
||||
from typing import (
|
||||
Any,
|
||||
ClassVar,
|
||||
Generic,
|
||||
Optional,
|
||||
TypeVar,
|
||||
cast,
|
||||
overload,
|
||||
)
|
||||
|
||||
__all__ = ("DataMixin", "ContextInstanceMixin")
|
||||
__all__ = ("ContextInstanceMixin",)
|
||||
|
||||
from typing_extensions import Literal
|
||||
|
||||
|
||||
class DataMixin:
|
||||
@property
|
||||
def data(self):
|
||||
data = getattr(self, "_data", None)
|
||||
if data is None:
|
||||
data = {}
|
||||
setattr(self, "_data", data)
|
||||
return data
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.data[item]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.data[key] = value
|
||||
|
||||
def __delitem__(self, key):
|
||||
del self.data[key]
|
||||
|
||||
def __contains__(self, item):
|
||||
return item in self.data
|
||||
|
||||
def get(self, key, default=None):
|
||||
return self.data.get(key, default)
|
||||
ContextInstance = TypeVar("ContextInstance")
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
class ContextInstanceMixin(Generic[ContextInstance]):
|
||||
__context_instance: ClassVar[contextvars.ContextVar[ContextInstance]]
|
||||
|
||||
|
||||
class ContextInstanceMixin:
|
||||
def __init_subclass__(cls, **kwargs):
|
||||
super().__init_subclass__(**kwargs)
|
||||
def __init_subclass__(cls, **kwargs: Any) -> None:
|
||||
super().__init_subclass__()
|
||||
cls.__context_instance = contextvars.ContextVar(f"instance_{cls.__name__}")
|
||||
return cls
|
||||
|
||||
@overload
|
||||
@classmethod
|
||||
def get_current(cls) -> Optional[ContextInstance]:
|
||||
...
|
||||
|
||||
@overload # noqa: F811, it's overload, not redefinition
|
||||
@classmethod
|
||||
def get_current(cls, no_error: Literal[True]) -> Optional[ContextInstance]:
|
||||
...
|
||||
|
||||
@overload # noqa: F811, it's overload, not redefinition
|
||||
@classmethod
|
||||
def get_current(cls, no_error: Literal[False]) -> ContextInstance:
|
||||
...
|
||||
|
||||
@classmethod # noqa: F811, it's overload, not redefinition
|
||||
def get_current(cls, no_error: bool = True) -> Optional[ContextInstance]:
|
||||
# on mypy 0.770 I catch that contextvars.ContextVar always contextvars.ContextVar[Any]
|
||||
cls.__context_instance = cast(
|
||||
contextvars.ContextVar[ContextInstance], cls.__context_instance
|
||||
)
|
||||
|
||||
try:
|
||||
current: Optional[ContextInstance] = cls.__context_instance.get()
|
||||
except LookupError:
|
||||
if no_error:
|
||||
current = None
|
||||
else:
|
||||
raise
|
||||
|
||||
return current
|
||||
|
||||
@classmethod
|
||||
def get_current(cls: Type[T], no_error=True) -> T:
|
||||
if no_error:
|
||||
return cls.__context_instance.get(None)
|
||||
return cls.__context_instance.get()
|
||||
|
||||
@classmethod
|
||||
def set_current(cls: Type[T], value: T) -> contextvars.Token:
|
||||
def set_current(cls, value: ContextInstance) -> contextvars.Token[ContextInstance]:
|
||||
if not isinstance(value, cls):
|
||||
raise TypeError(
|
||||
f"Value should be instance of {cls.__name__!r} not {type(value).__name__!r}"
|
||||
|
|
@ -53,5 +66,5 @@ class ContextInstanceMixin:
|
|||
return cls.__context_instance.set(value)
|
||||
|
||||
@classmethod
|
||||
def reset_current(cls: Type[T], token: contextvars.Token):
|
||||
def reset_current(cls, token: contextvars.Token[ContextInstance]) -> None:
|
||||
cls.__context_instance.reset(token)
|
||||
|
|
|
|||
19
mypy.ini
19
mypy.ini
|
|
@ -1,6 +1,21 @@
|
|||
[mypy]
|
||||
# plugins = pydantic.mypy
|
||||
|
||||
;plugins = pydantic.mypy
|
||||
;python_version = 3.8
|
||||
warn_unused_configs = True
|
||||
disallow_subclassing_any = True
|
||||
disallow_any_generics = True
|
||||
disallow_untyped_calls = True
|
||||
disallow_untyped_defs = True
|
||||
disallow_incomplete_defs = True
|
||||
check_untyped_defs = True
|
||||
disallow_untyped_decorators = True
|
||||
no_implicit_optional = True
|
||||
warn_redundant_casts = True
|
||||
warn_unused_ignores = True
|
||||
warn_return_any = True
|
||||
follow_imports_for_stubs = True
|
||||
namespace_packages = True
|
||||
show_absolute_path = True
|
||||
ignore_missing_imports = True
|
||||
show_error_context = True
|
||||
show_error_codes = True
|
||||
|
|
|
|||
260
poetry.lock
generated
260
poetry.lock
generated
|
|
@ -153,8 +153,8 @@ category = "dev"
|
|||
description = "Composable command line interface toolkit"
|
||||
name = "click"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "7.0"
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
version = "7.1.1"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
|
|
@ -171,7 +171,7 @@ description = "Code coverage measurement for Python"
|
|||
name = "coverage"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
||||
version = "5.0.3"
|
||||
version = "5.0.4"
|
||||
|
||||
[package.extras]
|
||||
toml = ["toml"]
|
||||
|
|
@ -182,7 +182,7 @@ description = "Decorators for Humans"
|
|||
name = "decorator"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*"
|
||||
version = "4.4.1"
|
||||
version = "4.4.2"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
|
|
@ -219,6 +219,14 @@ flake8 = ">=3.3.0"
|
|||
jinja2 = ">=2.9.0"
|
||||
pygments = ">=2.2.0"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Clean single-source support for Python 3 and 2"
|
||||
name = "future"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
version = "0.18.2"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
|
|
@ -249,7 +257,7 @@ description = "IPython: Productive Interactive Computing"
|
|||
name = "ipython"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
version = "7.12.0"
|
||||
version = "7.13.0"
|
||||
|
||||
[package.dependencies]
|
||||
appnope = "*"
|
||||
|
|
@ -265,7 +273,7 @@ setuptools = ">=18.5"
|
|||
traitlets = ">=4.2"
|
||||
|
||||
[package.extras]
|
||||
all = ["ipyparallel", "requests", "notebook", "qtconsole", "ipywidgets", "pygments", "nbconvert", "testpath", "Sphinx (>=1.3)", "nbformat", "numpy (>=1.14)", "ipykernel", "nose (>=0.10.1)"]
|
||||
all = ["numpy (>=1.14)", "testpath", "notebook", "nose (>=0.10.1)", "nbconvert", "requests", "ipywidgets", "qtconsole", "ipyparallel", "Sphinx (>=1.3)", "pygments", "nbformat", "ipykernel"]
|
||||
doc = ["Sphinx (>=1.3)"]
|
||||
kernel = ["ipykernel"]
|
||||
nbconvert = ["nbconvert"]
|
||||
|
|
@ -338,6 +346,25 @@ version = "2.6.1"
|
|||
six = "*"
|
||||
tornado = "*"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "A Python implementation of Lunr.js"
|
||||
name = "lunr"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "0.5.6"
|
||||
|
||||
[package.dependencies]
|
||||
future = ">=0.16.0"
|
||||
six = ">=1.11.0"
|
||||
|
||||
[package.dependencies.nltk]
|
||||
optional = true
|
||||
version = ">=3.2.5"
|
||||
|
||||
[package.extras]
|
||||
languages = ["nltk (>=3.2.5)"]
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API."
|
||||
|
|
@ -406,17 +433,21 @@ category = "dev"
|
|||
description = "Project documentation with Markdown."
|
||||
name = "mkdocs"
|
||||
optional = false
|
||||
python-versions = ">=2.7.9,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
|
||||
version = "1.0.4"
|
||||
python-versions = ">=3.5"
|
||||
version = "1.1"
|
||||
|
||||
[package.dependencies]
|
||||
Jinja2 = ">=2.7.1"
|
||||
Markdown = ">=2.3.1"
|
||||
Jinja2 = ">=2.10.1"
|
||||
Markdown = ">=3.2.1"
|
||||
PyYAML = ">=3.10"
|
||||
click = ">=3.3"
|
||||
livereload = ">=2.5.1"
|
||||
tornado = ">=5.0"
|
||||
|
||||
[package.dependencies.lunr]
|
||||
extras = ["languages"]
|
||||
version = "0.5.6"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "A Material Design theme for MkDocs"
|
||||
|
|
@ -453,7 +484,7 @@ description = "Optional static typing for Python"
|
|||
name = "mypy"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
version = "0.761"
|
||||
version = "0.770"
|
||||
|
||||
[package.dependencies]
|
||||
mypy-extensions = ">=0.4.3,<0.5.0"
|
||||
|
|
@ -471,13 +502,32 @@ optional = false
|
|||
python-versions = "*"
|
||||
version = "0.4.3"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Natural Language Toolkit"
|
||||
name = "nltk"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "3.4.5"
|
||||
|
||||
[package.dependencies]
|
||||
six = "*"
|
||||
|
||||
[package.extras]
|
||||
all = ["pyparsing", "scikit-learn", "python-crfsuite", "matplotlib", "scipy", "gensim", "requests", "twython", "numpy"]
|
||||
corenlp = ["requests"]
|
||||
machine_learning = ["gensim", "numpy", "python-crfsuite", "scikit-learn", "scipy"]
|
||||
plot = ["matplotlib"]
|
||||
tgrep = ["pyparsing"]
|
||||
twitter = ["twython"]
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Core utilities for Python packages"
|
||||
name = "packaging"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "20.1"
|
||||
version = "20.3"
|
||||
|
||||
[package.dependencies]
|
||||
pyparsing = ">=2.0.2"
|
||||
|
|
@ -489,7 +539,7 @@ description = "A Python Parser"
|
|||
name = "parso"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2"
|
||||
|
||||
[package.extras]
|
||||
testing = ["docopt", "pytest (>=3.0.7)"]
|
||||
|
|
@ -543,8 +593,8 @@ category = "dev"
|
|||
description = "Library for building powerful interactive command lines in Python"
|
||||
name = "prompt-toolkit"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
version = "3.0.3"
|
||||
python-versions = ">=3.6.1"
|
||||
version = "3.0.4"
|
||||
|
||||
[package.dependencies]
|
||||
wcwidth = "*"
|
||||
|
|
@ -600,8 +650,8 @@ category = "dev"
|
|||
description = "Pygments is a syntax highlighting package written in Python."
|
||||
name = "pygments"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
version = "2.5.2"
|
||||
python-versions = ">=3.5"
|
||||
version = "2.6.1"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
|
|
@ -628,7 +678,7 @@ description = "pytest: simple powerful testing with Python"
|
|||
name = "pytest"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
version = "5.3.5"
|
||||
version = "5.4.1"
|
||||
|
||||
[package.dependencies]
|
||||
atomicwrites = ">=1.0"
|
||||
|
|
@ -683,7 +733,7 @@ description = "pytest plugin for generating HTML reports"
|
|||
name = "pytest-html"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
version = "2.0.1"
|
||||
version = "2.1.0"
|
||||
|
||||
[package.dependencies]
|
||||
pytest = ">=5.0"
|
||||
|
|
@ -781,7 +831,7 @@ description = "Tornado is a Python web framework and asynchronous networking lib
|
|||
name = "tornado"
|
||||
optional = false
|
||||
python-versions = ">= 3.5"
|
||||
version = "6.0.3"
|
||||
version = "6.0.4"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
|
|
@ -851,7 +901,7 @@ marker = "python_version >= \"3.5\" and python_version < \"3.8\" or python_versi
|
|||
name = "zipp"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
version = "3.0.0"
|
||||
version = "3.1.0"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
|
||||
|
|
@ -861,7 +911,7 @@ testing = ["jaraco.itertools", "func-timeout"]
|
|||
fast = ["uvloop"]
|
||||
|
||||
[metadata]
|
||||
content-hash = "2eb50b5b57d0fac4780f1eb3f92ff129d891fd346e0c00856c1a56c58feffb03"
|
||||
content-hash = "bce1bc7bf9eb949283094490a084d484a3d2f7b0d992ea3a4ea1e75401f6e2da"
|
||||
python-versions = "^3.7"
|
||||
|
||||
[metadata.files]
|
||||
|
|
@ -931,49 +981,49 @@ chardet = [
|
|||
{file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"},
|
||||
]
|
||||
click = [
|
||||
{file = "Click-7.0-py2.py3-none-any.whl", hash = "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13"},
|
||||
{file = "Click-7.0.tar.gz", hash = "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"},
|
||||
{file = "click-7.1.1-py2.py3-none-any.whl", hash = "sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a"},
|
||||
{file = "click-7.1.1.tar.gz", hash = "sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc"},
|
||||
]
|
||||
colorama = [
|
||||
{file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
|
||||
{file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"},
|
||||
]
|
||||
coverage = [
|
||||
{file = "coverage-5.0.3-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:cc1109f54a14d940b8512ee9f1c3975c181bbb200306c6d8b87d93376538782f"},
|
||||
{file = "coverage-5.0.3-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:be18f4ae5a9e46edae3f329de2191747966a34a3d93046dbdf897319923923bc"},
|
||||
{file = "coverage-5.0.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3230d1003eec018ad4a472d254991e34241e0bbd513e97a29727c7c2f637bd2a"},
|
||||
{file = "coverage-5.0.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:e69215621707119c6baf99bda014a45b999d37602cb7043d943c76a59b05bf52"},
|
||||
{file = "coverage-5.0.3-cp27-cp27m-win32.whl", hash = "sha256:1daa3eceed220f9fdb80d5ff950dd95112cd27f70d004c7918ca6dfc6c47054c"},
|
||||
{file = "coverage-5.0.3-cp27-cp27m-win_amd64.whl", hash = "sha256:51bc7710b13a2ae0c726f69756cf7ffd4362f4ac36546e243136187cfcc8aa73"},
|
||||
{file = "coverage-5.0.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:9bea19ac2f08672636350f203db89382121c9c2ade85d945953ef3c8cf9d2a68"},
|
||||
{file = "coverage-5.0.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:5012d3b8d5a500834783689a5d2292fe06ec75dc86ee1ccdad04b6f5bf231691"},
|
||||
{file = "coverage-5.0.3-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:d513cc3db248e566e07a0da99c230aca3556d9b09ed02f420664e2da97eac301"},
|
||||
{file = "coverage-5.0.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:3dbb72eaeea5763676a1a1efd9b427a048c97c39ed92e13336e726117d0b72bf"},
|
||||
{file = "coverage-5.0.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:15cf13a6896048d6d947bf7d222f36e4809ab926894beb748fc9caa14605d9c3"},
|
||||
{file = "coverage-5.0.3-cp35-cp35m-win32.whl", hash = "sha256:fca1669d464f0c9831fd10be2eef6b86f5ebd76c724d1e0706ebdff86bb4adf0"},
|
||||
{file = "coverage-5.0.3-cp35-cp35m-win_amd64.whl", hash = "sha256:1e44a022500d944d42f94df76727ba3fc0a5c0b672c358b61067abb88caee7a0"},
|
||||
{file = "coverage-5.0.3-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:b26aaf69713e5674efbde4d728fb7124e429c9466aeaf5f4a7e9e699b12c9fe2"},
|
||||
{file = "coverage-5.0.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:722e4557c8039aad9592c6a4213db75da08c2cd9945320220634f637251c3894"},
|
||||
{file = "coverage-5.0.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:7afad9835e7a651d3551eab18cbc0fdb888f0a6136169fbef0662d9cdc9987cf"},
|
||||
{file = "coverage-5.0.3-cp36-cp36m-win32.whl", hash = "sha256:25dbf1110d70bab68a74b4b9d74f30e99b177cde3388e07cc7272f2168bd1477"},
|
||||
{file = "coverage-5.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:c312e57847db2526bc92b9bfa78266bfbaabac3fdcd751df4d062cd4c23e46dc"},
|
||||
{file = "coverage-5.0.3-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:a8b8ac7876bc3598e43e2603f772d2353d9931709345ad6c1149009fd1bc81b8"},
|
||||
{file = "coverage-5.0.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:527b4f316e6bf7755082a783726da20671a0cc388b786a64417780b90565b987"},
|
||||
{file = "coverage-5.0.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d649dc0bcace6fcdb446ae02b98798a856593b19b637c1b9af8edadf2b150bea"},
|
||||
{file = "coverage-5.0.3-cp37-cp37m-win32.whl", hash = "sha256:cd60f507c125ac0ad83f05803063bed27e50fa903b9c2cfee3f8a6867ca600fc"},
|
||||
{file = "coverage-5.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c60097190fe9dc2b329a0eb03393e2e0829156a589bd732e70794c0dd804258e"},
|
||||
{file = "coverage-5.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:d7008a6796095a79544f4da1ee49418901961c97ca9e9d44904205ff7d6aa8cb"},
|
||||
{file = "coverage-5.0.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ea9525e0fef2de9208250d6c5aeeee0138921057cd67fcef90fbed49c4d62d37"},
|
||||
{file = "coverage-5.0.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:c62a2143e1313944bf4a5ab34fd3b4be15367a02e9478b0ce800cb510e3bbb9d"},
|
||||
{file = "coverage-5.0.3-cp38-cp38m-win32.whl", hash = "sha256:b0840b45187699affd4c6588286d429cd79a99d509fe3de0f209594669bb0954"},
|
||||
{file = "coverage-5.0.3-cp38-cp38m-win_amd64.whl", hash = "sha256:76e2057e8ffba5472fd28a3a010431fd9e928885ff480cb278877c6e9943cc2e"},
|
||||
{file = "coverage-5.0.3-cp39-cp39m-win32.whl", hash = "sha256:b63dd43f455ba878e5e9f80ba4f748c0a2156dde6e0e6e690310e24d6e8caf40"},
|
||||
{file = "coverage-5.0.3-cp39-cp39m-win_amd64.whl", hash = "sha256:da93027835164b8223e8e5af2cf902a4c80ed93cb0909417234f4a9df3bcd9af"},
|
||||
{file = "coverage-5.0.3.tar.gz", hash = "sha256:77afca04240c40450c331fa796b3eab6f1e15c5ecf8bf2b8bee9706cd5452fef"},
|
||||
{file = "coverage-5.0.4-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:8a620767b8209f3446197c0e29ba895d75a1e272a36af0786ec70fe7834e4307"},
|
||||
{file = "coverage-5.0.4-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:73aa6e86034dad9f00f4bbf5a666a889d17d79db73bc5af04abd6c20a014d9c8"},
|
||||
{file = "coverage-5.0.4-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:408ce64078398b2ee2ec08199ea3fcf382828d2f8a19c5a5ba2946fe5ddc6c31"},
|
||||
{file = "coverage-5.0.4-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:cda33311cb9fb9323958a69499a667bd728a39a7aa4718d7622597a44c4f1441"},
|
||||
{file = "coverage-5.0.4-cp27-cp27m-win32.whl", hash = "sha256:5f587dfd83cb669933186661a351ad6fc7166273bc3e3a1531ec5c783d997aac"},
|
||||
{file = "coverage-5.0.4-cp27-cp27m-win_amd64.whl", hash = "sha256:9fad78c13e71546a76c2f8789623eec8e499f8d2d799f4b4547162ce0a4df435"},
|
||||
{file = "coverage-5.0.4-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:2e08c32cbede4a29e2a701822291ae2bc9b5220a971bba9d1e7615312efd3037"},
|
||||
{file = "coverage-5.0.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:922fb9ef2c67c3ab20e22948dcfd783397e4c043a5c5fa5ff5e9df5529074b0a"},
|
||||
{file = "coverage-5.0.4-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:c3fc325ce4cbf902d05a80daa47b645d07e796a80682c1c5800d6ac5045193e5"},
|
||||
{file = "coverage-5.0.4-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:046a1a742e66d065d16fb564a26c2a15867f17695e7f3d358d7b1ad8a61bca30"},
|
||||
{file = "coverage-5.0.4-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6ad6ca45e9e92c05295f638e78cd42bfaaf8ee07878c9ed73e93190b26c125f7"},
|
||||
{file = "coverage-5.0.4-cp35-cp35m-win32.whl", hash = "sha256:eda55e6e9ea258f5e4add23bcf33dc53b2c319e70806e180aecbff8d90ea24de"},
|
||||
{file = "coverage-5.0.4-cp35-cp35m-win_amd64.whl", hash = "sha256:4a8a259bf990044351baf69d3b23e575699dd60b18460c71e81dc565f5819ac1"},
|
||||
{file = "coverage-5.0.4-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:f372cdbb240e09ee855735b9d85e7f50730dcfb6296b74b95a3e5dea0615c4c1"},
|
||||
{file = "coverage-5.0.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a37c6233b28e5bc340054cf6170e7090a4e85069513320275a4dc929144dccf0"},
|
||||
{file = "coverage-5.0.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:443be7602c790960b9514567917af538cac7807a7c0c0727c4d2bbd4014920fd"},
|
||||
{file = "coverage-5.0.4-cp36-cp36m-win32.whl", hash = "sha256:165a48268bfb5a77e2d9dbb80de7ea917332a79c7adb747bd005b3a07ff8caf0"},
|
||||
{file = "coverage-5.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:0a907199566269e1cfa304325cc3b45c72ae341fbb3253ddde19fa820ded7a8b"},
|
||||
{file = "coverage-5.0.4-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:513e6526e0082c59a984448f4104c9bf346c2da9961779ede1fc458e8e8a1f78"},
|
||||
{file = "coverage-5.0.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:3844c3dab800ca8536f75ae89f3cf566848a3eb2af4d9f7b1103b4f4f7a5dad6"},
|
||||
{file = "coverage-5.0.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:641e329e7f2c01531c45c687efcec8aeca2a78a4ff26d49184dce3d53fc35014"},
|
||||
{file = "coverage-5.0.4-cp37-cp37m-win32.whl", hash = "sha256:db1d4e38c9b15be1521722e946ee24f6db95b189d1447fa9ff18dd16ba89f732"},
|
||||
{file = "coverage-5.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:62061e87071497951155cbccee487980524d7abea647a1b2a6eb6b9647df9006"},
|
||||
{file = "coverage-5.0.4-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:65a7e00c00472cd0f59ae09d2fb8a8aaae7f4a0cf54b2b74f3138d9f9ceb9cb2"},
|
||||
{file = "coverage-5.0.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1f66cf263ec77af5b8fe14ef14c5e46e2eb4a795ac495ad7c03adc72ae43fafe"},
|
||||
{file = "coverage-5.0.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:85596aa5d9aac1bf39fe39d9fa1051b0f00823982a1de5766e35d495b4a36ca9"},
|
||||
{file = "coverage-5.0.4-cp38-cp38-win32.whl", hash = "sha256:86a0ea78fd851b313b2e712266f663e13b6bc78c2fb260b079e8b67d970474b1"},
|
||||
{file = "coverage-5.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:03f630aba2b9b0d69871c2e8d23a69b7fe94a1e2f5f10df5049c0df99db639a0"},
|
||||
{file = "coverage-5.0.4-cp39-cp39-win32.whl", hash = "sha256:7c9762f80a25d8d0e4ab3cb1af5d9dffbddb3ee5d21c43e3474c84bf5ff941f7"},
|
||||
{file = "coverage-5.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:4482f69e0701139d0f2c44f3c395d1d1d37abd81bfafbf9b6efbe2542679d892"},
|
||||
{file = "coverage-5.0.4.tar.gz", hash = "sha256:1b60a95fc995649464e0cd48cecc8288bac5f4198f21d04b8229dc4097d76823"},
|
||||
]
|
||||
decorator = [
|
||||
{file = "decorator-4.4.1-py2.py3-none-any.whl", hash = "sha256:5d19b92a3c8f7f101c8dd86afd86b0f061a8ce4540ab8cd401fa2542756bce6d"},
|
||||
{file = "decorator-4.4.1.tar.gz", hash = "sha256:54c38050039232e1db4ad7375cfce6748d7b41c29e95a081c8a6d2c30364a2ce"},
|
||||
{file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"},
|
||||
{file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"},
|
||||
]
|
||||
entrypoints = [
|
||||
{file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"},
|
||||
|
|
@ -987,6 +1037,9 @@ flake8-html = [
|
|||
{file = "flake8-html-0.4.0.tar.gz", hash = "sha256:44bec37f142e97c4a5b2cf10efe24ed253617a9736878851a594d4763011e742"},
|
||||
{file = "flake8_html-0.4.0-py2.py3-none-any.whl", hash = "sha256:f372cd599ba9a374943eaa75a9cce30408cf4c0cc2251bc5194e6b0d3fc2bc3a"},
|
||||
]
|
||||
future = [
|
||||
{file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
|
||||
]
|
||||
idna = [
|
||||
{file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"},
|
||||
{file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"},
|
||||
|
|
@ -996,8 +1049,8 @@ importlib-metadata = [
|
|||
{file = "importlib_metadata-1.5.0.tar.gz", hash = "sha256:06f5b3a99029c7134207dd882428a66992a9de2bef7c2b699b5641f9886c3302"},
|
||||
]
|
||||
ipython = [
|
||||
{file = "ipython-7.12.0-py3-none-any.whl", hash = "sha256:f6689108b1734501d3b59c84427259fd5ac5141afe2e846cfa8598eb811886c9"},
|
||||
{file = "ipython-7.12.0.tar.gz", hash = "sha256:d9459e7237e2e5858738ff9c3e26504b79899b58a6d49e574d352493d80684c6"},
|
||||
{file = "ipython-7.13.0-py3-none-any.whl", hash = "sha256:eb8d075de37f678424527b5ef6ea23f7b80240ca031c2dd6de5879d687a65333"},
|
||||
{file = "ipython-7.13.0.tar.gz", hash = "sha256:ca478e52ae1f88da0102360e57e528b92f3ae4316aabac80a2cd7f7ab2efb48a"},
|
||||
]
|
||||
ipython-genutils = [
|
||||
{file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"},
|
||||
|
|
@ -1019,6 +1072,10 @@ livereload = [
|
|||
{file = "livereload-2.6.1-py2.py3-none-any.whl", hash = "sha256:78d55f2c268a8823ba499305dcac64e28ddeb9a92571e12d543cd304faf5817b"},
|
||||
{file = "livereload-2.6.1.tar.gz", hash = "sha256:89254f78d7529d7ea0a3417d224c34287ebfe266b05e67e51facaf82c27f0f66"},
|
||||
]
|
||||
lunr = [
|
||||
{file = "lunr-0.5.6-py2.py3-none-any.whl", hash = "sha256:1208622930c915a07e6f8e8640474357826bad48534c0f57969b6fca9bffc88e"},
|
||||
{file = "lunr-0.5.6.tar.gz", hash = "sha256:7be69d7186f65784a4f2adf81e5c58efd6a9921aa95966babcb1f2f2ada75c20"},
|
||||
]
|
||||
lxml = [
|
||||
{file = "lxml-4.5.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0701f7965903a1c3f6f09328c1278ac0eee8f56f244e66af79cb224b7ef3801c"},
|
||||
{file = "lxml-4.5.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:06d4e0bbb1d62e38ae6118406d7cdb4693a3fa34ee3762238bcb96c9e36a93cd"},
|
||||
|
|
@ -1083,6 +1140,11 @@ markupsafe = [
|
|||
{file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"},
|
||||
{file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"},
|
||||
{file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"},
|
||||
{file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"},
|
||||
{file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"},
|
||||
{file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"},
|
||||
{file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"},
|
||||
{file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"},
|
||||
{file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"},
|
||||
]
|
||||
mccabe = [
|
||||
|
|
@ -1093,8 +1155,8 @@ mkautodoc = [
|
|||
{file = "mkautodoc-0.1.0.tar.gz", hash = "sha256:7c2595f40276b356e576ce7e343338f8b4fa1e02ea904edf33fadf82b68ca67c"},
|
||||
]
|
||||
mkdocs = [
|
||||
{file = "mkdocs-1.0.4-py2.py3-none-any.whl", hash = "sha256:8cc8b38325456b9e942c981a209eaeb1e9f3f77b493ad755bfef889b9c8d356a"},
|
||||
{file = "mkdocs-1.0.4.tar.gz", hash = "sha256:17d34329aad75d5de604b9ed4e31df3a4d235afefdc46ce7b1964fddb2e1e939"},
|
||||
{file = "mkdocs-1.1-py2.py3-none-any.whl", hash = "sha256:1e385a70aea8a9dedb731aea4fd5f3704b2074801c4f96f06b2920999babda8a"},
|
||||
{file = "mkdocs-1.1.tar.gz", hash = "sha256:9243291392f59e20b655e4e46210233453faf97787c2cf72176510e868143174"},
|
||||
]
|
||||
mkdocs-material = [
|
||||
{file = "mkdocs-material-4.6.3.tar.gz", hash = "sha256:1d486635b03f5a2ec87325842f7b10c7ae7daa0eef76b185572eece6a6ea212c"},
|
||||
|
|
@ -1124,32 +1186,36 @@ multidict = [
|
|||
{file = "multidict-4.7.5.tar.gz", hash = "sha256:aee283c49601fa4c13adc64c09c978838a7e812f85377ae130a24d7198c0331e"},
|
||||
]
|
||||
mypy = [
|
||||
{file = "mypy-0.761-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:7f672d02fffcbace4db2b05369142e0506cdcde20cea0e07c7c2171c4fd11dd6"},
|
||||
{file = "mypy-0.761-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:87c556fb85d709dacd4b4cb6167eecc5bbb4f0a9864b69136a0d4640fdc76a36"},
|
||||
{file = "mypy-0.761-cp35-cp35m-win_amd64.whl", hash = "sha256:c6d27bd20c3ba60d5b02f20bd28e20091d6286a699174dfad515636cb09b5a72"},
|
||||
{file = "mypy-0.761-cp36-cp36m-macosx_10_6_x86_64.whl", hash = "sha256:4b9365ade157794cef9685791032521233729cb00ce76b0ddc78749abea463d2"},
|
||||
{file = "mypy-0.761-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:634aef60b4ff0f650d3e59d4374626ca6153fcaff96ec075b215b568e6ee3cb0"},
|
||||
{file = "mypy-0.761-cp36-cp36m-win_amd64.whl", hash = "sha256:53ea810ae3f83f9c9b452582261ea859828a9ed666f2e1ca840300b69322c474"},
|
||||
{file = "mypy-0.761-cp37-cp37m-macosx_10_6_x86_64.whl", hash = "sha256:0a9a45157e532da06fe56adcfef8a74629566b607fa2c1ac0122d1ff995c748a"},
|
||||
{file = "mypy-0.761-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:7eadc91af8270455e0d73565b8964da1642fe226665dd5c9560067cd64d56749"},
|
||||
{file = "mypy-0.761-cp37-cp37m-win_amd64.whl", hash = "sha256:e2bb577d10d09a2d8822a042a23b8d62bc3b269667c9eb8e60a6edfa000211b1"},
|
||||
{file = "mypy-0.761-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c35cae79ceb20d47facfad51f952df16c2ae9f45db6cb38405a3da1cf8fc0a7"},
|
||||
{file = "mypy-0.761-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:f97a605d7c8bc2c6d1172c2f0d5a65b24142e11a58de689046e62c2d632ca8c1"},
|
||||
{file = "mypy-0.761-cp38-cp38-win_amd64.whl", hash = "sha256:a6bd44efee4dc8c3324c13785a9dc3519b3ee3a92cada42d2b57762b7053b49b"},
|
||||
{file = "mypy-0.761-py3-none-any.whl", hash = "sha256:7e396ce53cacd5596ff6d191b47ab0ea18f8e0ec04e15d69728d530e86d4c217"},
|
||||
{file = "mypy-0.761.tar.gz", hash = "sha256:85baab8d74ec601e86134afe2bcccd87820f79d2f8d5798c889507d1088287bf"},
|
||||
{file = "mypy-0.770-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:a34b577cdf6313bf24755f7a0e3f3c326d5c1f4fe7422d1d06498eb25ad0c600"},
|
||||
{file = "mypy-0.770-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:86c857510a9b7c3104cf4cde1568f4921762c8f9842e987bc03ed4f160925754"},
|
||||
{file = "mypy-0.770-cp35-cp35m-win_amd64.whl", hash = "sha256:a8ffcd53cb5dfc131850851cc09f1c44689c2812d0beb954d8138d4f5fc17f65"},
|
||||
{file = "mypy-0.770-cp36-cp36m-macosx_10_6_x86_64.whl", hash = "sha256:7687f6455ec3ed7649d1ae574136835a4272b65b3ddcf01ab8704ac65616c5ce"},
|
||||
{file = "mypy-0.770-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3beff56b453b6ef94ecb2996bea101a08f1f8a9771d3cbf4988a61e4d9973761"},
|
||||
{file = "mypy-0.770-cp36-cp36m-win_amd64.whl", hash = "sha256:15b948e1302682e3682f11f50208b726a246ab4e6c1b39f9264a8796bb416aa2"},
|
||||
{file = "mypy-0.770-cp37-cp37m-macosx_10_6_x86_64.whl", hash = "sha256:b90928f2d9eb2f33162405f32dde9f6dcead63a0971ca8a1b50eb4ca3e35ceb8"},
|
||||
{file = "mypy-0.770-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c56ffe22faa2e51054c5f7a3bc70a370939c2ed4de308c690e7949230c995913"},
|
||||
{file = "mypy-0.770-cp37-cp37m-win_amd64.whl", hash = "sha256:8dfb69fbf9f3aeed18afffb15e319ca7f8da9642336348ddd6cab2713ddcf8f9"},
|
||||
{file = "mypy-0.770-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:219a3116ecd015f8dca7b5d2c366c973509dfb9a8fc97ef044a36e3da66144a1"},
|
||||
{file = "mypy-0.770-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7ec45a70d40ede1ec7ad7f95b3c94c9cf4c186a32f6bacb1795b60abd2f9ef27"},
|
||||
{file = "mypy-0.770-cp38-cp38-win_amd64.whl", hash = "sha256:f91c7ae919bbc3f96cd5e5b2e786b2b108343d1d7972ea130f7de27fdd547cf3"},
|
||||
{file = "mypy-0.770-py3-none-any.whl", hash = "sha256:3b1fc683fb204c6b4403a1ef23f0b1fac8e4477091585e0c8c54cbdf7d7bb164"},
|
||||
{file = "mypy-0.770.tar.gz", hash = "sha256:8a627507ef9b307b46a1fea9513d5c98680ba09591253082b4c48697ba05a4ae"},
|
||||
]
|
||||
mypy-extensions = [
|
||||
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
||||
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
|
||||
]
|
||||
nltk = [
|
||||
{file = "nltk-3.4.5.win32.exe", hash = "sha256:a08bdb4b8a1c13de16743068d9eb61c8c71c2e5d642e8e08205c528035843f82"},
|
||||
{file = "nltk-3.4.5.zip", hash = "sha256:bed45551259aa2101381bbdd5df37d44ca2669c5c3dad72439fa459b29137d94"},
|
||||
]
|
||||
packaging = [
|
||||
{file = "packaging-20.1-py2.py3-none-any.whl", hash = "sha256:170748228214b70b672c581a3dd610ee51f733018650740e98c7df862a583f73"},
|
||||
{file = "packaging-20.1.tar.gz", hash = "sha256:e665345f9eef0c621aa0bf2f8d78cf6d21904eef16a93f020240b704a57f1334"},
|
||||
{file = "packaging-20.3-py2.py3-none-any.whl", hash = "sha256:82f77b9bee21c1bafbf35a84905d604d5d1223801d639cf3ed140bd651c08752"},
|
||||
{file = "packaging-20.3.tar.gz", hash = "sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3"},
|
||||
]
|
||||
parso = [
|
||||
{file = "parso-0.6.1-py2.py3-none-any.whl", hash = "sha256:951af01f61e6dccd04159042a0706a31ad437864ec6e25d0d7a96a9fbb9b0095"},
|
||||
{file = "parso-0.6.1.tar.gz", hash = "sha256:56b2105a80e9c4df49de85e125feb6be69f49920e121406f15e7acde6c9dfc57"},
|
||||
{file = "parso-0.6.2-py2.py3-none-any.whl", hash = "sha256:8515fc12cfca6ee3aa59138741fc5624d62340c97e401c74875769948d4f2995"},
|
||||
{file = "parso-0.6.2.tar.gz", hash = "sha256:0c5659e0c6eba20636f99a04f469798dca8da279645ce5c387315b2c23912157"},
|
||||
]
|
||||
pathspec = [
|
||||
{file = "pathspec-0.7.0-py2.py3-none-any.whl", hash = "sha256:163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424"},
|
||||
|
|
@ -1168,8 +1234,8 @@ pluggy = [
|
|||
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
|
||||
]
|
||||
prompt-toolkit = [
|
||||
{file = "prompt_toolkit-3.0.3-py3-none-any.whl", hash = "sha256:c93e53af97f630f12f5f62a3274e79527936ed466f038953dfa379d4941f651a"},
|
||||
{file = "prompt_toolkit-3.0.3.tar.gz", hash = "sha256:a402e9bf468b63314e37460b68ba68243d55b2f8c4d0192f85a019af3945050e"},
|
||||
{file = "prompt_toolkit-3.0.4-py3-none-any.whl", hash = "sha256:859e1b205b6cf6a51fa57fa34202e45365cf58f8338f0ee9f4e84a4165b37d5b"},
|
||||
{file = "prompt_toolkit-3.0.4.tar.gz", hash = "sha256:ebe6b1b08c888b84c50d7f93dee21a09af39860144ff6130aadbd61ae8d29783"},
|
||||
]
|
||||
ptyprocess = [
|
||||
{file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"},
|
||||
|
|
@ -1204,8 +1270,8 @@ pyflakes = [
|
|||
{file = "pyflakes-2.1.1.tar.gz", hash = "sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"},
|
||||
]
|
||||
pygments = [
|
||||
{file = "Pygments-2.5.2-py2.py3-none-any.whl", hash = "sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b"},
|
||||
{file = "Pygments-2.5.2.tar.gz", hash = "sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe"},
|
||||
{file = "Pygments-2.6.1-py3-none-any.whl", hash = "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"},
|
||||
{file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"},
|
||||
]
|
||||
pymdown-extensions = [
|
||||
{file = "pymdown-extensions-6.3.tar.gz", hash = "sha256:cb879686a586b22292899771f5e5bc3382808e92aa938f71b550ecdea709419f"},
|
||||
|
|
@ -1216,8 +1282,8 @@ pyparsing = [
|
|||
{file = "pyparsing-2.4.6.tar.gz", hash = "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f"},
|
||||
]
|
||||
pytest = [
|
||||
{file = "pytest-5.3.5-py3-none-any.whl", hash = "sha256:ff615c761e25eb25df19edddc0b970302d2a9091fbce0e7213298d85fb61fef6"},
|
||||
{file = "pytest-5.3.5.tar.gz", hash = "sha256:0d5fe9189a148acc3c3eb2ac8e1ac0742cb7618c084f3d228baaec0c254b318d"},
|
||||
{file = "pytest-5.4.1-py3-none-any.whl", hash = "sha256:0e5b30f5cb04e887b91b1ee519fa3d89049595f428c1db76e73bd7f17b09b172"},
|
||||
{file = "pytest-5.4.1.tar.gz", hash = "sha256:84dde37075b8805f3d1f392cc47e38a0e59518fb46a431cfdaf7cf1ce805f970"},
|
||||
]
|
||||
pytest-asyncio = [
|
||||
{file = "pytest-asyncio-0.10.0.tar.gz", hash = "sha256:9fac5100fd716cbecf6ef89233e8590a4ad61d729d1732e0a96b84182df1daaf"},
|
||||
|
|
@ -1228,8 +1294,8 @@ pytest-cov = [
|
|||
{file = "pytest_cov-2.8.1-py2.py3-none-any.whl", hash = "sha256:cdbdef4f870408ebdbfeb44e63e07eb18bb4619fae852f6e760645fa36172626"},
|
||||
]
|
||||
pytest-html = [
|
||||
{file = "pytest-html-2.0.1.tar.gz", hash = "sha256:933da7a5e71e5eace9e475441ed88a684f20f6198aa36516cb947ac05edd9921"},
|
||||
{file = "pytest_html-2.0.1-py2.py3-none-any.whl", hash = "sha256:bc40553ca2a1835479c2caf7d48604502cd66d0c5db58ddbca53d74946ee71bd"},
|
||||
{file = "pytest-html-2.1.0.tar.gz", hash = "sha256:8645a8616c8ed7414678e0aeebc3b2fd7d44268773ef5e7289289ad8632c9e91"},
|
||||
{file = "pytest_html-2.1.0-py2.py3-none-any.whl", hash = "sha256:0317a0a589db59c26091ab6068b3edac8d9bc1a8bb9727ade48f806797346956"},
|
||||
]
|
||||
pytest-metadata = [
|
||||
{file = "pytest-metadata-1.8.0.tar.gz", hash = "sha256:2071a59285de40d7541fde1eb9f1ddea1c9db165882df82781367471238b66ba"},
|
||||
|
|
@ -1293,13 +1359,15 @@ toml = [
|
|||
{file = "toml-0.10.0.tar.gz", hash = "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"},
|
||||
]
|
||||
tornado = [
|
||||
{file = "tornado-6.0.3-cp35-cp35m-win32.whl", hash = "sha256:c9399267c926a4e7c418baa5cbe91c7d1cf362d505a1ef898fde44a07c9dd8a5"},
|
||||
{file = "tornado-6.0.3-cp35-cp35m-win_amd64.whl", hash = "sha256:398e0d35e086ba38a0427c3b37f4337327231942e731edaa6e9fd1865bbd6f60"},
|
||||
{file = "tornado-6.0.3-cp36-cp36m-win32.whl", hash = "sha256:4e73ef678b1a859f0cb29e1d895526a20ea64b5ffd510a2307b5998c7df24281"},
|
||||
{file = "tornado-6.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:349884248c36801afa19e342a77cc4458caca694b0eda633f5878e458a44cb2c"},
|
||||
{file = "tornado-6.0.3-cp37-cp37m-win32.whl", hash = "sha256:559bce3d31484b665259f50cd94c5c28b961b09315ccd838f284687245f416e5"},
|
||||
{file = "tornado-6.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:abbe53a39734ef4aba061fca54e30c6b4639d3e1f59653f0da37a0003de148c7"},
|
||||
{file = "tornado-6.0.3.tar.gz", hash = "sha256:c845db36ba616912074c5b1ee897f8e0124df269468f25e4fe21fe72f6edd7a9"},
|
||||
{file = "tornado-6.0.4-cp35-cp35m-win32.whl", hash = "sha256:5217e601700f24e966ddab689f90b7ea4bd91ff3357c3600fa1045e26d68e55d"},
|
||||
{file = "tornado-6.0.4-cp35-cp35m-win_amd64.whl", hash = "sha256:c98232a3ac391f5faea6821b53db8db461157baa788f5d6222a193e9456e1740"},
|
||||
{file = "tornado-6.0.4-cp36-cp36m-win32.whl", hash = "sha256:5f6a07e62e799be5d2330e68d808c8ac41d4a259b9cea61da4101b83cb5dc673"},
|
||||
{file = "tornado-6.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:c952975c8ba74f546ae6de2e226ab3cc3cc11ae47baf607459a6728585bb542a"},
|
||||
{file = "tornado-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:2c027eb2a393d964b22b5c154d1a23a5f8727db6fda837118a776b29e2b8ebc6"},
|
||||
{file = "tornado-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:5618f72e947533832cbc3dec54e1dffc1747a5cb17d1fd91577ed14fa0dc081b"},
|
||||
{file = "tornado-6.0.4-cp38-cp38-win32.whl", hash = "sha256:22aed82c2ea340c3771e3babc5ef220272f6fd06b5108a53b4976d0d722bcd52"},
|
||||
{file = "tornado-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:c58d56003daf1b616336781b26d184023ea4af13ae143d9dda65e31e534940b9"},
|
||||
{file = "tornado-6.0.4.tar.gz", hash = "sha256:0fe2d45ba43b00a41cd73f8be321a44936dc1aba233dee979f17a042b83eb6dc"},
|
||||
]
|
||||
traitlets = [
|
||||
{file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"},
|
||||
|
|
@ -1368,6 +1436,6 @@ yarl = [
|
|||
{file = "yarl-1.4.2.tar.gz", hash = "sha256:58cd9c469eced558cd81aa3f484b2924e8897049e06889e8ff2510435b7ef74b"},
|
||||
]
|
||||
zipp = [
|
||||
{file = "zipp-3.0.0-py3-none-any.whl", hash = "sha256:12248a63bbdf7548f89cb4c7cda4681e537031eda29c02ea29674bc6854460c2"},
|
||||
{file = "zipp-3.0.0.tar.gz", hash = "sha256:7c0f8e91abc0dc07a5068f315c52cb30c66bfbc581e5b50704c8a2f6ebae794a"},
|
||||
{file = "zipp-3.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"},
|
||||
{file = "zipp-3.1.0.tar.gz", hash = "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ black = {version = "^19.0", allow-prereleases = true}
|
|||
isort = "^4.3"
|
||||
flake8 = "^3.7"
|
||||
flake8-html = "^0.4.0"
|
||||
mypy = "^0.761"
|
||||
mypy = "^0.770"
|
||||
mkdocs = "^1.0"
|
||||
mkdocs-material = "^4.6"
|
||||
mkautodoc = "^0.1.0"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import pytest
|
|||
from aiogram.api.client.session.base import BaseSession, T
|
||||
from aiogram.api.client.telegram import PRODUCTION, TelegramAPIServer
|
||||
from aiogram.api.methods import GetMe, Response, TelegramMethod
|
||||
from aiogram.utils.mixins import DataMixin
|
||||
|
||||
try:
|
||||
from asynctest import CoroutineMock, patch
|
||||
|
|
@ -31,7 +30,7 @@ class CustomSession(BaseSession):
|
|||
yield b"\f" * 10
|
||||
|
||||
|
||||
class TestBaseSession(DataMixin):
|
||||
class TestBaseSession():
|
||||
def test_init_api(self):
|
||||
session = CustomSession()
|
||||
assert session.api == PRODUCTION
|
||||
|
|
|
|||
|
|
@ -1,42 +1,11 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.utils.mixins import ContextInstanceMixin, DataMixin
|
||||
from aiogram.utils.mixins import ContextInstanceMixin
|
||||
|
||||
|
||||
class DataObject(DataMixin):
|
||||
class ContextObject(ContextInstanceMixin['ContextObject']):
|
||||
pass
|
||||
|
||||
|
||||
class ContextObject(ContextInstanceMixin):
|
||||
pass
|
||||
|
||||
|
||||
class TestDataMixin:
|
||||
def test_store_value(self):
|
||||
obj = DataObject()
|
||||
obj["foo"] = 42
|
||||
|
||||
assert "foo" in obj
|
||||
assert obj["foo"] == 42
|
||||
assert len(obj.data) == 1
|
||||
|
||||
def test_remove_value(self):
|
||||
obj = DataObject()
|
||||
obj["foo"] = 42
|
||||
del obj["foo"]
|
||||
|
||||
assert "key" not in obj
|
||||
assert len(obj.data) == 0
|
||||
|
||||
def test_getter(self):
|
||||
obj = DataObject()
|
||||
obj["foo"] = 42
|
||||
|
||||
assert obj.get("foo") == 42
|
||||
assert obj.get("bar") is None
|
||||
assert obj.get("baz", "test") == "test"
|
||||
|
||||
|
||||
class TestContextInstanceMixin:
|
||||
def test_empty(self):
|
||||
obj = ContextObject()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue