Skip to content

Commit

Permalink
Отказ от JSONModule и choicelib, больше тестов
Browse files Browse the repository at this point in the history
  • Loading branch information
K1rL3s committed Jan 19, 2024
1 parent 16bc932 commit 80b3833
Show file tree
Hide file tree
Showing 29 changed files with 1,039 additions and 168 deletions.
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,21 @@ repos:
hooks:
- id: black
name: black
entry: black
entry: black .
language: system
types: [ python ]
stages: [ commit ]

- id: isort
name: isort
entry: isort
entry: isort .
language: system
types: [ python ]
stages: [ commit ]

- id: ruff
name: ruff
entry: ruff check
entry: ruff check aliceio examples
language: system
types: [ python ]
stages: [ commit ]
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 K1rL3s - Kirill Lesovoy
Copyright (c) 2024, K1rL3s - Kirill Lesovoy

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
1 change: 1 addition & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
The creators of the project were inspired by aiogram, aioalice and vkbottle.
Part of the project's source code was taken from them.

aiogram sources:
- https://github.com/aiogram/aiogram
aioalice sources:
Expand Down
16 changes: 5 additions & 11 deletions aliceio/client/session/aiohttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,18 +157,15 @@ def build_form_data(
for key, value in files.items():
form.add_field(
key,
value.read(skill),
value.read(),
filename=key,
)
return form

@staticmethod
def _build_request_headers(
skill: Skill, method: AliceMethod[Any]
) -> Dict[str, Any]:
def _build_request_headers(skill: Skill) -> Dict[str, Any]:
if skill.oauth_token is None:
raise AliceNoCredentialsError(
method,
"To use the Alice API, "
"you need to set an oauth token when creating a Skill",
)
Expand All @@ -191,16 +188,13 @@ async def make_request(
url,
data=form,
timeout=self.timeout if timeout is None else timeout,
headers=self._build_request_headers(skill, method),
headers=self._build_request_headers(skill),
) as resp:
raw_result = await resp.text()
except asyncio.TimeoutError:
raise AliceNetworkError(method=method, message="AliceRequest timeout error")
raise AliceNetworkError(message="AliceRequest timeout error")
except ClientError as e:
raise AliceNetworkError(
method=method,
message=f"{type(e).__name__}: {e}",
)
raise AliceNetworkError(message=f"{type(e).__name__}: {e}")
response = self.check_response(
skill=skill,
method=method,
Expand Down
28 changes: 15 additions & 13 deletions aliceio/client/session/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

import abc
import datetime
import json
from enum import Enum
from http import HTTPStatus
from types import TracebackType
from typing import TYPE_CHECKING, Any, Dict, Final, Optional, Type, cast
from typing import TYPE_CHECKING, Any, Callable, Dict, Final, Optional, Type, cast

from pydantic import ValidationError

from aliceio.exceptions import AliceAPIError, ClientDecodeError

from ...dispatcher.event.bases import REJECTED, UNHANDLED
from ...json import JSONModule, json
from ...methods import AliceMethod, AliceType, Response
from ...types import ErrorResult, InputFile
from ..alice import PRODUCTION, AliceAPIServer
Expand All @@ -22,6 +22,8 @@
from ..skill import Skill

DEFAULT_TIMEOUT: Final[float] = 60.0
_JsonLoads = Callable[..., Any]
_JsonDumps = Callable[..., str]


class BaseSession(abc.ABC):
Expand All @@ -34,17 +36,20 @@ class BaseSession(abc.ABC):
def __init__(
self,
api: AliceAPIServer = PRODUCTION,
json_module: JSONModule = json,
json_loads: _JsonLoads = json.loads,
json_dumps: _JsonDumps = json.dumps,
timeout: float = DEFAULT_TIMEOUT,
) -> None:
"""
:param api: URL паттерны API Алисы.
:param json_module: JSON Модуль.
:param json_loads: JSON Loads.
:param json_dumps: Json Dumps.
:param timeout: Тайм-аут запроса сессии.
"""
self.api = api
self.json = json_module
self.json_loads = json_loads
self.json_dumps = json_dumps
self.timeout = timeout

self.middleware = RequestMiddlewareManager()
Expand All @@ -58,7 +63,7 @@ def check_response(
) -> Response[AliceType]:
"""Проверка статуса ответа."""
try:
json_data = self.json.loads(content)
json_data = self.json_loads(content)
except Exception as e:
# Обрабатываемая ошибка не может быть поймана конкретным типом,
# поскольку декодер можно кастомизировать и вызвать любое исключение.
Expand All @@ -79,10 +84,7 @@ def check_response(
except ValidationError as e:
raise ClientDecodeError("Failed to deserialize object", e, json_data)

raise AliceAPIError(
method=method,
message=response.message,
)
raise AliceAPIError(message=response.message)

@abc.abstractmethod
async def close(self) -> None: # pragma: no cover
Expand Down Expand Up @@ -140,7 +142,7 @@ def prepare_value( # noqa: C901
is not None
}
if _dumps_json:
return self.json.dumps(value)
return self.json_dumps(value)
return value
if isinstance(value, list):
value = [
Expand All @@ -154,7 +156,7 @@ def prepare_value( # noqa: C901
is not None
]
if _dumps_json:
return self.json.dumps(value)
return self.json_dumps(value)
return value
if isinstance(value, datetime.timedelta):
now = datetime.datetime.now()
Expand All @@ -167,7 +169,7 @@ def prepare_value( # noqa: C901
)

if _dumps_json:
return self.json.dumps(value)
return self.json_dumps(value)
return value

async def __call__(
Expand Down
37 changes: 23 additions & 14 deletions aliceio/client/skill.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
from __future__ import annotations

from contextlib import asynccontextmanager
from typing import Any, AsyncGenerator, AsyncIterator, Optional, TypeVar

import aiofiles
from typing import Any, AsyncIterator, Optional, TypeVar, Union, overload

from ..methods import (
DeleteImage,
Expand Down Expand Up @@ -86,16 +84,6 @@ async def context(self, auto_close: bool = True) -> AsyncIterator[Skill]:
if auto_close:
await self.session.close()

@classmethod
async def __aiofiles_reader(
cls,
file: str,
chunk_size: int = 65536,
) -> AsyncGenerator[bytes, None]:
async with aiofiles.open(file, "rb") as f:
while chunk := await f.read(chunk_size):
yield chunk

def __eq__(self, other: Any) -> bool:
"""
Сравнить текущий навык с другим экземпляром навыка.
Expand Down Expand Up @@ -131,12 +119,33 @@ async def get_images(
get_images = GetImages()
return await self(get_images, request_timeout=request_timeout)

@overload
async def upload_image(
self,
url: str,
/,
request_timeout: Optional[int] = None,
) -> PreUploadedImage:
pass

@overload
async def upload_image(
self,
file: InputFile,
/,
request_timeout: Optional[int] = None,
) -> PreUploadedImage:
pass

async def upload_image(
self,
file: Union[InputFile, str],
request_timeout: Optional[int] = None,
) -> PreUploadedImage:
upload_image = UploadImage(file=file)
if isinstance(file, str):
upload_image = UploadImage(url=file)
else:
upload_image = UploadImage(file=file)
return await self(upload_image, request_timeout=request_timeout)

async def delete_image(
Expand Down
10 changes: 6 additions & 4 deletions aliceio/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from typing import Any, Optional

from aliceio.methods.base import AliceMethod, AliceType


class AliceioError(Exception):
"""Базовое исключение для всех ошибок aliceio."""
Expand Down Expand Up @@ -32,11 +30,9 @@ class AliceAPIError(DetailedAliceioError):

def __init__(
self,
method: AliceMethod[AliceType],
message: str,
) -> None:
super().__init__(message=message)
self.method = method

def __str__(self) -> str:
original_message = super().__str__()
Expand All @@ -55,6 +51,12 @@ class AliceNoCredentialsError(AliceAPIError):
label = "No OAuth Token"


class AliceWrongFieldError(AliceAPIError):
"""Исключение при создании модели с неправильным(и) полем(ями)."""

label = "Wrong field(s)"


class ClientDecodeError(AliceioError):
"""
Исключение возникает, когда клиент не может декодировать ответ.
Expand Down
20 changes: 13 additions & 7 deletions aliceio/fsm/storage/redis.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import json
from abc import ABC, abstractmethod
from typing import Any, Dict, Literal, Optional, cast
from typing import Any, Callable, Dict, Literal, Optional, cast

from redis.asyncio.client import Redis
from redis.asyncio.connection import ConnectionPool
from redis.typing import ExpiryT

from aliceio.fsm.state import State
from aliceio.fsm.storage.base import DEFAULT_DESTINY, BaseStorage, StateType, StorageKey
from aliceio.json import JSONModule, json

_JsonLoads = Callable[..., Any]
_JsonDumps = Callable[..., str]


class KeyBuilder(ABC):
Expand Down Expand Up @@ -80,22 +83,25 @@ def __init__(
key_builder: Optional[KeyBuilder] = None,
state_ttl: Optional[ExpiryT] = None,
data_ttl: Optional[ExpiryT] = None,
json_module: JSONModule = json,
json_loads: _JsonLoads = json.loads,
json_dumps: _JsonDumps = json.dumps,
) -> None:
"""
:param redis: Экземпляр подключения Redis.
:param key_builder: builder that helps to convert contextual key to string
:param state_ttl: TTL для записей состояния.
:param data_ttl: TTL для записей данных.
:param json_module: JSON Модуль.
:param json_loads: JSON Loads.
:param json_dumps: JSON Dumps.
"""
if key_builder is None:
key_builder = DefaultKeyBuilder()
self.redis = redis
self.key_builder = key_builder
self.state_ttl = state_ttl
self.data_ttl = data_ttl
self.json = json_module
self.json_loads = json_loads
self.json_dumps = json_dumps

@classmethod
def from_url(
Expand Down Expand Up @@ -157,7 +163,7 @@ async def set_data(
return
await self.redis.set(
redis_key,
self.json.dumps(data),
self.json_dumps(data),
ex=self.data_ttl,
)

Expand All @@ -171,4 +177,4 @@ async def get_data(
return {}
if isinstance(value, bytes):
value = value.decode("utf-8")
return cast(Dict[str, Any], self.json.loads(value))
return cast(Dict[str, Any], self.json_loads(value))
24 changes: 0 additions & 24 deletions aliceio/json.py

This file was deleted.

Loading

0 comments on commit 80b3833

Please sign in to comment.