Skip to content

Commit

Permalink
feat: bump pydantic version, adapt models
Browse files Browse the repository at this point in the history
  • Loading branch information
Kiruha01 committed Apr 19, 2024
1 parent 02d7443 commit 143eac6
Show file tree
Hide file tree
Showing 27 changed files with 368 additions and 126 deletions.
327 changes: 279 additions & 48 deletions poetry.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions pybotx/bot/api/exceptions.py
@@ -1,7 +1,7 @@
from pybotx.constants import BOT_API_VERSION


class UnsupportedBotAPIVersionError(Exception):
class UnsupportedBotAPIVersionError(ValueError):
def __init__(self, version: int) -> None:
self.version = version
self.message = (
Expand All @@ -10,7 +10,7 @@ def __init__(self, version: int) -> None:
super().__init__(self.message)


class UnknownSystemEventError(Exception):
class UnknownSystemEventError(ValueError):
def __init__(self, type_name: str) -> None:
self.type_name = type_name
self.message = f"Unknown system event: `{type_name}`"
Expand Down
7 changes: 4 additions & 3 deletions pybotx/bot/bot.py
Expand Up @@ -22,7 +22,7 @@
import jwt
from aiocsv.readers import AsyncDictReader
from aiofiles.tempfile import NamedTemporaryFile, TemporaryDirectory
from pydantic import ValidationError, parse_obj_as
from pydantic import TypeAdapter, ValidationError, parse_obj_as

from pybotx.async_buffer import AsyncBufferReadable, AsyncBufferWritable
from pybotx.bot.bot_accounts_storage import BotAccountsStorage
Expand Down Expand Up @@ -305,10 +305,11 @@ def async_execute_raw_bot_command(
self._verify_request(request_headers)

try:
bot_api_command: BotAPICommand = parse_obj_as(
bot_api_command: BotAPICommand = TypeAdapter(BotAPICommand).validate_python(
# Same ignore as in pydantic
BotAPICommand, # type: ignore[arg-type]
# BotAPICommand, # type: ignore[arg-type]
raw_bot_command,
# strict=False
)
except ValidationError as validation_exc:
raise ValueError("Bot command validation error") from validation_exc
Expand Down
4 changes: 3 additions & 1 deletion pybotx/bot/bot_accounts_storage.py
Expand Up @@ -4,6 +4,8 @@
from typing import Dict, Iterator, List, Optional
from uuid import UUID

from pydantic import AnyHttpUrl

from pybotx.bot.exceptions import UnknownBotAccountError
from pybotx.models.bot_account import BotAccountWithSecret

Expand All @@ -23,7 +25,7 @@ def get_bot_account(self, bot_id: UUID) -> BotAccountWithSecret:
def iter_bot_accounts(self) -> Iterator[BotAccountWithSecret]:
yield from self._bot_accounts

def get_cts_url(self, bot_id: UUID) -> str:
def get_cts_url(self, bot_id: UUID) -> AnyHttpUrl:
bot_account = self.get_bot_account(bot_id)
return bot_account.cts_url

Expand Down
2 changes: 1 addition & 1 deletion pybotx/client/botx_method.py
Expand Up @@ -88,7 +88,7 @@ async def execute(self, *args: Any, **kwargs: Any) -> Any: # type: ignore

def _build_url(self, path: str) -> str:
cts_url = self._bot_accounts_storage.get_cts_url(self._bot_id)
return "/".join(part.strip("/") for part in (cts_url, path))
return "/".join(part.strip("/") for part in (cts_url.unicode_string(), path))

def _verify_and_extract_api_model(
self,
Expand Down
2 changes: 1 addition & 1 deletion pybotx/client/chats_api/chat_info.py
Expand Up @@ -32,7 +32,7 @@ class BotXAPIChatInfoMember(VerifiedPayloadBaseModel):

class BotXAPIChatInfoResult(VerifiedPayloadBaseModel):
chat_type: APIChatTypes
creator: Optional[UUID]
creator: Optional[UUID] = None
description: Optional[str] = None
group_chat_id: UUID
inserted_at: dt
Expand Down
2 changes: 1 addition & 1 deletion pybotx/client/chats_api/create_chat.py
Expand Up @@ -14,7 +14,7 @@

class BotXAPICreateChatRequestPayload(UnverifiedPayloadBaseModel):
name: str
description: Optional[str]
description: Optional[str] = None
chat_type: APIChatTypes
members: List[UUID]
shared_history: Missing[bool]
Expand Down
1 change: 1 addition & 0 deletions pybotx/client/get_token.py
Expand Up @@ -27,6 +27,7 @@ async def get_token(

signature = bot_accounts_storage.build_signature(bot_id)
payload = BotXAPIGetTokenRequestPayload.from_domain(signature)
print(payload.dict(), "a")

botx_api_token = await method.execute(payload)

Expand Down
2 changes: 1 addition & 1 deletion pybotx/client/openid_api/refresh_access_token.py
Expand Up @@ -7,7 +7,7 @@

class BotXAPIRefreshAccessTokenRequestPayload(UnverifiedPayloadBaseModel):
user_huid: UUID
ref: Optional[UUID]
ref: Optional[UUID] = None

@classmethod
def from_domain(
Expand Down
2 changes: 1 addition & 1 deletion pybotx/client/stickers_api/edit_sticker_pack.py
Expand Up @@ -12,7 +12,7 @@ class BotXAPIEditStickerPackRequestPayload(UnverifiedPayloadBaseModel):
sticker_pack_id: UUID
name: str
preview: UUID
stickers_order: Optional[List[UUID]]
stickers_order: Optional[List[UUID]] = None

@classmethod
def from_domain(
Expand Down
6 changes: 3 additions & 3 deletions pybotx/client/stickers_api/get_sticker_packs.py
Expand Up @@ -10,7 +10,7 @@
class BotXAPIGetStickerPacksRequestPayload(UnverifiedPayloadBaseModel):
user_huid: UUID
limit: int
after: Optional[str]
after: Optional[str] = None

@classmethod
def from_domain(
Expand All @@ -23,15 +23,15 @@ def from_domain(


class BotXAPIGetPaginationResult(VerifiedPayloadBaseModel):
after: Optional[str]
after: Optional[str] = None


class BotXAPIGetStickerPackResult(VerifiedPayloadBaseModel):
id: UUID
name: str
public: bool
stickers_count: int
stickers_order: Optional[List[UUID]]
stickers_order: Optional[List[UUID]] = None


class BotXAPIGetStickerPacksResult(VerifiedPayloadBaseModel):
Expand Down
2 changes: 1 addition & 1 deletion pybotx/client/stickers_api/sticker_pack.py
Expand Up @@ -15,7 +15,7 @@ class BotXAPIGetStickerPackResult(VerifiedPayloadBaseModel):
id: UUID
name: str
public: bool
stickers_order: Optional[List[UUID]]
stickers_order: Optional[List[UUID]] = None
stickers: List[BotXAPIGetStickerResult]


Expand Down
13 changes: 7 additions & 6 deletions pybotx/client/users_api/user_from_csv.py
@@ -1,7 +1,7 @@
from typing import Optional, Union
from uuid import UUID

from pydantic import Field, validator
from pydantic import Field, field_validator

from pybotx.models.api_base import VerifiedPayloadBaseModel
from pybotx.models.enums import (
Expand All @@ -17,16 +17,17 @@ class BotXAPIUserFromCSVResult(VerifiedPayloadBaseModel):
huid: UUID = Field(alias="HUID")
ad_login: str = Field(alias="AD Login")
ad_domain: str = Field(alias="Domain")
email: Optional[str] = Field(alias="AD E-mail")
email: Optional[str] = Field(None, alias="AD E-mail")
name: str = Field(alias="Name")
sync_source: Union[APISyncSourceTypes, str] = Field(alias="Sync source")
active: bool = Field(alias="Active")
user_kind: APIUserKinds = Field(alias="Kind")
company: Optional[str] = Field(alias="Company")
department: Optional[str] = Field(alias="Department")
position: Optional[str] = Field(alias="Position")
company: Optional[str] = Field(None, alias="Company")
department: Optional[str] = Field(None, alias="Department")
position: Optional[str] = Field(None, alias="Position")

@validator("email", "company", "department", "position", pre=True)
@field_validator("email", "company", "department", "position", mode="before")
@classmethod
@classmethod
def replace_empty_string_with_none(cls, field_value: str) -> Optional[str]:
if field_value == "":
Expand Down
7 changes: 3 additions & 4 deletions pybotx/models/api_base.py
@@ -1,7 +1,7 @@
import json
from typing import Any, Dict, List, Optional, Set, Union, cast

from pydantic import BaseModel
from pydantic import BaseModel, ConfigDict
from pydantic.json import pydantic_encoder

from pybotx.missing import Undefined
Expand Down Expand Up @@ -67,8 +67,7 @@ def __init__(
_fields_set: Optional[Set[str]] = None,
**kwargs: Any,
) -> None:
model = BaseModel.construct(_fields_set, **kwargs)
model = self.model_construct(_fields_set, **kwargs)
self.__dict__.update(model.__dict__) # noqa: WPS609 (Replace self attrs)

class Config:
arbitrary_types_allowed = True
model_config = ConfigDict(arbitrary_types_allowed=True)
11 changes: 2 additions & 9 deletions pybotx/models/async_files.py
Expand Up @@ -4,6 +4,7 @@
from uuid import UUID

from aiofiles.tempfile import SpooledTemporaryFile
from pydantic import ConfigDict

from pybotx.bot.contextvars import bot_id_var, bot_var, chat_id_var
from pybotx.constants import CHUNK_SIZE
Expand Down Expand Up @@ -76,15 +77,7 @@ class APIAsyncFileBase(VerifiedPayloadBaseModel):
file_name: str
file_size: int
file_hash: str

class Config:
"""BotX sends extra fields which are used by client only.
We skip their validation, but extra fields will be saved during
serialization/deserialization.
"""

extra = "allow"
model_config = ConfigDict(extra="allow")


class ApiAsyncFileImage(APIAsyncFileBase):
Expand Down
40 changes: 20 additions & 20 deletions pybotx/models/base_command.py
Expand Up @@ -2,7 +2,7 @@
from typing import Any, Dict, Literal, Optional, Union
from uuid import UUID

from pydantic import validator
from pydantic import field_validator

from pybotx.bot.api.exceptions import (
UnknownSystemEventError,
Expand All @@ -27,9 +27,9 @@ class BotAPICommandPayload(VerifiedPayloadBaseModel):


class BotAPIDeviceMeta(VerifiedPayloadBaseModel):
pushes: Optional[bool]
timezone: Optional[str]
permissions: Optional[Dict[str, Any]]
pushes: Optional[bool] = None
timezone: Optional[str] = None
permissions: Optional[Dict[str, Any]] = None


class BaseBotAPIContext(VerifiedPayloadBaseModel):
Expand All @@ -38,12 +38,12 @@ class BaseBotAPIContext(VerifiedPayloadBaseModel):

class BotAPIUserContext(BaseBotAPIContext):
user_huid: UUID
user_udid: Optional[UUID]
ad_domain: Optional[str]
ad_login: Optional[str]
username: Optional[str]
is_admin: Optional[bool]
is_creator: Optional[bool]
user_udid: Optional[UUID] = None
ad_domain: Optional[str] = None
ad_login: Optional[str] = None
username: Optional[str] = None
is_admin: Optional[bool] = None
is_creator: Optional[bool] = None


class BotAPIChatContext(BaseBotAPIContext):
Expand All @@ -52,22 +52,22 @@ class BotAPIChatContext(BaseBotAPIContext):


class BotAPIDeviceContext(BaseBotAPIContext):
app_version: Optional[str]
platform: Optional[BotAPIClientPlatforms]
platform_package_id: Optional[str]
device: Optional[str]
device_meta: Optional[BotAPIDeviceMeta]
device_software: Optional[str]
manufacturer: Optional[str]
locale: Optional[str]
app_version: Optional[str] = None
platform: Optional[BotAPIClientPlatforms] = None
platform_package_id: Optional[str] = None
device: Optional[str] = None
device_meta: Optional[BotAPIDeviceMeta] = None
device_software: Optional[str] = None
manufacturer: Optional[str] = None
locale: Optional[str] = None


class BotAPIBaseCommand(VerifiedPayloadBaseModel):
bot_id: UUID
sync_id: UUID
proto_version: int

@validator("proto_version", pre=True)
@field_validator("proto_version", mode="before")
@classmethod
def validate_proto_version(cls, version: Any) -> int:
if isinstance(version, int) and version == BOT_API_VERSION:
Expand All @@ -79,7 +79,7 @@ def validate_proto_version(cls, version: Any) -> int:
class BotAPIBaseSystemEventPayload(VerifiedPayloadBaseModel):
command_type: Literal[BotAPICommandTypes.SYSTEM]

@validator("body", pre=True, check_fields=False)
@field_validator("body", mode="before", check_fields=False)
@classmethod
def find_unknown_system_event(cls, body: str) -> str:
if body not in BotAPISystemEventTypes.__members__.values(): # noqa: WPS609
Expand Down
13 changes: 4 additions & 9 deletions pybotx/models/bot_account.py
@@ -1,9 +1,8 @@
from dataclasses import dataclass
from functools import cached_property
from urllib.parse import urlparse
from uuid import UUID

from pydantic import AnyHttpUrl, BaseModel
from pydantic import AnyHttpUrl, BaseModel, ConfigDict


@dataclass
Expand All @@ -17,15 +16,11 @@ class BotAccountWithSecret(BaseModel):
cts_url: AnyHttpUrl
secret_key: str

class Config:
allow_mutation = False
keep_untouched = (cached_property,)
model_config = ConfigDict(frozen=True, ignored_types=(cached_property,))

@cached_property
def host(self) -> str:
hostname = urlparse(self.cts_url).hostname

if hostname is None:
if self.cts_url.host is None:
raise ValueError("Could not parse host from cts_url.")

return hostname
return self.cts_url.host
2 changes: 1 addition & 1 deletion pybotx/models/message/incoming_message.py
Expand Up @@ -194,7 +194,7 @@ class BotAPIIncomingMessage(BotAPIBaseCommand):
payload: BotAPICommandPayload = Field(..., alias="command")
sender: BotAPIIncomingMessageContext = Field(..., alias="from")

source_sync_id: Optional[UUID]
source_sync_id: Optional[UUID] = None
attachments: List[Union[BotAPIAttachment, Dict[str, Any]]] # noqa: WPS234
entities: List[Union[BotAPIEntity, Dict[str, Any]]] # noqa: WPS234

Expand Down
5 changes: 3 additions & 2 deletions pybotx/models/message/markup.py
Expand Up @@ -2,6 +2,8 @@
from enum import Enum
from typing import Any, Dict, Iterator, List, Literal, Optional, Union

from pydantic import RootModel

from pybotx.missing import Missing, Undefined
from pybotx.models.api_base import UnverifiedPayloadBaseModel

Expand Down Expand Up @@ -180,8 +182,7 @@ class BotXAPIButton(UnverifiedPayloadBaseModel):
opts: BotXAPIButtonOptions


class BotXAPIMarkup(UnverifiedPayloadBaseModel):
__root__: List[List[BotXAPIButton]]
BotXAPIMarkup = RootModel[List[List[BotXAPIButton]]]


def api_button_from_domain(button: Button) -> BotXAPIButton:
Expand Down
7 changes: 4 additions & 3 deletions pybotx/models/message/mentions.py
Expand Up @@ -3,7 +3,7 @@
from typing import Dict, List, Literal, Optional, Tuple, Union
from uuid import UUID, uuid4

from pydantic import Field, validator
from pydantic import Field, field_validator

from pybotx.missing import Missing, Undefined
from pybotx.models.api_base import UnverifiedPayloadBaseModel, VerifiedPayloadBaseModel
Expand Down Expand Up @@ -174,9 +174,10 @@ class BotAPINestedGroupMentionData(VerifiedPayloadBaseModel):
class BotAPIMentionData(VerifiedPayloadBaseModel):
mention_type: BotAPIMentionTypes
mention_id: UUID
mention_data: Optional[BotAPINestedMentionData]
mention_data: Optional[BotAPINestedMentionData] = None

@validator("mention_data", pre=True)
@field_validator("mention_data", mode="before")
@classmethod
@classmethod
def validate_mention_data(
cls,
Expand Down

0 comments on commit 143eac6

Please sign in to comment.