From 5c4dad906bd2011598fccd0ac6acb80e4cf29e37 Mon Sep 17 00:00:00 2001 From: K1rLes Date: Fri, 10 May 2024 23:59:57 +0500 Subject: [PATCH] =?UTF-8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B8=D0=B7=20aiogram'=D0=B0=20=D0=B8=20=D1=83?= =?UTF-8?q?=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD=D0=B8=D0=B5=20Ruff'=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix missing error logging (by @unintended) loosened pydantic upper bound to <2.8 (by @jorenham) Bump actions versions (by @Olegt0rr) --- .github/workflows/tests.yml | 5 +- aliceio/client/context_controller.py | 2 +- aliceio/client/session/aiohttp.py | 15 ++-- aliceio/client/session/base.py | 12 ++-- aliceio/client/session/middlewares/base.py | 7 +- aliceio/dispatcher/dispatcher.py | 18 +++-- aliceio/dispatcher/event/alice.py | 2 +- aliceio/dispatcher/event/handler.py | 6 +- aliceio/dispatcher/flags.py | 2 +- aliceio/dispatcher/middlewares/base.py | 1 - aliceio/dispatcher/middlewares/error.py | 2 +- aliceio/dispatcher/router.py | 15 ++-- aliceio/filters/base.py | 8 +-- aliceio/filters/exception.py | 2 +- aliceio/filters/magic_data.py | 4 +- aliceio/fsm/middlewares/api_storage.py | 2 +- aliceio/fsm/middlewares/fsm_context.py | 2 +- aliceio/fsm/state.py | 13 ++-- aliceio/fsm/storage/api.py | 4 -- aliceio/fsm/storage/base.py | 5 -- aliceio/fsm/storage/memory.py | 2 +- aliceio/fsm/storage/redis.py | 3 +- aliceio/methods/base.py | 4 +- aliceio/types/alice_request.py | 2 +- aliceio/types/analytic_event.py | 2 +- aliceio/types/analytics.py | 2 +- aliceio/types/application.py | 2 +- aliceio/types/audio_player_directive.py | 2 +- aliceio/types/audio_player_error.py | 2 +- aliceio/types/audio_player_item.py | 2 +- aliceio/types/big_image.py | 2 +- aliceio/types/card_footer.py | 2 +- aliceio/types/card_header.py | 2 +- aliceio/types/datetime.py | 2 +- aliceio/types/directives.py | 2 +- aliceio/types/entity.py | 2 +- aliceio/types/fio_entity.py | 2 +- aliceio/types/image_gallery.py | 2 +- aliceio/types/image_gallery_item.py | 2 +- aliceio/types/interfaces.py | 2 +- aliceio/types/item_image.py | 2 +- aliceio/types/items_list.py | 2 +- aliceio/types/markup.py | 2 +- aliceio/types/media_button.py | 2 +- aliceio/types/metadata.py | 2 +- aliceio/types/nlu.py | 2 +- aliceio/types/nlu_entity.py | 2 - aliceio/types/response.py | 2 +- aliceio/types/result.py | 2 +- aliceio/types/space_status.py | 2 +- aliceio/types/stream.py | 2 +- aliceio/types/text_button.py | 2 +- aliceio/types/timeout_event.py | 5 +- aliceio/types/tokens_entity.py | 2 +- aliceio/types/update.py | 12 ++-- aliceio/types/uploaded_image.py | 2 +- aliceio/types/uploaded_sound.py | 2 +- aliceio/types/url.py | 2 +- aliceio/utils/builders.py | 38 +++++----- aliceio/utils/funcs.py | 3 +- aliceio/utils/mixins.py | 40 ++++++----- aliceio/webhook/aiohttp_server.py | 8 +-- aliceio/webhook/security.py | 1 - examples/__init__.py | 0 examples/context_addition.py | 2 +- examples/echo_skill.py | 5 +- examples/echo_skill_ssl.py | 5 +- examples/error_handling.py | 10 +-- examples/fsm_form.py | 2 +- examples/fsm_games.py | 6 +- examples/middlewares.py | 2 +- examples/multi_file_skill/__init__.py | 0 pyproject.toml | 71 ++++++++++++++----- tests/mocked/mocked_skill.py | 4 +- tests/mocked/mocked_update.py | 4 +- .../test_api/test_client/test_alice_server.py | 16 +++-- .../test_session/test_aiohttp_session.py | 5 +- .../test_middlewares/test_manager.py | 4 +- .../test_middlewares/test_request_logging.py | 4 +- tests/test_api/test_client/test_skill.py | 2 +- .../test_types/test_audio_player_directive.py | 8 ++- tests/test_api/test_types/test_error_event.py | 4 +- tests/test_api/test_types/test_update.py | 6 +- tests/test_dispatcher/test_dispatcher.py | 11 +-- .../test_dispatcher/test_event/test_alice.py | 2 +- .../test_dispatcher/test_event/test_event.py | 3 +- .../test_event/test_handler.py | 15 ++-- .../test_response_converter.py | 4 +- .../test_middlewares/test_user_context.py | 4 +- tests/test_dispatcher/test_router.py | 6 +- tests/test_filters/test_exception.py | 6 +- tests/test_filters/test_state.py | 2 +- .../test_fsm/middlewares/test_api_storage.py | 6 +- tests/test_fsm/storage/test_storages.py | 2 +- tests/test_fsm/test_context.py | 5 +- tests/test_handlers/test_audio_player.py | 3 +- tests/test_webhook/test_aiohttp_server.py | 6 +- tests/test_webhook/test_security.py | 2 +- 98 files changed, 305 insertions(+), 241 deletions(-) create mode 100644 examples/__init__.py create mode 100644 examples/multi_file_skill/__init__.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index da0c9e4..ed26fc6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -54,7 +54,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} on ${{ matrix.os }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} cache: "pip" @@ -102,6 +102,7 @@ jobs: python-version: - 'pypy3.8' - 'pypy3.9' + - 'pypy3.10' defaults: # Windows sucks. Force use bash instead of PowerShell @@ -115,7 +116,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} on ${{ matrix.os }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} cache: "pip" diff --git a/aliceio/client/context_controller.py b/aliceio/client/context_controller.py index a732301..86f8983 100644 --- a/aliceio/client/context_controller.py +++ b/aliceio/client/context_controller.py @@ -15,7 +15,7 @@ class SkillContextController(BaseModel): def __init__( __pydantic_self__, *, - _skill: Optional["Skill"] = PrivateAttr(), + _skill: Optional["Skill"], **__pydantic_kwargs: Any, ) -> None: super().__init__( diff --git a/aliceio/client/session/aiohttp.py b/aliceio/client/session/aiohttp.py index 4778d32..95ddefb 100644 --- a/aliceio/client/session/aiohttp.py +++ b/aliceio/client/session/aiohttp.py @@ -7,7 +7,6 @@ Any, Dict, Iterable, - List, Optional, Tuple, Type, @@ -63,7 +62,7 @@ def _retrieve_basic(basic: _ProxyBasic) -> Dict[str, Any]: def _prepare_connector( chain_or_plain: _ProxyType, -) -> Tuple[Type["TCPConnector"], Dict[str, Any]]: +) -> Tuple[Type[TCPConnector], Dict[str, Any]]: from aiohttp_socks import ( # type: ignore ChainProxyConnector, ProxyConnector, @@ -80,9 +79,7 @@ def _prepare_connector( return ProxyConnector, _retrieve_basic(chain_or_plain) chain_or_plain = cast(_ProxyChain, chain_or_plain) - infos: List[ProxyInfo] = [] - for basic in chain_or_plain: - infos.append(ProxyInfo(**_retrieve_basic(basic))) + infos = [ProxyInfo(**_retrieve_basic(basic)) for basic in chain_or_plain] return ChainProxyConnector, {"proxy_infos": infos} @@ -105,7 +102,7 @@ def __init__(self, proxy: Optional[_ProxyType] = None, **kwargs: Any) -> None: except ImportError as exc: # pragma: no cover raise RuntimeError( "In order to use aiohttp client for proxy requests, install " - "https://pypi.org/project/aiohttp-socks/" + "https://pypi.org/project/aiohttp-socks/", ) from exc def _setup_proxy_connector(self, proxy: _ProxyType) -> None: @@ -205,10 +202,10 @@ async def make_request( headers=self._build_request_headers(skill), ) as resp: raw_result = await resp.text() - except asyncio.TimeoutError: - raise AliceNetworkError(message="AliceRequest timeout error") + except asyncio.TimeoutError as e: + raise AliceNetworkError(message="AliceRequest timeout error") from e except ClientError as e: - raise AliceNetworkError(message=f"{type(e).__name__}: {e}") + raise AliceNetworkError(message=f"{type(e).__name__}: {e}") from e response = self.check_response( skill=skill, method=method, diff --git a/aliceio/client/session/base.py b/aliceio/client/session/base.py index 9f183c8..97dc543 100644 --- a/aliceio/client/session/base.py +++ b/aliceio/client/session/base.py @@ -63,7 +63,7 @@ def check_response( except Exception as e: # Обрабатываемая ошибка не может быть поймана конкретным типом, # поскольку декодер можно кастомизировать и вызвать любое исключение. - raise ClientDecodeError("Failed to decode object", e, content) + raise ClientDecodeError("Failed to decode object", e, content) from e if HTTPStatus.OK <= status_code <= HTTPStatus.IM_USED: try: @@ -73,19 +73,22 @@ def check_response( context={"skill": skill}, ) except ValidationError as e: - raise ClientDecodeError("Failed to deserialize object", e, json_data) + raise ClientDecodeError( + "Failed to deserialize object", + e, + json_data, + ) from e try: response = ErrorResult.model_validate(json_data) except ValidationError as e: - raise ClientDecodeError("Failed to deserialize object", e, json_data) + raise ClientDecodeError("Failed to deserialize object", e, json_data) from e raise AliceAPIError(message=response.message) @abc.abstractmethod async def close(self) -> None: # pragma: no cover """Закрыть клиентскую сессию.""" - pass @abc.abstractmethod async def make_request( @@ -103,7 +106,6 @@ async def make_request( :return: :raise AliceApiError: """ - pass async def __call__( self, diff --git a/aliceio/client/session/middlewares/base.py b/aliceio/client/session/middlewares/base.py index 67f0725..4d69ad9 100644 --- a/aliceio/client/session/middlewares/base.py +++ b/aliceio/client/session/middlewares/base.py @@ -13,7 +13,7 @@ class NextRequestMiddlewareType(Protocol[AliceType]): # pragma: no cover async def __call__( self, - skill: "Skill", + skill: Skill, method: AliceMethod[AliceType], ) -> ApiResponse[AliceType]: pass @@ -23,7 +23,7 @@ class RequestMiddlewareType(Protocol): # pragma: no cover async def __call__( self, make_request: NextRequestMiddlewareType[AliceType], - skill: "Skill", + skill: Skill, method: AliceMethod[AliceType], ) -> ApiResponse[AliceType]: pass @@ -36,7 +36,7 @@ class BaseRequestMiddleware(ABC): async def __call__( self, make_request: NextRequestMiddlewareType[AliceType], - skill: "Skill", + skill: Skill, method: AliceMethod[AliceType], ) -> ApiResponse[AliceType]: """ @@ -49,4 +49,3 @@ async def __call__( :return: :class:`aiolice.methods.ApiResponse` """ - pass diff --git a/aliceio/dispatcher/dispatcher.py b/aliceio/dispatcher/dispatcher.py index 4720a89..74276a9 100644 --- a/aliceio/dispatcher/dispatcher.py +++ b/aliceio/dispatcher/dispatcher.py @@ -48,7 +48,7 @@ def __init__( :param kwargs: Остальные аргументы, будут переданы в обработчики как именованные аргументы """ - super(Dispatcher, self).__init__(name=name) + super().__init__(name=name) self.update = self.observers["update"] = AliceEventObserver( router=self, @@ -82,7 +82,7 @@ def __init__( self.update.outer_middleware(self.fsm) if use_api_storage: self.update.outer_middleware( - FSMApiStorageMiddleware(strategy=fsm_strategy) + FSMApiStorageMiddleware(strategy=fsm_strategy), ) self.update.outer_middleware(ResponseConvertMiddleware()) self.shutdown.register(self.fsm.close) @@ -116,7 +116,7 @@ def parent_router(self) -> Optional[Router]: У диспетчера нет родительского маршрутизатора и он не может быть включен ни в какие другие роутеры или диспетчеры. """ - return None # noqa: RET501 + return None @parent_router.setter def parent_router(self, value: Router) -> None: @@ -207,7 +207,11 @@ async def feed_raw_update( :param kwargs: """ parsed_update = Update.model_validate(update, context={"skill": skill}) - return await self.feed_update(skill=skill, update=parsed_update, **kwargs) + return await self._feed_webhook_update( + skill=skill, + update=parsed_update, + **kwargs, + ) async def _listen_update(self, update: Update, **kwargs: Any) -> Any: """ @@ -228,8 +232,9 @@ async def _listen_update(self, update: Update, **kwargs: Any) -> Any: "installed not latest version of aliceio framework" f"\nUpdate: {update.model_dump_json(exclude_unset=True)}", RuntimeWarning, + stacklevel=1, ) - raise SkipHandler() from e + raise SkipHandler from e kwargs.update(event_update=update) @@ -287,7 +292,7 @@ def release_waiter(*_: Any) -> None: timeout_handle = loop.call_later(self.response_timeout, release_waiter) process_updates: Future[Optional[AliceResponse]] = asyncio.ensure_future( - self._feed_webhook_update(skill=skill, update=update, **kwargs) + self._feed_webhook_update(skill=skill, update=update, **kwargs), ) process_updates.add_done_callback(release_waiter, context=ctx) @@ -325,6 +330,7 @@ async def _process_timeouted_update( "the skill dialog, so be careful and register ultra-fast handlers " "by `@.timeout` to respond to timeouted updates.", RuntimeWarning, + stacklevel=1, ) return await self._feed_webhook_update( skill=skill, diff --git a/aliceio/dispatcher/event/alice.py b/aliceio/dispatcher/event/alice.py index eb8dc89..667d9cb 100644 --- a/aliceio/dispatcher/event/alice.py +++ b/aliceio/dispatcher/event/alice.py @@ -71,7 +71,7 @@ def register( callback=callback, filters=[FilterObject(filter_) for filter_ in filters], flags=flags, - ) + ), ) return callback diff --git a/aliceio/dispatcher/event/handler.py b/aliceio/dispatcher/event/handler.py index ba58415..238f3ad 100644 --- a/aliceio/dispatcher/event/handler.py +++ b/aliceio/dispatcher/event/handler.py @@ -27,7 +27,7 @@ class CallableObject: def __post_init__(self) -> None: callback = inspect.unwrap(self.callback) self.awaitable = inspect.isawaitable(callback) or inspect.iscoroutinefunction( - callback + callback, ) spec = inspect.getfullargspec(callback) self.params = {*spec.args, *spec.kwonlyargs} @@ -71,7 +71,7 @@ def __post_init__(self) -> None: stacklevel=6, ) - super(FilterObject, self).__post_init__() + super(FilterObject, self).__post_init__() # noqa: UP008 if isinstance(self.callback, Filter): self.awaitable = True @@ -83,7 +83,7 @@ class HandlerObject(CallableObject): flags: Dict[str, Any] = field(default_factory=dict) def __post_init__(self) -> None: - super(HandlerObject, self).__post_init__() + super(HandlerObject, self).__post_init__() # noqa: UP008 callback = inspect.unwrap(self.callback) if inspect.isclass(callback) and issubclass(callback, BaseHandler): self.awaitable = True diff --git a/aliceio/dispatcher/flags.py b/aliceio/dispatcher/flags.py index be8c5be..cd4fa89 100644 --- a/aliceio/dispatcher/flags.py +++ b/aliceio/dispatcher/flags.py @@ -44,7 +44,7 @@ def __call__( ) -> Union[Callable[..., Any], "FlagDecorator"]: if value and kwargs: raise ValueError( - "The arguments `value` and **kwargs can not be used together" + "The arguments `value` and **kwargs can not be used together", ) if value is not None and callable(value): diff --git a/aliceio/dispatcher/middlewares/base.py b/aliceio/dispatcher/middlewares/base.py index ebf0d98..ce87716 100644 --- a/aliceio/dispatcher/middlewares/base.py +++ b/aliceio/dispatcher/middlewares/base.py @@ -25,4 +25,3 @@ async def __call__( :param data: Данные контекста. Будет сопоставлен с аргументами обработчика. :return: :class:`Any` """ - pass diff --git a/aliceio/dispatcher/middlewares/error.py b/aliceio/dispatcher/middlewares/error.py index 9050ddd..ee72167 100644 --- a/aliceio/dispatcher/middlewares/error.py +++ b/aliceio/dispatcher/middlewares/error.py @@ -12,7 +12,7 @@ class ErrorsMiddleware(BaseMiddleware[Update]): - def __init__(self, router: Router): + def __init__(self, router: Router) -> None: self.router = router async def __call__( diff --git a/aliceio/dispatcher/router.py b/aliceio/dispatcher/router.py index 4d28c3f..47c78b4 100644 --- a/aliceio/dispatcher/router.py +++ b/aliceio/dispatcher/router.py @@ -36,13 +36,16 @@ def __init__(self, *, name: Optional[str] = None) -> None: self.purchase = AliceEventObserver(router=self, event_name=EventType.PURCHASE) self.show_pull = AliceEventObserver(router=self, event_name=EventType.SHOW_PULL) self.button_pressed = AliceEventObserver( - router=self, event_name=EventType.BUTTON_PRESSED + router=self, + event_name=EventType.BUTTON_PRESSED, ) self.audio_player = AliceEventObserver( - router=self, event_name=EventType.AUDIO_PLAYER + router=self, + event_name=EventType.AUDIO_PLAYER, ) self.errors = self.error = AliceEventObserver( - router=self, event_name=EventType.ERROR + router=self, + event_name=EventType.ERROR, ) self.timeout = AliceEventObserver(router=self, event_name=EventType.TIMEOUT) @@ -157,7 +160,7 @@ def parent_router(self, router: Router) -> None: """ if not isinstance(router, Router): raise ValueError( - f"router should be instance of Router not {type(router).__name__!r}" + f"router should be instance of Router not {type(router).__name__!r}", ) if self._parent_router: raise RuntimeError(f"Router is already attached to {self._parent_router!r}") @@ -172,7 +175,7 @@ def parent_router(self, router: Router) -> None: parent = parent.parent_router self._parent_router = router - router._sub_routers.append(self) + router._sub_routers.append(self) # noqa: SLF001 def include_routers(self, *routers: Router) -> None: """ @@ -195,7 +198,7 @@ def include_router(self, router: Router) -> Router: if not isinstance(router, Router): raise ValueError( f"router should be instance of Router, " - f"not {type(router).__class__.__name__}" + f"not {type(router).__class__.__name__}", ) router.parent_router = self return router diff --git a/aliceio/filters/base.py b/aliceio/filters/base.py index a130018..2ca1b91 100644 --- a/aliceio/filters/base.py +++ b/aliceio/filters/base.py @@ -5,7 +5,7 @@ from aliceio.filters.logic import _InvertFilter -class Filter(ABC): +class Filter(ABC): # noqa: B024 """ Если вы хотите сделать собственные фильтры, такие же как встроенные фильтры, вам нужно будет написать подкласс с переопределением метода :code:`__call__` @@ -29,21 +29,19 @@ async def __call__( :return: :class:`bool` или :class:`Dict[str, Any]` """ - pass def __invert__(self) -> "_InvertFilter": from aliceio.filters.logic import invert_f return invert_f(self) - def update_handler_flags(self, flags: Dict[str, Any]) -> None: + def update_handler_flags(self, flags: Dict[str, Any]) -> None: # noqa: B027 """ Также, если вы хотите расширить флаги обработчика с помощью этого фильтра, вам следует реализовать этот метод :param flags: Существующие флаги, могут быть обновлены напрямую. """ - pass def _signature_to_string(self, *args: Any, **kwargs: Any) -> str: """ @@ -57,6 +55,6 @@ def _signature_to_string(self, *args: Any, **kwargs: Any) -> str: return f"{type(self).__name__}({', '.join(items)})" - def __await__(self): # type: ignore # pragma: no cover + def __await__(self): # type: ignore # pragma: no cover # noqa: ANN204 # Этот метод нужен только для проверки, никогда не вызывается return self.__call__ diff --git a/aliceio/filters/exception.py b/aliceio/filters/exception.py index 4b85a32..184324b 100644 --- a/aliceio/filters/exception.py +++ b/aliceio/filters/exception.py @@ -11,7 +11,7 @@ class ExceptionTypeFilter(Filter): __slots__ = ("exceptions",) - def __init__(self, *exceptions: Type[Exception]): + def __init__(self, *exceptions: Type[Exception]) -> None: """ :param exceptions: Типы исключений, на которые должен реагировать фильтр. """ diff --git a/aliceio/filters/magic_data.py b/aliceio/filters/magic_data.py index 94a4a91..79f86bb 100644 --- a/aliceio/filters/magic_data.py +++ b/aliceio/filters/magic_data.py @@ -9,14 +9,14 @@ class MagicData(Filter): """Этот фильтр помогает фильтровать события с контекстными данными.""" - __slots__ = "magic_data" + __slots__ = ("magic_data",) def __init__(self, magic_data: MagicFilter) -> None: self.magic_data = magic_data async def __call__(self, event: AliceEvent, *args: Any, **kwargs: Any) -> Any: return self.magic_data.resolve( - AttrDict({"event": event, **dict(enumerate(args)), **kwargs}) + AttrDict({"event": event, **dict(enumerate(args)), **kwargs}), ) def __str__(self) -> str: diff --git a/aliceio/fsm/middlewares/api_storage.py b/aliceio/fsm/middlewares/api_storage.py index 361570f..2fb1c2e 100644 --- a/aliceio/fsm/middlewares/api_storage.py +++ b/aliceio/fsm/middlewares/api_storage.py @@ -17,7 +17,7 @@ class FSMApiStorageMiddleware(BaseMiddleware[Update]): https://yandex.ru/dev/dialogs/alice/doc/session-persistence.html """ - def __init__(self, strategy: FSMStrategy = FSMStrategy.USER): + def __init__(self, strategy: FSMStrategy = FSMStrategy.USER) -> None: self.strategy = strategy async def __call__( diff --git a/aliceio/fsm/middlewares/fsm_context.py b/aliceio/fsm/middlewares/fsm_context.py index e3ef230..433dbd7 100644 --- a/aliceio/fsm/middlewares/fsm_context.py +++ b/aliceio/fsm/middlewares/fsm_context.py @@ -40,7 +40,7 @@ async def __call__( FSM_STORAGE_KEY: self.storage, FSM_CONTEXT_KEY: context, RAW_STATE_KEY: await context.get_state(), - } + }, ) return await handler(event, data) diff --git a/aliceio/fsm/state.py b/aliceio/fsm/state.py index 843df0e..8e6c579 100644 --- a/aliceio/fsm/state.py +++ b/aliceio/fsm/state.py @@ -74,13 +74,18 @@ class StatesGroupMeta(type): __state_names__: Tuple[str, ...] @no_type_check - def __new__(mcs, name, bases, namespace, **kwargs): - cls = super(StatesGroupMeta, mcs).__new__(mcs, name, bases, namespace) + def __new__(mcs, name, bases, namespace, **kwargs): # noqa: ANN204, ANN001, ANN003 + cls = super(StatesGroupMeta, mcs).__new__( # noqa: UP008 + mcs, + name, + bases, + namespace, + ) states = [] childs = [] - for name, arg in namespace.items(): + for arg in namespace.values(): if isinstance(arg, State): states.append(arg) elif inspect.isclass(arg) and issubclass(arg, StatesGroup): @@ -97,7 +102,7 @@ def __new__(mcs, name, bases, namespace, **kwargs): @property def __full_group_name__(cls) -> str: if cls.__parent__: - return ".".join((cls.__parent__.__full_group_name__, cls.__name__)) + return f"{cls.__parent__.__full_group_name__}.{cls.__name__}" return cls.__name__ @property diff --git a/aliceio/fsm/storage/api.py b/aliceio/fsm/storage/api.py index af3e885..ba0e96b 100644 --- a/aliceio/fsm/storage/api.py +++ b/aliceio/fsm/storage/api.py @@ -4,10 +4,6 @@ class ApiStorageRecord(MemoryStorageRecord): """Аналогичен :class:`MemoryStorageRecord`.""" - pass - class ApiStorage(MemoryStorage): """Аналогичен :class:`MemoryStorage`.""" - - pass diff --git a/aliceio/fsm/storage/base.py b/aliceio/fsm/storage/base.py index 40e060b..7dd1ab8 100644 --- a/aliceio/fsm/storage/base.py +++ b/aliceio/fsm/storage/base.py @@ -29,7 +29,6 @@ async def set_state(self, key: StorageKey, state: StateType = None) -> None: :param key: Ключ. :param state: Новое состояние. """ - pass @abstractmethod async def get_state(self, key: StorageKey) -> Optional[str]: @@ -39,7 +38,6 @@ async def get_state(self, key: StorageKey) -> Optional[str]: :param key: Ключ. :return: Текущее состояние. """ - pass @abstractmethod async def set_data(self, key: StorageKey, data: Dict[str, Any]) -> None: @@ -49,7 +47,6 @@ async def set_data(self, key: StorageKey, data: Dict[str, Any]) -> None: :param key: Ключ. :param data: Новые данные. """ - pass @abstractmethod async def get_data(self, key: StorageKey) -> Dict[str, Any]: @@ -59,7 +56,6 @@ async def get_data(self, key: StorageKey) -> Dict[str, Any]: :param key: Ключ. :return: Текущие данные. """ - pass async def update_data( self, @@ -83,4 +79,3 @@ async def close(self) -> None: # pragma: no cover """ Закрыть хранилище (подключение к бд, файлу итп.) """ - pass diff --git a/aliceio/fsm/storage/memory.py b/aliceio/fsm/storage/memory.py index 218f232..e3c94dc 100644 --- a/aliceio/fsm/storage/memory.py +++ b/aliceio/fsm/storage/memory.py @@ -24,7 +24,7 @@ class MemoryStorage(BaseStorage): def __init__(self) -> None: self.storage: DefaultDict[StorageKey, MemoryStorageRecord] = defaultdict( - MemoryStorageRecord + MemoryStorageRecord, ) async def close(self) -> None: diff --git a/aliceio/fsm/storage/redis.py b/aliceio/fsm/storage/redis.py index 6f2be5c..8464947 100644 --- a/aliceio/fsm/storage/redis.py +++ b/aliceio/fsm/storage/redis.py @@ -24,7 +24,6 @@ def build(self, key: StorageKey, part: Literal["data", "state"]) -> str: :param part: Часть записи. :return: ключ, который будет использоваться в запросах Redis. """ - pass class DefaultKeyBuilder(KeyBuilder): @@ -66,7 +65,7 @@ def build(self, key: StorageKey, part: Literal["data", "state"]) -> str: "Redis key builder is not configured to use key destiny other the default.\n" # noqa: E501 "\n" "Probably, you should set `with_destiny=True` in for DefaultKeyBuilder.\n" # noqa: E501 - "E.g: `RedisStorage(redis, key_builder=DefaultKeyBuilder(with_destiny=True))`" # noqa: E501 + "E.g: `RedisStorage(redis, key_builder=DefaultKeyBuilder(with_destiny=True))`", # noqa: E501 ) parts.append(part) return self.separator.join(parts) diff --git a/aliceio/methods/base.py b/aliceio/methods/base.py index 581ead9..e25299b 100644 --- a/aliceio/methods/base.py +++ b/aliceio/methods/base.py @@ -48,7 +48,7 @@ def __http_method__(self) -> str: def api_url(self, api_server: AliceAPIServer) -> str: pass - async def emit(self, skill: "Skill") -> AliceType: + async def emit(self, skill: Skill) -> AliceType: if not self._skill: self._skill = self.skill return await skill(self) @@ -61,6 +61,6 @@ def __await__(self) -> Generator[Any, None, AliceType]: "please call it explicilty " "with skill instance `await skill(method)`\n" "or mount method to a skill instance `method.as_(skill)` " - "and then call it `await method()`" + "and then call it `await method()`", ) return self.emit(skill).__await__() diff --git a/aliceio/types/alice_request.py b/aliceio/types/alice_request.py index 3f30a28..f8022f1 100644 --- a/aliceio/types/alice_request.py +++ b/aliceio/types/alice_request.py @@ -12,7 +12,7 @@ class AliceRequest(AliceObject): Запрос с информацией от пользователя от API Алисы. [Source](https://yandex.ru/dev/dialogs/alice/doc/request.html#request__request-desc) - """ # noqa: E501 + """ type: str payload: Optional[Payload] = None diff --git a/aliceio/types/analytic_event.py b/aliceio/types/analytic_event.py index 3a68077..2dc0b7c 100644 --- a/aliceio/types/analytic_event.py +++ b/aliceio/types/analytic_event.py @@ -10,7 +10,7 @@ class AnalyticEvent(MutableAliceObject): Событие для аналитики. [Source](https://yandex.ru/dev/dialogs/alice/doc/response.html#response__events-desc) - """ # noqa: E501 + """ name: str value: CustomEventData diff --git a/aliceio/types/analytics.py b/aliceio/types/analytics.py index bac9a32..cd3915e 100644 --- a/aliceio/types/analytics.py +++ b/aliceio/types/analytics.py @@ -9,7 +9,7 @@ class Analytics(MutableAliceObject): Данные для аналитики AppMetrica. [Source](https://yandex.ru/dev/dialogs/alice/doc/response.html#response__analytics-desc) - """ # noqa: E501 + """ events: List[AnalyticEvent] diff --git a/aliceio/types/application.py b/aliceio/types/application.py index e48f955..d18eabd 100644 --- a/aliceio/types/application.py +++ b/aliceio/types/application.py @@ -8,7 +8,7 @@ class Application(MutableAliceObject): Приложение из :class:`Session`. [Source](https://yandex.ru/dev/dialogs/alice/doc/request.html#request__application-desc) - """ # noqa: E501 + """ application_id: str diff --git a/aliceio/types/audio_player_directive.py b/aliceio/types/audio_player_directive.py index 199db80..2e52b83 100644 --- a/aliceio/types/audio_player_directive.py +++ b/aliceio/types/audio_player_directive.py @@ -39,7 +39,7 @@ def action_validate(cls, v: str) -> str: if v.capitalize() not in Action.values(): raise AliceWrongFieldError( f"AudioPlayer action must be " - f'{", ".join(atype for atype in Action)}, not "{v}"' + f'{", ".join(atype for atype in Action)}, not "{v}"', ) return v.capitalize() diff --git a/aliceio/types/audio_player_error.py b/aliceio/types/audio_player_error.py index 1a9b5af..872e1a6 100644 --- a/aliceio/types/audio_player_error.py +++ b/aliceio/types/audio_player_error.py @@ -8,7 +8,7 @@ class AudioPlayerError(AliceObject): Ошибка аудиоплеера. [Source](https://yandex.ru/dev/dialogs/alice/doc/request-audioplayer.html#request-audioplayer__playback-failed) - """ # noqa: E501 + """ message: str type: str diff --git a/aliceio/types/audio_player_item.py b/aliceio/types/audio_player_item.py index 10071c2..b7203c6 100644 --- a/aliceio/types/audio_player_item.py +++ b/aliceio/types/audio_player_item.py @@ -10,7 +10,7 @@ class AudioPlayerItem(MutableAliceObject): Данные директивы аудиоплеера. [Source](https://yandex.ru/dev/dialogs/alice/doc/response-audio-player.html#direct-play__audio-player-item-desc) - """ # noqa: E501 + """ stream: Stream metadata: Optional[Metadata] = None diff --git a/aliceio/types/big_image.py b/aliceio/types/big_image.py index 16f9ef9..377c951 100644 --- a/aliceio/types/big_image.py +++ b/aliceio/types/big_image.py @@ -46,6 +46,6 @@ def __init__( def type_validate(cls, v: str) -> str: if v.lower() != CardType.BIG_IMAGE.lower(): raise AliceWrongFieldError( - f'BigImage type must be "{CardType.BIG_IMAGE}", not "{v}"' + f'BigImage type must be "{CardType.BIG_IMAGE}", not "{v}"', ) return CardType.BIG_IMAGE diff --git a/aliceio/types/card_footer.py b/aliceio/types/card_footer.py index 675a114..aa1178f 100644 --- a/aliceio/types/card_footer.py +++ b/aliceio/types/card_footer.py @@ -9,7 +9,7 @@ class CardFooter(MutableAliceObject): Текст и кнопки под :class:`ItemsList`. [Source](https://yandex.ru/dev/dialogs/alice/doc/response-card-itemslist.html#response-card-itemslist__footer-desc) - """ # noqa: E501 + """ text: Optional[str] = None # Optional XD button: Optional[MediaButton] = None diff --git a/aliceio/types/card_header.py b/aliceio/types/card_header.py index 1021306..d10e502 100644 --- a/aliceio/types/card_header.py +++ b/aliceio/types/card_header.py @@ -8,7 +8,7 @@ class CardHeader(MutableAliceObject): Заголовок :class:`ItemsList`. [Source](https://yandex.ru/dev/dialogs/alice/doc/response-card-itemslist.html#response-card-itemslist__header-desc) - """ # noqa: E501 + """ text: Optional[str] = None # Optional XD diff --git a/aliceio/types/datetime.py b/aliceio/types/datetime.py index aa4f6a2..4e712b1 100644 --- a/aliceio/types/datetime.py +++ b/aliceio/types/datetime.py @@ -8,7 +8,7 @@ class DateTimeEntity(NLUEntity): NLU Entity Даты и времени. [Source](https://yandex.ru/dev/dialogs/alice/doc/naming-entities.html#naming-entities__datetime) - """ # noqa: E501 + """ year: Optional[int] = None month: Optional[int] = None diff --git a/aliceio/types/directives.py b/aliceio/types/directives.py index d85ec6d..5e58a59 100644 --- a/aliceio/types/directives.py +++ b/aliceio/types/directives.py @@ -14,7 +14,7 @@ class Directives(MutableAliceObject): [Source 1](https://yandex.ru/dev/dialogs/alice/doc/response-audio-player.html) [Source 2](https://yandex.ru/dev/dialogs/alice/doc/response-start-account-linking.html) - """ # noqa: E501 + """ audio_player: Optional[AudioPlayerDirective] = None start_account_linking: Optional[StartAccountLinking] = None diff --git a/aliceio/types/entity.py b/aliceio/types/entity.py index b856db4..4fea368 100644 --- a/aliceio/types/entity.py +++ b/aliceio/types/entity.py @@ -15,7 +15,7 @@ class Entity(MutableAliceObject): NLU Entity [Source](https://yandex.ru/dev/dialogs/alice/doc/request-simpleutterance.html#request-simpleutterance__entities-desc) - """ # noqa: E501 + """ type: str tokens: TokensEntity diff --git a/aliceio/types/fio_entity.py b/aliceio/types/fio_entity.py index 9503500..0bb6dca 100644 --- a/aliceio/types/fio_entity.py +++ b/aliceio/types/fio_entity.py @@ -8,7 +8,7 @@ class FIOEntity(NLUEntity): NLU Entity Фамилии, имени и отчества. [Source](https://yandex.ru/dev/dialogs/alice/doc/naming-entities.html#naming-entities__fio) - """ # noqa: E501 + """ first_name: Optional[str] = None patronymic_name: Optional[str] = None diff --git a/aliceio/types/image_gallery.py b/aliceio/types/image_gallery.py index 99e79db..913088a 100644 --- a/aliceio/types/image_gallery.py +++ b/aliceio/types/image_gallery.py @@ -40,6 +40,6 @@ def __init__( def type_validate(cls, v: str) -> str: if v.lower() != CardType.IMAGE_GALLERY.lower(): raise AliceWrongFieldError( - f'ImageGallery type must be "{CardType.IMAGE_GALLERY}", not "{v}"' + f'ImageGallery type must be "{CardType.IMAGE_GALLERY}", not "{v}"', ) return CardType.IMAGE_GALLERY diff --git a/aliceio/types/image_gallery_item.py b/aliceio/types/image_gallery_item.py index 864b768..7abe57e 100644 --- a/aliceio/types/image_gallery_item.py +++ b/aliceio/types/image_gallery_item.py @@ -9,7 +9,7 @@ class ImageGalleryItem(MutableAliceObject): Изображение в :class:`ImageGallery`. [Source](https://yandex.ru/dev/dialogs/alice/doc/response-card-imagegallery.html#response-card-imagegallery__items-desc) - """ # noqa: E501 + """ image_id: str title: Optional[str] = None diff --git a/aliceio/types/interfaces.py b/aliceio/types/interfaces.py index 4b8108b..472d0ec 100644 --- a/aliceio/types/interfaces.py +++ b/aliceio/types/interfaces.py @@ -12,7 +12,7 @@ class Interfaces(AliceObject): Интерфейсы, доступные на устройстве пользователя. [Source](https://yandex.ru/dev/dialogs/alice/doc/request.html#request__interfaces-desc) - """ # noqa: E501 + """ account_linking: Optional[AccountLinking] = None screen: Optional[Screen] = None diff --git a/aliceio/types/item_image.py b/aliceio/types/item_image.py index 43c598c..1b3e739 100644 --- a/aliceio/types/item_image.py +++ b/aliceio/types/item_image.py @@ -9,7 +9,7 @@ class ItemImage(MutableAliceObject): Изображение в :class:`ItemsList` [Source](https://yandex.ru/dev/dialogs/alice/doc/response-card-itemslist.html#response-card-itemslist__items-desc) - """ # noqa: E501 + """ image_id: Optional[str] = None # Optional XD title: Optional[str] = None diff --git a/aliceio/types/items_list.py b/aliceio/types/items_list.py index d0dab53..0001cd0 100644 --- a/aliceio/types/items_list.py +++ b/aliceio/types/items_list.py @@ -49,6 +49,6 @@ def __init__( def type_validate(cls, v: str) -> str: if v.lower() != CardType.ITEMS_LIST.lower(): raise AliceWrongFieldError( - f'ItemsList type must be "{CardType.ITEMS_LIST}", not "{v}"' + f'ItemsList type must be "{CardType.ITEMS_LIST}", not "{v}"', ) return CardType.ITEMS_LIST diff --git a/aliceio/types/markup.py b/aliceio/types/markup.py index 2a5eaf2..32c2f57 100644 --- a/aliceio/types/markup.py +++ b/aliceio/types/markup.py @@ -9,7 +9,7 @@ class Markup(AliceObject): Объект отсутствует, если ни одно из вложенных свойств не применимо. [Source](https://yandex.ru/dev/dialogs/alice/doc/request-simpleutterance.html#request-simpleutterance__markup-desc) - """ # noqa: E501 + """ dangerous_context: bool diff --git a/aliceio/types/media_button.py b/aliceio/types/media_button.py index a169cc7..bd48bd1 100644 --- a/aliceio/types/media_button.py +++ b/aliceio/types/media_button.py @@ -10,7 +10,7 @@ class MediaButton(MutableAliceObject): [Source 1](https://yandex.ru/dev/dialogs/alice/doc/response-card-imagegallery.html#response-card-imagegallery__items-button-desc) [Source 2](https://yandex.ru/dev/dialogs/alice/doc/response-card-itemslist.html#response-card-itemslist__items-button-desc) - """ # noqa: E501 + """ text: str url: str diff --git a/aliceio/types/metadata.py b/aliceio/types/metadata.py index 4e21100..0571790 100644 --- a/aliceio/types/metadata.py +++ b/aliceio/types/metadata.py @@ -9,7 +9,7 @@ class Metadata(MutableAliceObject): Метадата аудиоплеера. [Source](https://yandex.ru/dev/dialogs/alice/doc/response-audio-player.html#direct-play__audio-player-item-metadata-desc) - """ # noqa: E501 + """ title: Optional[str] = None sub_title: Optional[str] = None diff --git a/aliceio/types/nlu.py b/aliceio/types/nlu.py index e297d35..c617287 100644 --- a/aliceio/types/nlu.py +++ b/aliceio/types/nlu.py @@ -11,7 +11,7 @@ class NLU(AliceObject): Слова и сущности, которые Диалоги извлекли из запроса пользователя. [Source](https://yandex.ru/dev/dialogs/alice/doc/request-simpleutterance.html#request-simpleutterance__nlu-desc) - """ # noqa: E501 + """ tokens: List[str] entities: List[Entity] diff --git a/aliceio/types/nlu_entity.py b/aliceio/types/nlu_entity.py index 76b6168..05074be 100644 --- a/aliceio/types/nlu_entity.py +++ b/aliceio/types/nlu_entity.py @@ -5,5 +5,3 @@ class NLUEntity(AliceObject, ABC): """Родителский класс для NLU сущностей""" - - pass diff --git a/aliceio/types/response.py b/aliceio/types/response.py index a82c68d..49608c6 100644 --- a/aliceio/types/response.py +++ b/aliceio/types/response.py @@ -12,7 +12,7 @@ class Response(AliceObject): Ответ для API Алисы с полезной для пользователя информацией. [Source](https://yandex.ru/dev/dialogs/alice/doc/response.html#response__response-desc) - """ # noqa: E501 + """ text: str tts: Optional[str] = None diff --git a/aliceio/types/result.py b/aliceio/types/result.py index eb228ad..317bd05 100644 --- a/aliceio/types/result.py +++ b/aliceio/types/result.py @@ -8,7 +8,7 @@ class Result(AliceObject): Ответ на удаление файла. [Source](https://yandex.ru/dev/dialogs/alice/doc/resource-upload.html#http-images-load__delete) - """ # noqa: E501 + """ result: str diff --git a/aliceio/types/space_status.py b/aliceio/types/space_status.py index c66d2ed..562da58 100644 --- a/aliceio/types/space_status.py +++ b/aliceio/types/space_status.py @@ -9,7 +9,7 @@ class SpaceStatus(AliceObject): Оставшееся место в байтах для изображений и звуков. [Source](https://yandex.ru/dev/dialogs/alice/doc/resource-upload.html#http-images-load__quota) - """ # noqa: E501 + """ images: PreQuota sounds: PreQuota diff --git a/aliceio/types/stream.py b/aliceio/types/stream.py index 0bd1e96..9c2460a 100644 --- a/aliceio/types/stream.py +++ b/aliceio/types/stream.py @@ -8,7 +8,7 @@ class Stream(MutableAliceObject): Описание аудиопотока. [Source](https://yandex.ru/dev/dialogs/alice/doc/response-audio-player.html#direct-play__audio-player-item-stream-desc) - """ # noqa: E501 + """ url: str offset_ms: int diff --git a/aliceio/types/text_button.py b/aliceio/types/text_button.py index 6d17735..f698d7c 100644 --- a/aliceio/types/text_button.py +++ b/aliceio/types/text_button.py @@ -9,7 +9,7 @@ class TextButton(MutableAliceObject): Кнопка под сообщением навыка или над клавиатурой пользователя. [Source](https://yandex.ru/dev/dialogs/alice/doc/response.html#response__buttons-desc) - """ # noqa: E501 + """ title: str url: Optional[str] = None diff --git a/aliceio/types/timeout_event.py b/aliceio/types/timeout_event.py index f8c60a7..89fc365 100644 --- a/aliceio/types/timeout_event.py +++ b/aliceio/types/timeout_event.py @@ -1,3 +1,4 @@ +import contextlib from typing import TYPE_CHECKING, Any, cast from ..enums import EventType @@ -25,10 +26,8 @@ def event_type(self) -> str: def model_post_init(self, __context: Any) -> None: MutableAliceObject.model_post_init(self, __context) - try: + with contextlib.suppress(UpdateTypeLookupError): self._event_model_validate(self._real_event_type, __context) - except UpdateTypeLookupError: # pragma: no cover - pass @property def _real_event_type(self) -> str: diff --git a/aliceio/types/tokens_entity.py b/aliceio/types/tokens_entity.py index 475d95f..b48cd15 100644 --- a/aliceio/types/tokens_entity.py +++ b/aliceio/types/tokens_entity.py @@ -8,7 +8,7 @@ class TokensEntity(AliceObject): start и end из request.nlu.entities. [Source](https://yandex.ru/dev/dialogs/alice/doc/request-simpleutterance.html#request-simpleutterance__entities-desc) - """ # noqa: E501 + """ start: int end: int diff --git a/aliceio/types/update.py b/aliceio/types/update.py index 200262e..1950c4e 100644 --- a/aliceio/types/update.py +++ b/aliceio/types/update.py @@ -1,3 +1,4 @@ +import contextlib from typing import TYPE_CHECKING, Any, ClassVar, Dict, Optional, Type, cast from ..enums import EventType, RequestType @@ -80,18 +81,16 @@ def event(self) -> AliceEvent: def event_type(self) -> str: if (event_type := req_type_to_event_type.get(self.request.type)) is None: raise UpdateTypeLookupError( - "Update does not contain any known event type." + "Update does not contain any known event type.", ) return str(event_type) def model_post_init(self, __context: Any) -> None: super().model_post_init(__context) - try: - self._event_model_validate(self.event_type, __context) - except UpdateTypeLookupError: + with contextlib.suppress(UpdateTypeLookupError): # При работе ошибка возникнет ещё раз в диспетчере, # здесь она глушится для работы тестов - pass + self._event_model_validate(self.event_type, __context) def _event_model_validate(self, event_type: str, __context: Any) -> None: """ @@ -103,7 +102,8 @@ def _event_model_validate(self, event_type: str, __context: Any) -> None: self, event_type, event_type_to_event_model[event_type].model_validate( - dump, context=__context + dump, + context=__context, ), ) diff --git a/aliceio/types/uploaded_image.py b/aliceio/types/uploaded_image.py index c1c2b49..f5188ba 100644 --- a/aliceio/types/uploaded_image.py +++ b/aliceio/types/uploaded_image.py @@ -12,7 +12,7 @@ class UploadedImage(AliceObject): [Source 2](https://yandex.ru/dev/dialogs/alice/doc/resource-upload.html#http-images-load__upload-file) [Source 3](https://yandex.ru/dev/dialogs/alice/doc/resource-upload.html#http-images-load__list) - """ # noqa: E501 + """ id: str size: int diff --git a/aliceio/types/uploaded_sound.py b/aliceio/types/uploaded_sound.py index 2cff785..4f7582b 100644 --- a/aliceio/types/uploaded_sound.py +++ b/aliceio/types/uploaded_sound.py @@ -10,7 +10,7 @@ class UploadedSound(AliceObject): [Source 1](https://yandex.ru/dev/dialogs/alice/doc/resource-sounds-upload.html#http-load__upload-file) [Source 2](https://yandex.ru/dev/dialogs/alice/doc/resource-sounds-upload.html#http-load__list) - """ # noqa: E501 + """ id: str skillId: str diff --git a/aliceio/types/url.py b/aliceio/types/url.py index ef9ffd1..56921c6 100644 --- a/aliceio/types/url.py +++ b/aliceio/types/url.py @@ -10,7 +10,7 @@ class URL(AliceObject): [Source 1](https://yandex.ru/dev/dialogs/alice/doc/response-audio-player.html#direct-play__audio-player-item-metadata-art-desc) [Source 2](https://yandex.ru/dev/dialogs/alice/doc/response-audio-player.html#direct-play__audio-player-item-metadata-background-image-desc) - """ # noqa: E501 + """ url: Optional[str] = None diff --git a/aliceio/utils/builders.py b/aliceio/utils/builders.py index 72e2a63..616d622 100644 --- a/aliceio/utils/builders.py +++ b/aliceio/utils/builders.py @@ -23,6 +23,7 @@ class Builder(ABC, Generic[Collection, Item]): """ Базовый класс билдера """ + _items: List[Item] @abstractmethod @@ -32,12 +33,10 @@ def __init__(self) -> None: @abstractmethod def add(self, item: Item) -> Self: """Добавить айтем в билдер.""" - pass @abstractmethod def to_collection(self) -> Collection: """Создать коллекцию из текущего билдера.""" - pass def __len__(self) -> int: """Узнать длину коллекции.""" @@ -62,12 +61,10 @@ def add( title: Optional[str] = None, description: Optional[str] = None, button: Optional[MediaButton] = None, - ) -> Self: - ... + ) -> Self: ... @overload - def add(self, item: ItemImage, /) -> Self: - ... + def add(self, item: ItemImage, /) -> Self: ... def add( self, @@ -88,12 +85,10 @@ def add( return self @overload - def set_header(self, text: str, /) -> Self: - ... + def set_header(self, text: str, /) -> Self: ... @overload - def set_header(self, header: CardHeader, /) -> Self: - ... + def set_header(self, header: CardHeader, /) -> Self: ... def set_header(self, header: Union[CardHeader, str], /) -> Self: if isinstance(header, str): @@ -102,12 +97,15 @@ def set_header(self, header: Union[CardHeader, str], /) -> Self: return self @overload - def set_footer(self, text: str, /, button: Optional[MediaButton] = None) -> Self: - ... + def set_footer( + self, + text: str, + /, + button: Optional[MediaButton] = None, + ) -> Self: ... @overload - def set_footer(self, footer: CardFooter, /) -> Self: - ... + def set_footer(self, footer: CardFooter, /) -> Self: ... def set_footer( self, @@ -142,12 +140,10 @@ def add( /, title: Optional[str] = None, button: Optional[MediaButton] = None, - ) -> Self: - ... + ) -> Self: ... @overload - def add(self, item: ImageGalleryItem, /) -> Self: - ... + def add(self, item: ImageGalleryItem, /) -> Self: ... def add( self, @@ -177,12 +173,10 @@ def add( url: Optional[str] = None, payload: Optional[Payload] = None, hide: bool = True, - ) -> Self: - ... + ) -> Self: ... @overload - def add(self, item: TextButton, /) -> Self: - ... + def add(self, item: TextButton, /) -> Self: ... def add( self, diff --git a/aliceio/utils/funcs.py b/aliceio/utils/funcs.py index 57e8d81..c9539f2 100644 --- a/aliceio/utils/funcs.py +++ b/aliceio/utils/funcs.py @@ -10,8 +10,7 @@ class PrepareValue(Protocol): # pragma: no cover - def __call__(self, value: Any, files: Dict[str, Any]) -> Any: - ... + def __call__(self, value: Any, files: Dict[str, Any]) -> Any: ... # Ключи, у которых значение = None, не пропускаются, потому что иначе не получится diff --git a/aliceio/utils/mixins.py b/aliceio/utils/mixins.py index 07f755f..b282c61 100644 --- a/aliceio/utils/mixins.py +++ b/aliceio/utils/mixins.py @@ -10,12 +10,14 @@ class DataMixin: + _data: Optional[Dict[str, Any]] + @property def data(self) -> Dict[str, Any]: data: Optional[Dict[str, Any]] = getattr(self, "_data", None) if data is None: data = {} - setattr(self, "_data", data) + self._data = data return data def __getitem__(self, key: str) -> Any: @@ -44,33 +46,37 @@ def __init_subclass__(cls, **kwargs: Any) -> None: super().__init_subclass__() cls.__context_instance = contextvars.ContextVar(f"instance_{cls.__name__}") - @overload # noqa: F811 + @overload @classmethod - def get_current(cls) -> Optional[ContextInstance]: # pragma: no cover # noqa: F811 + def get_current(cls) -> Optional[ContextInstance]: # pragma: no cover ... - @overload # noqa: F811 + @overload @classmethod - def get_current( # noqa: F811 - cls, no_error: Literal[True] - ) -> Optional[ContextInstance]: # pragma: no cover # noqa: F811 + def get_current( + cls, + no_error: Literal[True], + ) -> Optional[ContextInstance]: # pragma: no cover ... - @overload # noqa: F811 + @overload @classmethod - def get_current( # noqa: F811 - cls, no_error: Literal[False] - ) -> ContextInstance: # pragma: no cover # noqa: F811 + def get_current( + cls, + no_error: Literal[False], + ) -> ContextInstance: # pragma: no cover ... - @classmethod # noqa: F811 - def get_current( # noqa: F811 - cls, no_error: bool = True - ) -> Optional[ContextInstance]: # pragma: no cover # noqa: F811 + @classmethod + def get_current( + cls, + no_error: bool = True, + ) -> Optional[ContextInstance]: # pragma: no cover # on mypy 0.770 I catch that contextvars.ContextVar # always contextvars.ContextVar[Any] cls.__context_instance = cast( - contextvars.ContextVar[ContextInstance], cls.__context_instance + contextvars.ContextVar[ContextInstance], + cls.__context_instance, ) try: @@ -88,7 +94,7 @@ def set_current(cls, value: ContextInstance) -> contextvars.Token[ContextInstanc if not isinstance(value, cls): raise TypeError( f"Value should be instance of {cls.__name__!r} " - f"not {type(value).__name__!r}" + f"not {type(value).__name__!r}", ) return cls.__context_instance.set(value) diff --git a/aliceio/webhook/aiohttp_server.py b/aliceio/webhook/aiohttp_server.py index 1c8f665..14f31ff 100644 --- a/aliceio/webhook/aiohttp_server.py +++ b/aliceio/webhook/aiohttp_server.py @@ -83,9 +83,10 @@ async def _ip_filter_middleware(request: web.Request, handler: Handler) -> Any: ip_address, accept = check_ip(ip_filter=ip_filter, request=request) if not accept: loggers.webhook.warning( - "Blocking request from an unauthorized IP: %s", ip_address + "Blocking request from an unauthorized IP: %s", + ip_address, ) - raise web.HTTPUnauthorized() + raise web.HTTPUnauthorized return await handler(request) return _ip_filter_middleware @@ -142,7 +143,6 @@ async def resolve_skill(self, request: web.Request) -> Skill: :param request: :return: Экземпляр навыка. """ - pass @abstractmethod async def _handle_request( @@ -164,7 +164,7 @@ async def _handle_request( # Сделать здесь обработку, если прилетает некорректный update? async def _update_validate(self, skill: Skill, request: web.Request) -> Update: json_data = self._convert_show_pull_to_normal_request( - await request.json(loads=self.json_loads) + await request.json(loads=self.json_loads), ) return Update.model_validate(json_data, context={"skill": skill}) diff --git a/aliceio/webhook/security.py b/aliceio/webhook/security.py index b635b9c..42292a7 100644 --- a/aliceio/webhook/security.py +++ b/aliceio/webhook/security.py @@ -18,7 +18,6 @@ IPv4Network("178.154.128.0/18"), IPv4Network("185.32.187.0/24"), IPv4Network("213.180.192.0/19"), - # IPv6Network("2a02:6b8::/29"), # ? :( ] diff --git a/examples/__init__.py b/examples/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/context_addition.py b/examples/context_addition.py index 54a9286..e25c2d6 100644 --- a/examples/context_addition.py +++ b/examples/context_addition.py @@ -34,7 +34,7 @@ class RandomNumberFilter(BaseFilter): async def __call__( self, message: Message, - event_from_user: User + event_from_user: User, # Фильтры также могут принимать данные из контекста как обработчики ) -> Union[bool, Dict[str, Any]]: if message.command == "число": diff --git a/examples/echo_skill.py b/examples/echo_skill.py index 7dc3bd2..e3a98f7 100644 --- a/examples/echo_skill.py +++ b/examples/echo_skill.py @@ -13,10 +13,7 @@ @router.message() async def echo(message: Message) -> AliceResponse: - if message.session.new: - text = "Привет!" - else: - text = message.original_utterance + text = "Привет!" if message.session.new else message.original_utterance return AliceResponse(response=Response(text=text)) diff --git a/examples/echo_skill_ssl.py b/examples/echo_skill_ssl.py index 5c73376..f8066cb 100644 --- a/examples/echo_skill_ssl.py +++ b/examples/echo_skill_ssl.py @@ -14,10 +14,7 @@ @router.message() async def echo(message: Message) -> AliceResponse: - if message.session.new: - text = "Привет!" - else: - text = message.original_utterance + text = "Привет!" if message.session.new else message.original_utterance return AliceResponse(response=Response(text=text)) diff --git a/examples/error_handling.py b/examples/error_handling.py index 55c9504..43718d1 100644 --- a/examples/error_handling.py +++ b/examples/error_handling.py @@ -18,7 +18,7 @@ class InvalidAge(Exception): class InvalidName(Exception): - def __init__(self, message: str): + def __init__(self, message: str) -> None: super().__init__(message) @@ -29,7 +29,7 @@ async def handle_invalid_age_exception(event: ErrorEvent) -> str: assert event.update.message is not None assert event.update.event is event.update.message - return f"Произошла ошибка: {repr(event.exception)}" + return f"Произошла ошибка: {event.exception!r}" @router.errors(ExceptionMessageFilter("Invalid")) @@ -38,9 +38,11 @@ async def handle_invalid_exceptions(event: ErrorEvent) -> str: # этот обработчик будет получать ошибки всех типов, # если сообщение в ней содержит подстроку "Invalid". logger.error( - "Error `Invalid` caught: %r while processing %r", event.exception, event.update + "Error `Invalid` caught: %r while processing %r", + event.exception, + event.update, ) - return f"Произошла ошибка: {repr(event.exception)}" + return f"Произошла ошибка: {event.exception!r}" @router.message(F.command.startswith("мне")) diff --git a/examples/fsm_form.py b/examples/fsm_form.py index b77a4ab..d59457c 100644 --- a/examples/fsm_form.py +++ b/examples/fsm_form.py @@ -64,7 +64,7 @@ async def process_dont_like_skills(message: Message, state: FSMContext) -> Respo async def process_like_skills(message: Message, state: FSMContext) -> Response: await state.set_state(Form.device) return Response( - text="Класс! Мне тоже!\nЧерез какое устройство ты обычно их используешь?" + text="Класс! Мне тоже!\nЧерез какое устройство ты обычно их используешь?", ) diff --git a/examples/fsm_games.py b/examples/fsm_games.py index e218992..afff458 100644 --- a/examples/fsm_games.py +++ b/examples/fsm_games.py @@ -108,12 +108,8 @@ async def guess_num_not_digit(message: Message) -> Response: # Сработает на нажатие кнопки с напёрстком в состоянии Game.thimbles @router.button_pressed(Game.thimbles) async def thimbles_button(button: ButtonPressed) -> Response: - if button.payload["win"]: - text = "Верно! Ты угадал!" - else: - text = "Неа, давай ещё разок." + text = "Верно! Ты угадал!" if button.payload["win"] else "Неа, давай ещё разок." text += "\nКручу, верчу! Где?" - return Response(text=text, buttons=generate_thimbles()) diff --git a/examples/middlewares.py b/examples/middlewares.py index 636c760..ad7125a 100644 --- a/examples/middlewares.py +++ b/examples/middlewares.py @@ -80,7 +80,7 @@ async def __call__( logging.info("Замечен пользователь без аккаунта, блокирую!") return Response( text="Я вас не знаю, у вас нет аккаунта в Яндексе. " - "А чтобы пользоваться мной, он нужен!" + "А чтобы пользоваться мной, он нужен!", ) return await handler(event, data) diff --git a/examples/multi_file_skill/__init__.py b/examples/multi_file_skill/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml index a8ab860..743b6d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ dependencies = [ "aiohttp~=3.9.0", "certifi>=2023.7.22", "magic-filter>=1.0.12,<1.1", - "pydantic>=2.4.1,<2.6", + "pydantic>=2.4.1,<2.8", "typing-extensions>=4.7.0,<=5.0", ] @@ -75,12 +75,12 @@ test = [ "pycryptodomex~=3.19.0", ] dev = [ - "black~=23.10.0", - "isort~=5.12.0", - "ruff~=0.1.1", - "mypy~=1.6.1", + "black~=24.4.0", + "isort~=5.13.2", + "ruff~=0.4.1", + "mypy~=1.9.0", "toml~=0.10.2", - "pre-commit~=3.5.0", + "pre-commit~=3.7.0", "packaging~=23.1", ] docs = [ @@ -155,18 +155,45 @@ python = ["38", "39", "310", "311", "312"] [tool.ruff] line-length = 88 -select = [ - "C", - "C4", - "E", - "F", - "T10", - "T20", - "Q", - "RET", +lint.select = [ + "F", # Pyflakes + "E", # pycodestyle + "W", # pycodestyle + "UP", # pyupgrade + "ANN", # flake8-annotations + "TRIO", # flake8-trio + "BLE", # flake8-blind-except + "B", # flake8-bugbear + "COM", # flake8-commas + "C4", # flake8-comprehensions + "PLC", # Pylint + "ISC", # flake8-implicit-str-concat + "INP", # flake8-no-pep420 + "PIE", # flake8-pie + "Q", # flake8-quotes + "RSE", # flake8-raise + "RET", # flake8-return + "SLF", # flake8-self + "SIM", # flake8-simplify + "FLY", # flynt + "PERF", # Perflint + "RUF", # Ruff-specific rules + "T10", # flake8-debugger + "T20", # flake8-print ] -ignore = [ - "F401" +lint.ignore = [ + "ANN101", # missing-type-self | Missing type annotation for {name} in method + "ANN102", # missing-type-cls | Missing type annotation for {name} in classmethod + "ANN401", # any-type | Dynamically typed expressions (typing.Any) are disallowed in {name} + "UP038", # Use `X | Y` in `isinstance` call instead of `(X, Y)` + "B905", # `zip()` without an explicit `strict=` parameter + "RUF001", # ambiguous-unicode-character-string | String contains ambiguous {}. Did you mean {}? + "RUF002", # ambiguous-unicode-character-docstring | Docstring contains ambiguous {}. Did you mean {}? + "RUF003", # ambiguous-unicode-character-comment | Comment contains ambiguous {}. Did you mean {}? + "UP042", # Class StrEnum inherits from both `str` and `enum.Enum` + "UP007", # Use `X | Y` for type annotations + "UP006", # Use `dict` instead of `Dict` for type annotation + "SIM102", # Use a single `if` statement instead of nested `if` statements ] src = ["aliceio", "tests"] exclude = [ @@ -182,6 +209,16 @@ exclude = [ "*.egg-info", ] target-version = "py38" +indent-width = 4 + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto" + +[tool.ruff.lint.per-file-ignores] +"tests/*" = ["ANN", "SLF", "B"] [tool.pytest.ini_options] asyncio_mode = "auto" diff --git a/tests/mocked/mocked_skill.py b/tests/mocked/mocked_skill.py index fe3cf87..df2a297 100644 --- a/tests/mocked/mocked_skill.py +++ b/tests/mocked/mocked_skill.py @@ -9,7 +9,7 @@ class MockedSession(BaseSession): def __init__(self) -> None: - super(MockedSession, self).__init__() + super().__init__() self.responses: Deque[ApiResponse[AliceType]] = deque() self.requests: Deque[AliceMethod[AliceType]] = deque() self.closed = True @@ -58,7 +58,7 @@ class MockedSkill(Skill): session: MockedSession def __init__(self, **kwargs) -> None: - super(MockedSkill, self).__init__( + super().__init__( kwargs.pop("token", "42:SKILL_ID"), session=MockedSession(), **kwargs, diff --git a/tests/mocked/mocked_update.py b/tests/mocked/mocked_update.py index 43cde86..0c98091 100644 --- a/tests/mocked/mocked_update.py +++ b/tests/mocked/mocked_update.py @@ -198,5 +198,7 @@ def create_mocked_api_state( application: Optional[Dict[str, Any]] = None, ) -> ApiState: return state or ApiState( - user=user or {}, session=session or {}, application=application or {} + user=user or {}, + session=session or {}, + application=application or {}, ) diff --git a/tests/test_api/test_client/test_alice_server.py b/tests/test_api/test_client/test_alice_server.py index fd79c9e..4c7ac1d 100644 --- a/tests/test_api/test_client/test_alice_server.py +++ b/tests/test_api/test_client/test_alice_server.py @@ -16,13 +16,17 @@ def test_get_all_url(self): def test_get_url(self): url = PRODUCTION.get_file_url( - skill_id="SKILL", file_type="TYPE", file_id="FILE" + skill_id="SKILL", + file_type="TYPE", + file_id="FILE", ) assert url == "https://dialogs.yandex.net/api/v1/skills/SKILL/TYPE/FILE" def test_delete_url(self): url = PRODUCTION.delete_file_url( - skill_id="SKILL", file_type="TYPE", file_id="FILE" + skill_id="SKILL", + file_type="TYPE", + file_id="FILE", ) assert url == "https://dialogs.yandex.net/api/v1/skills/SKILL/TYPE/FILE" @@ -33,10 +37,14 @@ def test_from_base(self): upload_url = local_server.upload_file_url(skill_id="SKILL", file_type="TYPE") all_url = local_server.get_all_files_url(skill_id="SKILL", file_type="TYPE") get_url = local_server.get_file_url( - skill_id="SKILL", file_type="TYPE", file_id="FILE" + skill_id="SKILL", + file_type="TYPE", + file_id="FILE", ) delete_url = local_server.delete_file_url( - skill_id="SKILL", file_type="TYPE", file_id="FILE" + skill_id="SKILL", + file_type="TYPE", + file_id="FILE", ) assert method_url == "http://localhost:8081/apiMethod" diff --git a/tests/test_api/test_client/test_session/test_aiohttp_session.py b/tests/test_api/test_client/test_session/test_aiohttp_session.py index c35f00b..09bcb48 100644 --- a/tests/test_api/test_client/test_session/test_aiohttp_session.py +++ b/tests/test_api/test_client/test_session/test_aiohttp_session.py @@ -77,7 +77,8 @@ async def test_create_proxy_session_chained_proxies(self): aiohttp_session = await session.create_session() assert isinstance( - aiohttp_session.connector, aiohttp_socks.ChainProxyConnector + aiohttp_session.connector, + aiohttp_socks.ChainProxyConnector, ) async def test_reset_connector(self): @@ -135,7 +136,7 @@ def api_url(self, api_server: AliceAPIServer) -> str: assert form is None assert len(json_data) == 5 - assert "null_" not in json_data.keys() + assert "null_" not in json_data def test_build_form_data_with_files(self, skill: Skill): class TestMethod(AliceMethod[bool]): diff --git a/tests/test_api/test_client/test_session/test_middlewares/test_manager.py b/tests/test_api/test_client/test_session/test_middlewares/test_manager.py index 69cceb8..dc25e7c 100644 --- a/tests/test_api/test_client/test_session/test_middlewares/test_manager.py +++ b/tests/test_api/test_client/test_session/test_middlewares/test_manager.py @@ -1,3 +1,5 @@ +from typing import Optional + import pytest from aliceio import Skill @@ -42,7 +44,7 @@ async def test_wrap_middlewares(self): async def middleware(make_request, skill, method): return await make_request(skill, method) - async def target_call(skill, method, timeout: int = None): + async def target_call(skill, method, timeout: Optional[int] = None): return timeout assert await manager.wrap_middlewares(target_call, timeout=42)(None, None) == 42 diff --git a/tests/test_api/test_client/test_session/test_middlewares/test_request_logging.py b/tests/test_api/test_client/test_session/test_middlewares/test_request_logging.py index ad18517..552ac2e 100644 --- a/tests/test_api/test_client/test_session/test_middlewares/test_request_logging.py +++ b/tests/test_api/test_client/test_session/test_middlewares/test_request_logging.py @@ -54,8 +54,8 @@ async def test_ignore_methods(self, skill: MockedSkill, caplog): id="42:IMAGE", size=100, createdAt="date", - ) - ] + ), + ], ), ) assert await skill.get_images() diff --git a/tests/test_api/test_client/test_skill.py b/tests/test_api/test_client/test_skill.py index 15df798..1bb9669 100644 --- a/tests/test_api/test_client/test_skill.py +++ b/tests/test_api/test_client/test_skill.py @@ -120,7 +120,7 @@ async def test_upload_image(self, skill: MockedSkill): skill.add_result_for( UploadImage, result=PreUploadedImage( - image=UploadedImage(id="id1", size=100, createdAt="1") + image=UploadedImage(id="id1", size=100, createdAt="1"), ), ) image = await skill.upload_image(BufferedInputFile(b"")) diff --git a/tests/test_api/test_types/test_audio_player_directive.py b/tests/test_api/test_types/test_audio_player_directive.py index 9f61cd8..0e43798 100644 --- a/tests/test_api/test_types/test_audio_player_directive.py +++ b/tests/test_api/test_types/test_audio_player_directive.py @@ -14,8 +14,10 @@ class TestAudioPlayerDirective: def test_wrong_action(self, action) -> None: item = AudioPlayerItem( stream=Stream( - url="https://example.com/stream-audio-url", offset_ms=0, token="token" - ) + url="https://example.com/stream-audio-url", + offset_ms=0, + token="token", + ), ) if isinstance(action, str): with pytest.raises(AliceWrongFieldError): @@ -36,6 +38,6 @@ def test_good_action(self, action: str) -> None: url="https://example.com/stream-audio-url", offset_ms=0, token="token", - ) + ), ), ) diff --git a/tests/test_api/test_types/test_error_event.py b/tests/test_api/test_types/test_error_event.py index 409070f..38f8dff 100644 --- a/tests/test_api/test_types/test_error_event.py +++ b/tests/test_api/test_types/test_error_event.py @@ -4,6 +4,8 @@ class TestErrorEvent: def test_event(self, update: Update): event = ErrorEvent( - update=update, exception=KeyError("KABOOM"), session=update.session + update=update, + exception=KeyError("KABOOM"), + session=update.session, ) assert event.event == update.event diff --git a/tests/test_api/test_types/test_update.py b/tests/test_api/test_types/test_update.py index b415a9a..e9e2f9d 100644 --- a/tests/test_api/test_types/test_update.py +++ b/tests/test_api/test_types/test_update.py @@ -58,7 +58,7 @@ class TestUpdate: "purchase_payload": {"value": "payload"}, "signed_data": "purchase_request_id=id_value&...", "signature": "Pi6JNCFeeleRa...", - } + }, }, Purchase, "purchase", @@ -74,7 +74,7 @@ class TestUpdate: "type": "SimpleUtterance", "command": "test", "original_utterance": "test", - } + }, }, Message, "message", @@ -110,7 +110,7 @@ def test_event( }, "state": {"session": {}, "user": {}, "application": {}}, "version": "1.0", - } + }, ) update = Update.model_validate(event) diff --git a/tests/test_dispatcher/test_dispatcher.py b/tests/test_dispatcher/test_dispatcher.py index 32f44f6..517f16f 100644 --- a/tests/test_dispatcher/test_dispatcher.py +++ b/tests/test_dispatcher/test_dispatcher.py @@ -136,11 +136,12 @@ async def test_feed_raw_update(self, skill: MockedSkill, dispatcher: Dispatcher) await dispatcher.feed_raw_update(skill, RAW_UPDATE, data={"k": "v"}) mock.assert_awaited_once() - args = mock.await_args_list[0][1] - assert len(args) == 3 - assert args["skill"] == skill - assert isinstance(args["update"], Update) - assert args["data"] == {"k": "v"} + args, kwargs = mock.await_args_list[0] + assert len(args) == 2 + assert args[0] == skill + assert isinstance(args[1], Update) + assert len(kwargs) == 1 + assert kwargs["data"] == {"k": "v"} async def test_feed_webhook_update_exception( self, diff --git a/tests/test_dispatcher/test_event/test_alice.py b/tests/test_dispatcher/test_event/test_alice.py index 4da26b6..1e8af0e 100644 --- a/tests/test_dispatcher/test_event/test_alice.py +++ b/tests/test_dispatcher/test_event/test_alice.py @@ -21,7 +21,7 @@ async def my_handler(event: Any, index: int = 0) -> Any: async def skip_my_handler(event: Any) -> NoReturn: - raise SkipHandler() + raise SkipHandler async def pipe_handler(*args, **kwargs): diff --git a/tests/test_dispatcher/test_event/test_event.py b/tests/test_dispatcher/test_event/test_event.py index b300a8d..befca2a 100644 --- a/tests/test_dispatcher/test_event/test_event.py +++ b/tests/test_dispatcher/test_event/test_event.py @@ -15,7 +15,8 @@ async def my_handler(value: str, index: int = 0) -> Any: class TestEventObserver: @pytest.mark.parametrize("via_decorator", [True, False]) @pytest.mark.parametrize( - "count,handler", ([5, my_handler], [3, my_handler], [2, my_handler]) + "count,handler", + ([5, my_handler], [3, my_handler], [2, my_handler]), ) def test_register_filters(self, via_decorator, count, handler): observer = EventObserver() diff --git a/tests/test_dispatcher/test_event/test_handler.py b/tests/test_dispatcher/test_event/test_handler.py index 4fc298f..ef2c2c6 100644 --- a/tests/test_dispatcher/test_event/test_handler.py +++ b/tests/test_dispatcher/test_event/test_handler.py @@ -30,7 +30,10 @@ async def callback4(foo: int, *, bar: int, baz: int): class TestFilter(Filter): async def __call__( - self, foo: int, bar: int, baz: int + self, + foo: int, + bar: int, + baz: int, ) -> Union[bool, Dict[str, Any]]: return locals() @@ -133,7 +136,10 @@ def callback2(foo, bar, baz): ], ) def test_prepare_kwargs( - self, callback: Callable, kwargs: Dict[str, Any], result: Dict[str, Any] + self, + callback: Callable, + kwargs: Dict[str, Any], + result: Dict[str, Any], ): obj = CallableObject(callback) assert obj._prepare_kwargs(kwargs) == result @@ -176,8 +182,9 @@ async def test_check_with_dict_result(self): [ FilterObject( functools.partial( - lambda value, index: {f"test{index}": "ok"}, index=item - ) + lambda value, index: {f"test{index}": "ok"}, + index=item, + ), ) for item in range(3) ], diff --git a/tests/test_dispatcher/test_middlewares/test_response_converter.py b/tests/test_dispatcher/test_middlewares/test_response_converter.py index 02c3be0..a59e98e 100644 --- a/tests/test_dispatcher/test_middlewares/test_response_converter.py +++ b/tests/test_dispatcher/test_middlewares/test_response_converter.py @@ -21,7 +21,9 @@ async def test_convert_alice_response(self): middleware = ResponseConvertMiddleware() result = await middleware( - create_handler(AliceResponse(response=Response(text="test"))), None, {} + create_handler(AliceResponse(response=Response(text="test"))), + None, + {}, ) assert isinstance(result, AliceResponse) assert result.response.text == "test" diff --git a/tests/test_dispatcher/test_middlewares/test_user_context.py b/tests/test_dispatcher/test_middlewares/test_user_context.py index f18eb6d..d0562df 100644 --- a/tests/test_dispatcher/test_middlewares/test_user_context.py +++ b/tests/test_dispatcher/test_middlewares/test_user_context.py @@ -25,7 +25,9 @@ async def test_call(self, update: Update) -> None: middleware = UserContextMiddleware() data = {} with patch.object( - UserContextMiddleware, "resolve_event_context", return_value=[1, 2] + UserContextMiddleware, + "resolve_event_context", + return_value=[1, 2], ): await middleware(next_handler, update, data) diff --git a/tests/test_dispatcher/test_router.py b/tests/test_dispatcher/test_router.py index 868eb1b..653cd0f 100644 --- a/tests/test_dispatcher/test_router.py +++ b/tests/test_dispatcher/test_router.py @@ -15,7 +15,8 @@ def test_including_routers(self): assert router3.parent_router is None with pytest.raises( - RuntimeError, match="Self-referencing routers is not allowed" + RuntimeError, + match="Self-referencing routers is not allowed", ): router1.include_router(router1) @@ -27,7 +28,8 @@ def test_including_routers(self): router2.include_router(router3) with pytest.raises( - RuntimeError, match="Circular referencing of Router is not allowed" + RuntimeError, + match="Circular referencing of Router is not allowed", ): router3.include_router(router1) diff --git a/tests/test_filters/test_exception.py b/tests/test_filters/test_exception.py index b6de2a6..907b0f5 100644 --- a/tests/test_filters/test_exception.py +++ b/tests/test_filters/test_exception.py @@ -26,7 +26,7 @@ async def test_match(self, update: Update) -> None: update=update, exception=Exception(), session=update.session, - ) + ), ) assert result is False @@ -35,7 +35,7 @@ async def test_match(self, update: Update) -> None: update=update, exception=Exception("BOOM"), session=update.session, - ) + ), ) assert isinstance(result, dict) assert "match_exception" in result @@ -73,7 +73,7 @@ async def test_check( f = ExceptionTypeFilter(MyException) result = await f( - ErrorEvent(update=update, exception=exception, session=update.session) + ErrorEvent(update=update, exception=exception, session=update.session), ) assert result == value diff --git a/tests/test_filters/test_state.py b/tests/test_filters/test_state.py index acefc17..08d59b0 100644 --- a/tests/test_filters/test_state.py +++ b/tests/test_filters/test_state.py @@ -67,7 +67,7 @@ class SG(StatesGroup): assert SG.state == copy(SG.state) assert SG.state == "SG:state" - assert "SG:state" == SG.state + assert SG.state == "SG:state" assert State() == State() assert SG.state != 1 diff --git a/tests/test_fsm/middlewares/test_api_storage.py b/tests/test_fsm/middlewares/test_api_storage.py index c4215af..5f2ab10 100644 --- a/tests/test_fsm/middlewares/test_api_storage.py +++ b/tests/test_fsm/middlewares/test_api_storage.py @@ -133,7 +133,7 @@ async def test_state_from_alice_no_data(self, state: FSMContext, update: Update) async def test_state_from_alice_data_and_strategy(self, state: FSMContext): update = create_mocked_update( - user_state={"state": "MyState", "data": {"foo": "bar"}} + user_state={"state": "MyState", "data": {"foo": "bar"}}, ) middleware = FSMApiStorageMiddleware(strategy=FSMStrategy.USER) @@ -277,7 +277,7 @@ async def next_handler(handler, data) -> AliceResponse: return AliceResponse(response=Response(text="test")) update = create_mocked_update( - user_state={"state": "MyState", "data": {"foo": "bar"}} + user_state={"state": "MyState", "data": {"foo": "bar"}}, ) middleware = FSMApiStorageMiddleware(strategy=FSMStrategy.USER) data = {"state": state} @@ -300,7 +300,7 @@ async def next_handler(handler, data) -> None: await state.update_data(bar="foo") update = create_mocked_update( - user_state={"state": "MyState", "data": {"foo": "bar"}} + user_state={"state": "MyState", "data": {"foo": "bar"}}, ) middleware = FSMApiStorageMiddleware(strategy=FSMStrategy.USER) data = {"state": state} diff --git a/tests/test_fsm/storage/test_storages.py b/tests/test_fsm/storage/test_storages.py index 7e5850c..4922a29 100644 --- a/tests/test_fsm/storage/test_storages.py +++ b/tests/test_fsm/storage/test_storages.py @@ -55,7 +55,7 @@ async def test_update_data( ) -> None: assert await storage.get_data(key=storage_key) == {} assert await storage.update_data(key=storage_key, data={"foo": "bar"}) == { - "foo": "bar" + "foo": "bar", } assert await storage.update_data(key=storage_key, data={"baz": "spam"}) == { "foo": "bar", diff --git a/tests/test_fsm/test_context.py b/tests/test_fsm/test_context.py index d771ad2..cb18f35 100644 --- a/tests/test_fsm/test_context.py +++ b/tests/test_fsm/test_context.py @@ -9,7 +9,10 @@ async def test_address_mapping(self, skill: MockedSkill) -> None: storage = MemoryStorage() ctx = storage.storage[ StorageKey( - session_id="-42", user_id="42", skill_id=skill.id, application_id="420" + session_id="-42", + user_id="42", + skill_id=skill.id, + application_id="420", ) ] ctx.state = "test" diff --git a/tests/test_handlers/test_audio_player.py b/tests/test_handlers/test_audio_player.py index b0ef939..31a82df 100644 --- a/tests/test_handlers/test_audio_player.py +++ b/tests/test_handlers/test_audio_player.py @@ -11,7 +11,8 @@ async def test_attributes_aliases(self): event = AudioPlayer( type=RequestType.AUDIO_PLAYER_FAILED, error=AudioPlayerError( - message="Something wrong", type="MEDIA_ERROR_UNKNOWN" + message="Something wrong", + type="MEDIA_ERROR_UNKNOWN", ), session=create_mocked_session(), ) diff --git a/tests/test_webhook/test_aiohttp_server.py b/tests/test_webhook/test_aiohttp_server.py index 97eba49..7c7c9fd 100644 --- a/tests/test_webhook/test_aiohttp_server.py +++ b/tests/test_webhook/test_aiohttp_server.py @@ -239,7 +239,11 @@ async def test_feed_webhook_update( aiohttp_client: Callable[..., Awaitable[TestClient]], ): async def fn_handler( - event, skill, event_update, event_session, event_from_user=None + event, + skill, + event_update, + event_session, + event_from_user=None, ): assert isinstance(skill, MockedSkill) assert isinstance(event_update, Update) diff --git a/tests/test_webhook/test_security.py b/tests/test_webhook/test_security.py index 8863ede..03622d4 100644 --- a/tests/test_webhook/test_security.py +++ b/tests/test_webhook/test_security.py @@ -31,7 +31,7 @@ def test_check_ip(self, ip, result): "127.0.0.1", IPv4Address("178.154.128.1"), IPv4Network("10.111.0.0/24"), - ] + ], ) assert (ip in ip_filter) is result