diff --git a/TikTok/Queries/Common.py b/TikTok/Queries/Common.py index 2cf9faa..bfbecab 100644 --- a/TikTok/Queries/Common.py +++ b/TikTok/Queries/Common.py @@ -10,13 +10,25 @@ consistent handling of API requests and responses. """ -from typing import TYPE_CHECKING +import httpx +import orjson +import structlog +from pydantic import ValidationError, BaseModel +from cytoolz import curry +from typing import TYPE_CHECKING, TypeVar, Generic, Type, Any + +from TikTok.Exceptions.Query import QueryException + +logger = structlog.get_logger() if TYPE_CHECKING: from TikTok.Query import Query +RequestModel = TypeVar("RequestModel", bound=BaseModel) +ResponseModel = TypeVar("ResponseModel", bound=BaseModel) + -class QueryClass: +class QueryClass(Generic[RequestModel, ResponseModel]): """ A subclass to handle common API queries. @@ -32,3 +44,64 @@ def __init__(self, query: "Query") -> None: query (Query): The parent Query instance. """ self.query = query + + @curry + async def _fetch_data( + self, + url: str, + request_model_class: Type[RequestModel], + response_model_class: Type[ResponseModel], + params: dict[str, Any] | None = None, + json_data: dict[str, Any] | None = None, + ) -> ResponseModel: + """ + Generalized method to fetch data from the TikTok API. + + This method handles the HTTP POST request, response validation, and error handling. + + Parameters: + url (str): The API endpoint URL. + request_model_class (Type[RequestModel]): The Pydantic model class for the request payload. + response_model_class (Type[ResponseModel]): The Pydantic model class for the response payload. + params (dict[str, Any] | None): Query parameters for the request. Defaults to None. + json_data (dict[str, Any] | None): JSON payload for the request. Defaults to None. + + Returns: + ResponseModel: An instance of the response_model_class containing the API response data. + + Raises: + QueryException: If the API query fails or returns an error. + ValidationError: If the response body is invalid according to the expected model. + Exception: For any other unexpected errors that may occur during the API request. + """ + headers = request_model_class.HeadersModel( + authorization=await self.query.auth.get_access_token() + ).model_dump(by_alias=True) + + try: + response: httpx.Response = await self.query.client.post( + url=url, + headers=headers, + params=params or {}, + json=request_model_class(**(json_data or {})).model_dump( + exclude_none=True + ), + ) + try: + return response_model_class(**orjson.loads(response.content)) + except ValidationError as _: + error_message: dict[str, Any] = orjson.loads(response.text) + logger.error( + f"API query failed with status {response.status_code}: {error_message['error']['message']}" + ) + raise QueryException( + f"TikTok API query failed because {error_message['error']['message']}" + ) + except QueryException as e: + raise e + except ValidationError as e: + logger.error(f"Invalid response body: {e}") + raise e + except Exception as e: + logger.error(f"Unknown exception during API query: {e}") + raise e diff --git a/TikTok/Queries/Playlist.py b/TikTok/Queries/Playlist.py index 3850ca9..ac269d4 100644 --- a/TikTok/Queries/Playlist.py +++ b/TikTok/Queries/Playlist.py @@ -2,18 +2,9 @@ This module contains the PlaylistQueries class for handling playlist-related API queries to the TikTok platform. """ -import httpx -import orjson -import structlog -from pydantic import ValidationError - from TikTok.ValidationModels import Playlist -from TikTok.Exceptions.Query import QueryException - from TikTok.Queries.Common import QueryClass -logger = structlog.get_logger() - class PlaylistQueries(QueryClass): async def info( @@ -34,41 +25,9 @@ async def info( ValidationError: If the response body is invalid according to the expected model. Exception: For any other unexpected errors that may occur during the API request. """ - headers = Playlist.RequestHeadersModel( - authorization=await self.query.auth.get_access_token() + return await self._fetch_data( + url=self.query.endpoints.PlaylistInfoURL, + request_model_class=Playlist.InfoRequestModel, + response_model_class=Playlist.InfoResponseModel, + json_data={"playlist_id": playlist_id, "cursor": cursor}, ) - try: - response: httpx.Response = await self.query.client.post( - url=self.query.endpoints.PlaylistInfoURL, - headers=headers.model_dump(by_alias=True), - json=Playlist.InfoRequestModel( - playlist_id=playlist_id, - cursor=cursor, - ).model_dump(exclude_none=True), - ) - if response.status_code != 200: - try: - return Playlist.InfoResponseModel(**orjson.loads(response.text)) - except ValidationError as e: - logger.error( - f"The attempted query failed because the response body was invalid: {e}" - ) - raise e - except Exception as e: - logger.error( - f"The attempted query failed with the status code {response.status_code}. Details: {e}" - ) - raise QueryException(f"TikTok API query failed. Details: {e}") - return Playlist.InfoResponseModel(**orjson.loads(response.content)) - except QueryException as e: - raise e - except ValidationError as e: - logger.error( - f"The attempted query failed because the response body was invalid: {e}" - ) - raise e - except Exception as e: - logger.error( - f"An unknown exception occurred while querying the TikTok API: {e}" - ) - raise e diff --git a/TikTok/Queries/User.py b/TikTok/Queries/User.py index 49670cf..88a1f70 100644 --- a/TikTok/Queries/User.py +++ b/TikTok/Queries/User.py @@ -2,20 +2,12 @@ This module contains the UserQueries class for handling user-related API queries to the TikTok platform. """ -import httpx -import orjson -import structlog -from pydantic import ValidationError - from TikTok.ValidationModels import User -from TikTok.Exceptions.Query import QueryException - -from TikTok.Queries.Common import QueryClass -logger = structlog.get_logger() +from TikTok.Queries.Common import QueryClass, RequestModel, ResponseModel -class UserQueries(QueryClass): +class UserQueries(QueryClass[RequestModel, ResponseModel]): """ A class to handle user-related API queries to the TikTok platform. @@ -31,7 +23,7 @@ async def info( Parameters: username (str): The username of the TikTok user. - fields (list[User.UserInfoQueryFields]): A list of fields to retrieve from the API. + fields (List[User.UserInfoQueryFields]): A list of fields to retrieve from the API. Returns: User.UserInfoResponseDataModel: The response data model containing user information. @@ -41,40 +33,13 @@ async def info( ValidationError: If the response body is invalid according to the expected model. Exception: For any other unexpected errors that may occur during the API request. """ - headers = User.UserInfoRequestHeadersModel( - authorization=await self.query.auth.get_access_token() + return await self._fetch_data( + url=self.query.endpoints.UserInfoURL, + request_model_class=User.UserInfoRequestModel, + response_model_class=User.UserInfoResponseModel, + params={"fields": fields}, + json_data={"username": username}, ) - try: - response: httpx.Response = await self.query.client.post( - url=self.query.endpoints.UserInfoURL, - headers=headers.model_dump(by_alias=True), - params={"fields": fields}, - json=User.UserInfoRequestModel( - username=username, - ).model_dump(), - ) - if response.status_code != 200: - error_message: dict[str, str] = orjson.loads(response.text) - - logger.error( - f"The attempted query failed with the status code: {response.status_code} because {error_message['error']['message']}" - ) - raise QueryException( - f"TikTok API query failed because {error_message['error']['message']}" - ) - return User.UserInfoResponseModel(**orjson.loads(response.content)) - except QueryException as e: - raise e - except ValidationError as e: - logger.error( - f"The attempted query failed because the response body was invalid: {e}" - ) - raise e - except Exception as e: - logger.error( - f"An unknown exception occurred while querying the TikTok API: {e}" - ) - raise e async def liked_videos( self, @@ -100,44 +65,17 @@ async def liked_videos( ValidationError: If the response body is invalid according to the expected model. Exception: For any other unexpected errors that may occur during the API request. """ - headers = User.UserDataRequestHeadersModel( - authorization=await self.query.auth.get_access_token() + return await self._fetch_data( + url=self.query.endpoints.UserLikedVideosURL, + request_model_class=User.UserLikedVideosRequestModel, + response_model_class=User.UserLikedVideosResponseModel, + params={"fields": fields}, + json_data={ + "username": username, + "max_count": max_count, + "cursor": cursor, + }, ) - try: - response: httpx.Response = await self.query.client.post( - url=self.query.endpoints.UserLikedVideosURL, - headers=headers.model_dump(by_alias=True), - params={"fields": fields}, - json=User.UserLikedVideosRequestModel( - username=username, - max_count=max_count, - cursor=cursor, - ).model_dump(exclude_none=True), - ) - logger.info(response.status_code) - logger.info(response.json()) - if response.status_code != 200: - error_message: dict[str, str] = orjson.loads(response.text) - - logger.error( - f"The attempted query failed with the status code: {response.status_code} because {error_message['error']['message']}" - ) - raise QueryException( - f"TikTok API query failed because {error_message['error']['message']}" - ) - return User.UserLikedVideosResponseModel(**orjson.loads(response.content)) - except QueryException as e: - raise e - except ValidationError as e: - logger.error( - f"The attempted query failed because the response body was invalid: {e}" - ) - raise e - except Exception as e: - logger.error( - f"An unknown exception occurred while querying the TikTok API: {e}" - ) - raise e async def pinned_videos( self, username: str, fields: list[User.UserVideosQueryFields] @@ -147,7 +85,7 @@ async def pinned_videos( Parameters: username (str): The username of the TikTok user whose pinned videos are to be retrieved. - fields (list[User.UserVideosQueryFields]): A list of fields to retrieve from the API. + fields (List[User.UserVideosQueryFields]): A list of fields to retrieve from the API. Returns: User.UserPinnedVideosResponseModel: The response data model containing the user's pinned videos. @@ -157,40 +95,13 @@ async def pinned_videos( ValidationError: If the response body is invalid according to the expected model. Exception: For any other unexpected errors that may occur during the API request. """ - headers = User.UserDataRequestHeadersModel( - authorization=await self.query.auth.get_access_token() + return await self._fetch_data( + url=self.query.endpoints.UserPinnedVideosURL, + request_model_class=User.UserPinnedVideosRequestModel, + response_model_class=User.UserPinnedVideosResponseModel, + params={"fields": fields}, + json_data={"username": username}, ) - try: - response: httpx.Response = await self.query.client.post( - url=self.query.endpoints.UserPinnedVideosURL, - headers=headers.model_dump(by_alias=True), - params={"fields": fields}, - json=User.UserPinnedVideosRequestModel( - username=username, - ).model_dump(), - ) - if response.status_code != 200: - error_message: dict[str, str] = orjson.loads(response.text) - - logger.error( - f"The attempted query failed with the status code: {response.status_code} because {error_message['error']['message']}" - ) - raise QueryException( - f"TikTok API query failed because {error_message['error']['message']}" - ) - return User.UserPinnedVideosResponseModel(**orjson.loads(response.content)) - except QueryException as e: - raise e - except ValidationError as e: - logger.error( - f"The attempted query failed because the response body was invalid: {e}" - ) - raise e - except Exception as e: - logger.error( - f"An unknown exception occurred while querying the TikTok API: {e}" - ) - raise e async def reposted_videos( self, @@ -204,7 +115,7 @@ async def reposted_videos( Parameters: username (str): The username of the TikTok user whose reposted videos are to be retrieved. - fields (list[User.UserVideosQueryFields]): A list of fields to retrieve from the API. + fields (List[User.UserVideosQueryFields]): A list of fields to retrieve from the API. max_count (int | None): The maximum number of reposted videos to retrieve. Defaults to None. cursor (int | None): A cursor for pagination, allowing retrieval of additional reposted videos. Defaults to None. @@ -216,41 +127,14 @@ async def reposted_videos( ValidationError: If the response body is invalid according to the expected model. Exception: For any other unexpected errors that may occur during the API request. """ - headers = User.UserDataRequestHeadersModel( - authorization=await self.query.auth.get_access_token() + return await self._fetch_data( + url=self.query.endpoints.UserRepostedVideosURL, + request_model_class=User.UserRepostedVideosRequestModel, + response_model_class=User.UserRepostedVideosResponseModel, + params={"fields": fields}, + json_data={ + "username": username, + "max_count": max_count, + "cursor": cursor, + }, ) - try: - response: httpx.Response = await self.query.client.post( - url=self.query.endpoints.UserRepostedVideosURL, - headers=headers.model_dump(by_alias=True), - params={"fields": fields}, - json=User.UserRepostedVideosRequestModel( - username=username, - max_count=max_count, - cursor=cursor, - ).model_dump(exclude_none=True), - ) - if response.status_code != 200: - error_message: dict[str, str] = orjson.loads(response.text) - - logger.error( - f"The attempted query failed with the status code: {response.status_code} because {error_message['error']['message']}" - ) - raise QueryException( - f"TikTok API query failed because {error_message['error']['message']}" - ) - return User.UserRepostedVideosResponseModel( - **orjson.loads(response.content) - ) - except QueryException as e: - raise e - except ValidationError as e: - logger.error( - f"The attempted query failed because the response body was invalid: {e}" - ) - raise e - except Exception as e: - logger.error( - f"An unknown exception occurred while querying the TikTok API: {e}" - ) - raise e diff --git a/TikTok/ValidationModels/BaseModels.py b/TikTok/ValidationModels/BaseModels.py index 9002873..6534625 100644 --- a/TikTok/ValidationModels/BaseModels.py +++ b/TikTok/ValidationModels/BaseModels.py @@ -16,10 +16,11 @@ thus preventing potential issues when making requests. """ -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel, ConfigDict, Field +from TikTok.ValidationModels.OAuth2 import AuthorizationHeaderModel -class NoExtraFieldsBaseModel(BaseModel): +class BaseRequestModel(BaseModel): """ A base model that forbids extra fields during instantiation. @@ -46,14 +47,28 @@ class User(NoExtraFieldsBaseModel): model_config: ConfigDict = ConfigDict(extra="forbid") + class HeadersModel(AuthorizationHeaderModel): + """ + Model for request headers specific to user data requests. -class HeadersModel(NoExtraFieldsBaseModel): + Attributes: + content_type (str): The content type of the request, defaulting to "application/json". + """ + + model_config: ConfigDict = ConfigDict(populate_by_name=True) + content_type: str = Field(default="application/json", alias="Content-Type") + + +class ResponseErrorModel(BaseRequestModel): """ - A model for headers that ensures only the specified fields are allowed. + Model for error information in the API response. - This class extends `NoExtraFieldsBaseModel` and configures it to allow - population by field name. It is specifically designed for headers that - must match the exact HTTP header names required by the API. + Attributes: + code (str): Error code. + message (str): Error message. + log_id (str): Log identifier for the error. """ - model_config: ConfigDict = ConfigDict(populate_by_name=True) + code: str | None = Field(default=None) + message: str | None = Field(default=None) + log_id: str | None = Field(default=None) diff --git a/TikTok/ValidationModels/Common.py b/TikTok/ValidationModels/Common.py deleted file mode 100644 index 695f7c9..0000000 --- a/TikTok/ValidationModels/Common.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -Contains data models shared between all API endpoints. - -It defines the structure of error responses returned by the API, ensuring -consistency and clarity in error handling across different endpoints. -""" - -from pydantic import Field -from TikTok.ValidationModels.BaseModels import NoExtraFieldsBaseModel - - -class ResponseErrorModel(NoExtraFieldsBaseModel): - """ - Model for error information in the API response. - - Attributes: - code (str): Error code. - message (str): Error message. - log_id (str): Log identifier for the error. - """ - - code: str | None = Field(default=None) - message: str | None = Field(default=None) - log_id: str | None = Field(default=None) diff --git a/TikTok/ValidationModels/OAuth2.py b/TikTok/ValidationModels/OAuth2.py index 68fee77..e9dd686 100644 --- a/TikTok/ValidationModels/OAuth2.py +++ b/TikTok/ValidationModels/OAuth2.py @@ -10,8 +10,7 @@ from enum import StrEnum from datetime import datetime -from pydantic import BaseModel, Field, field_validator -from TikTok.ValidationModels.BaseModels import NoExtraFieldsBaseModel, HeadersModel +from pydantic import BaseModel, Field, field_validator, ConfigDict class OAuth2GrantType(StrEnum): @@ -42,7 +41,7 @@ class OAuth2TokenType(StrEnum): bearer = "Bearer" -class RequestHeadersModel(HeadersModel): +class RequestHeadersModel(BaseModel): """ Model representing the request headers for OAuth2 requests. @@ -57,13 +56,15 @@ class RequestHeadersModel(HeadersModel): "no-cache". """ + model_config: ConfigDict = ConfigDict(populate_by_name=True) + content_type: str = Field( default="application/x-www-form-urlencoded", alias="Content-Type" ) cache_control: str = Field(default="no-cache", alias="Cache-Control") -class AuthorizationHeaderModel(HeadersModel): +class AuthorizationHeaderModel(BaseModel): """ Model representing the authorization header for OAuth2 requests. @@ -74,6 +75,8 @@ class AuthorizationHeaderModel(HeadersModel): authorization (str): The authorization token, typically prefixed with "Bearer ". """ + model_config: ConfigDict = ConfigDict(populate_by_name=True) + authorization: str = Field(alias="Authorization") @field_validator("authorization", mode="before") @@ -94,7 +97,7 @@ def prepend_bearer(cls, value: str) -> str: return f"Bearer {value}" if not value.startswith("Bearer ") else value -class TokenRequestBodyModel(NoExtraFieldsBaseModel): +class TokenRequestBodyModel(BaseModel): """ Model representing the body of a token request for OAuth2. @@ -133,7 +136,7 @@ class OAuth2ResponseModel(BaseModel): token_type: OAuth2TokenType -class OAuth2Token(NoExtraFieldsBaseModel): +class OAuth2Token(BaseModel): """ Model representing an OAuth2 token. diff --git a/TikTok/ValidationModels/Playlist.py b/TikTok/ValidationModels/Playlist.py index 6617131..9977e45 100644 --- a/TikTok/ValidationModels/Playlist.py +++ b/TikTok/ValidationModels/Playlist.py @@ -4,23 +4,10 @@ from pydantic import BaseModel, Field -from TikTok.ValidationModels.BaseModels import NoExtraFieldsBaseModel -from TikTok.ValidationModels.Common import ResponseErrorModel -from TikTok.ValidationModels.OAuth2 import AuthorizationHeaderModel +from TikTok.ValidationModels.BaseModels import BaseRequestModel, ResponseErrorModel -class RequestHeadersModel(AuthorizationHeaderModel): - """ - Model for request headers, extending AuthorizationHeaderModel. - - Attributes: - content_type (str): The content type of the request, defaulting to "application/json". - """ - - content_type: str = Field(default="application/json", alias="Content-Type") - - -class InfoRequestModel(BaseModel): +class InfoRequestModel(BaseRequestModel): """ Model for the playlist info request. @@ -36,7 +23,7 @@ class InfoRequestModel(BaseModel): ) -class ResponseDataModel(NoExtraFieldsBaseModel): +class ResponseDataModel(BaseRequestModel): """ Model for playlist data in the API response. diff --git a/TikTok/ValidationModels/RestAPI.py b/TikTok/ValidationModels/RestAPI.py index a9086bf..8694937 100644 --- a/TikTok/ValidationModels/RestAPI.py +++ b/TikTok/ValidationModels/RestAPI.py @@ -9,7 +9,7 @@ from enum import StrEnum from pydantic import HttpUrl -from TikTok.ValidationModels.BaseModels import NoExtraFieldsBaseModel +from TikTok.ValidationModels.BaseModels import BaseRequestModel class BaseAPI(StrEnum): @@ -29,7 +29,7 @@ class BaseAPI(StrEnum): api_version: str = "v2" -class APIEndpoints(NoExtraFieldsBaseModel): +class APIEndpoints(BaseRequestModel): """ Model representing the API endpoints for the TikTok API. @@ -57,6 +57,12 @@ class APIEndpoints(NoExtraFieldsBaseModel): UserRepostedVideosURL: HttpUrl = ( f"{BaseAPI.base_url}/{BaseAPI.api_version}/research/user/reposted_videos/" ) + UserFollowingURL: HttpUrl = ( + f"{BaseAPI.base_url}/{BaseAPI.api_version}/research/user/following/" + ) + UserFollowersURL: HttpUrl = ( + f"{BaseAPI.base_url}/{BaseAPI.api_version}/research/user/followers/" + ) PlaylistInfoURL: HttpUrl = ( f"{BaseAPI.base_url}/{BaseAPI.api_version}/research/playlist/info/" ) diff --git a/TikTok/ValidationModels/User.py b/TikTok/ValidationModels/User.py index 204dbf7..eddb4ef 100644 --- a/TikTok/ValidationModels/User.py +++ b/TikTok/ValidationModels/User.py @@ -4,9 +4,7 @@ from enum import StrEnum from pydantic import BaseModel, Field -from TikTok.ValidationModels.Common import ResponseErrorModel -from TikTok.ValidationModels.BaseModels import NoExtraFieldsBaseModel -from TikTok.ValidationModels.OAuth2 import AuthorizationHeaderModel +from TikTok.ValidationModels.BaseModels import ResponseErrorModel, BaseRequestModel class UserInfoQueryFields(StrEnum): @@ -34,21 +32,44 @@ class UserInfoQueryFields(StrEnum): video_count = "video_count" -UserInfoRequestHeadersModel = AuthorizationHeaderModel - - -class UserDataRequestHeadersModel(AuthorizationHeaderModel): +class UserVideosQueryFields(StrEnum): """ - Model for request headers specific to user data requests. + Enumeration of query fields for liked videos. Attributes: - content_type (str): The content type of the request, defaulting to "application/json". + id (int64): The unique identifier of the TikTok video. + create_time (int64): UTC Unix epoch (in seconds) of when the TikTok video was posted. + username (str): The username as the unique identifier of the video creator. + region_code (str): A two digit code for the country where the video creator registered their account. + video_description (str): The description of the liked video. + music_id (int64): The music ID used in the video. + like_count (int64): The number of likes the video has received. + comment_count (int64): The number of comments the video has received. + share_count (int64): The number of shares the video has received. + view_count (int64): The number of views the video has received. + hashtag_names (list[str]): The list of hashtags used in the video. + video_duration (int64): The duration of the video, in seconds. + is_stem_verified (bool): Whether the video has been verified as being high quality STEM content. + favorites_count (int64): The number of favorites that a video receives. """ - content_type: str = Field(default="application/json", alias="Content-Type") + id = "id" + create_time = "create_time" + username = "username" + region_code = "region_code" + video_description = "video_description" + music_id = "music_id" + like_count = "like_count" + comment_count = "comment_count" + share_count = "share_count" + view_count = "view_count" + hashtag_names = "hashtag_names" + video_duration = "video_duration" + is_stem_verified = "is_stem_verified" + favorites_count = "favorites_count" -class UserInfoResponseDataModel(NoExtraFieldsBaseModel): +class UserInfoResponseDataModel(BaseRequestModel): """ Model for user data in the API response. @@ -90,17 +111,6 @@ class UserInfoResponseDataModel(NoExtraFieldsBaseModel): ) -class UserInfoRequestModel(NoExtraFieldsBaseModel): - """ - Model for the user info request. - - Attributes: - username (str): The username of the user to fetch information for. - """ - - username: str = Field(description="Username as the unique identifier") - - class UserInfoResponseModel(BaseModel): """ Model for the complete API response for user information. @@ -114,44 +124,18 @@ class UserInfoResponseModel(BaseModel): error: ResponseErrorModel -class UserVideosQueryFields(StrEnum): +class UserInfoRequestModel(BaseRequestModel): """ - Enumeration of query fields for liked videos. + Model for the user info request. Attributes: - id (int64): The unique identifier of the TikTok video. - create_time (int64): UTC Unix epoch (in seconds) of when the TikTok video was posted. - username (str): The username as the unique identifier of the video creator. - region_code (str): A two digit code for the country where the video creator registered their account. - video_description (str): The description of the liked video. - music_id (int64): The music ID used in the video. - like_count (int64): The number of likes the video has received. - comment_count (int64): The number of comments the video has received. - share_count (int64): The number of shares the video has received. - view_count (int64): The number of views the video has received. - hashtag_names (list[str]): The list of hashtags used in the video. - video_duration (int64): The duration of the video, in seconds. - is_stem_verified (bool): Whether the video has been verified as being high quality STEM content. - favorites_count (int64): The number of favorites that a video receives. + username (str): The username of the user to fetch information for. """ - id = "id" - create_time = "create_time" - username = "username" - region_code = "region_code" - video_description = "video_description" - music_id = "music_id" - like_count = "like_count" - comment_count = "comment_count" - share_count = "share_count" - view_count = "view_count" - hashtag_names = "hashtag_names" - video_duration = "video_duration" - is_stem_verified = "is_stem_verified" - favorites_count = "favorites_count" + username: str = Field(description="Username as the unique identifier") -class UserLikedVideosRequestModel(NoExtraFieldsBaseModel): +class UserLikedVideosRequestModel(BaseRequestModel): """ Model for the request to retrieve liked videos of a user. @@ -177,7 +161,7 @@ class UserLikedVideosRequestModel(NoExtraFieldsBaseModel): ) -class UserPinnedVideosRequestModel(NoExtraFieldsBaseModel): +class UserPinnedVideosRequestModel(BaseRequestModel): """ Model for the request to retrieve pinned videos of a user. @@ -188,7 +172,7 @@ class UserPinnedVideosRequestModel(NoExtraFieldsBaseModel): username: str = Field(description="Username as the unique identifier") -class UserRepostedVideosRequestModel(NoExtraFieldsBaseModel): +class UserRepostedVideosRequestModel(BaseRequestModel): """ Model for the request to retrieve reposted videos of a user. diff --git a/poetry.lock b/poetry.lock index 564233d..b610163 100644 --- a/poetry.lock +++ b/poetry.lock @@ -252,6 +252,125 @@ files = [ [package.dependencies] pycparser = "*" +[[package]] +name = "cytoolz" +version = "0.12.3" +description = "Cython implementation of Toolz: High performance functional utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cytoolz-0.12.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bbe58e26c84b163beba0fbeacf6b065feabc8f75c6d3fe305550d33f24a2d346"}, + {file = "cytoolz-0.12.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c51b66ada9bfdb88cf711bf350fcc46f82b83a4683cf2413e633c31a64df6201"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e70d9c615e5c9dc10d279d1e32e846085fe1fd6f08d623ddd059a92861f4e3dd"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a83f4532707963ae1a5108e51fdfe1278cc8724e3301fee48b9e73e1316de64f"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d028044524ee2e815f36210a793c414551b689d4f4eda28f8bbb0883ad78bf5f"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c2875bcd1397d0627a09a4f9172fa513185ad302c63758efc15b8eb33cc2a98"}, + {file = "cytoolz-0.12.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:131ff4820e5d64a25d7ad3c3556f2d8aa65c66b3f021b03f8a8e98e4180dd808"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:04afa90d9d9d18394c40d9bed48c51433d08b57c042e0e50c8c0f9799735dcbd"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:dc1ca9c610425f9854323669a671fc163300b873731584e258975adf50931164"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:bfa3f8e01bc423a933f2e1c510cbb0632c6787865b5242857cc955cae220d1bf"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:f702e295dddef5f8af4a456db93f114539b8dc2a7a9bc4de7c7e41d169aa6ec3"}, + {file = "cytoolz-0.12.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0fbad1fb9bb47e827d00e01992a099b0ba79facf5e5aa453be066033232ac4b5"}, + {file = "cytoolz-0.12.3-cp310-cp310-win32.whl", hash = "sha256:8587c3c3dbe78af90c5025288766ac10dc2240c1e76eb0a93a4e244c265ccefd"}, + {file = "cytoolz-0.12.3-cp310-cp310-win_amd64.whl", hash = "sha256:9e45803d9e75ef90a2f859ef8f7f77614730f4a8ce1b9244375734567299d239"}, + {file = "cytoolz-0.12.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3ac4f2fb38bbc67ff1875b7d2f0f162a247f43bd28eb7c9d15e6175a982e558d"}, + {file = "cytoolz-0.12.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0cf1e1e96dd86829a0539baf514a9c8473a58fbb415f92401a68e8e52a34ecd5"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08a438701c6141dd34eaf92e9e9a1f66e23a22f7840ef8a371eba274477de85d"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6b6f11b0d7ed91be53166aeef2a23a799e636625675bb30818f47f41ad31821"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7fde09384d23048a7b4ac889063761e44b89a0b64015393e2d1d21d5c1f534a"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d3bfe45173cc8e6c76206be3a916d8bfd2214fb2965563e288088012f1dabfc"}, + {file = "cytoolz-0.12.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27513a5d5b6624372d63313574381d3217a66e7a2626b056c695179623a5cb1a"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d294e5e81ff094fe920fd545052ff30838ea49f9e91227a55ecd9f3ca19774a0"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:727b01a2004ddb513496507a695e19b5c0cfebcdfcc68349d3efd92a1c297bf4"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:fe1e1779a39dbe83f13886d2b4b02f8c4b10755e3c8d9a89b630395f49f4f406"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:de74ef266e2679c3bf8b5fc20cee4fc0271ba13ae0d9097b1491c7a9bcadb389"}, + {file = "cytoolz-0.12.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e04d22049233394e0b08193aca9737200b4a2afa28659d957327aa780ddddf2"}, + {file = "cytoolz-0.12.3-cp311-cp311-win32.whl", hash = "sha256:20d36430d8ac809186736fda735ee7d595b6242bdb35f69b598ef809ebfa5605"}, + {file = "cytoolz-0.12.3-cp311-cp311-win_amd64.whl", hash = "sha256:780c06110f383344d537f48d9010d79fa4f75070d214fc47f389357dd4f010b6"}, + {file = "cytoolz-0.12.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:86923d823bd19ce35805953b018d436f6b862edd6a7c8b747a13d52b39ed5716"}, + {file = "cytoolz-0.12.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3e61acfd029bfb81c2c596249b508dfd2b4f72e31b7b53b62e5fb0507dd7293"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd728f4e6051af6af234651df49319da1d813f47894d4c3c8ab7455e01703a37"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe8c6267caa7ec67bcc37e360f0d8a26bc3bdce510b15b97f2f2e0143bdd3673"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99462abd8323c52204a2a0ce62454ce8fa0f4e94b9af397945c12830de73f27e"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da125221b1fa25c690fcd030a54344cecec80074df018d906fc6a99f46c1e3a6"}, + {file = "cytoolz-0.12.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c18e351956f70db9e2d04ff02f28e9a41839250d3f936a4c8a1eabd1c3094d2"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:921e6d2440ac758c4945c587b1d1d9b781b72737ac0c0ca5d5e02ca1db8bded2"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1651a9bd591a8326329ce1d6336f3129161a36d7061a4d5ea9e5377e033364cf"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8893223b87c2782bd59f9c4bd5c7bf733edd8728b523c93efb91d7468b486528"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:e4d2961644153c5ae186db964aa9f6109da81b12df0f1d3494b4e5cf2c332ee2"}, + {file = "cytoolz-0.12.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:71b6eb97f6695f7ba8ce69c49b707a351c5f46fd97f5aeb5f6f2fb0d6e72b887"}, + {file = "cytoolz-0.12.3-cp312-cp312-win32.whl", hash = "sha256:cee3de65584e915053412cd178729ff510ad5f8f585c21c5890e91028283518f"}, + {file = "cytoolz-0.12.3-cp312-cp312-win_amd64.whl", hash = "sha256:9eef0d23035fa4dcfa21e570961e86c375153a7ee605cdd11a8b088c24f707f6"}, + {file = "cytoolz-0.12.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9a38332cfad2a91e89405b7c18b3f00e2edc951c225accbc217597d3e4e9fde"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f501ae1353071fa5d6677437bbeb1aeb5622067dce0977cedc2c5ec5843b202"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56f899758146a52e2f8cfb3fb6f4ca19c1e5814178c3d584de35f9e4d7166d91"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:800f0526adf9e53d3c6acda748f4def1f048adaa780752f154da5cf22aa488a2"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0976a3fcb81d065473173e9005848218ce03ddb2ec7d40dd6a8d2dba7f1c3ae"}, + {file = "cytoolz-0.12.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c835eab01466cb67d0ce6290601ebef2d82d8d0d0a285ed0d6e46989e4a7a71a"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4fba0616fcd487e34b8beec1ad9911d192c62e758baa12fcb44448b9b6feae22"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6f6e8207d732651e0204779e1ba5a4925c93081834570411f959b80681f8d333"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:8119bf5961091cfe644784d0bae214e273b3b3a479f93ee3baab97bbd995ccfe"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:7ad1331cb68afeec58469c31d944a2100cee14eac221553f0d5218ace1a0b25d"}, + {file = "cytoolz-0.12.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:92c53d508fb8a4463acc85b322fa24734efdc66933a5c8661bdc862103a3373d"}, + {file = "cytoolz-0.12.3-cp37-cp37m-win32.whl", hash = "sha256:2c6dd75dae3d84fa8988861ab8b1189d2488cb8a9b8653828f9cd6126b5e7abd"}, + {file = "cytoolz-0.12.3-cp37-cp37m-win_amd64.whl", hash = "sha256:caf07a97b5220e6334dd32c8b6d8b2bd255ca694eca5dfe914bb5b880ee66cdb"}, + {file = "cytoolz-0.12.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ed0cfb9326747759e2ad81cb6e45f20086a273b67ac3a4c00b19efcbab007c60"}, + {file = "cytoolz-0.12.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:96a5a0292575c3697121f97cc605baf2fd125120c7dcdf39edd1a135798482ca"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b76f2f50a789c44d6fd7f773ec43d2a8686781cd52236da03f7f7d7998989bee"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2905fdccacc64b4beba37f95cab9d792289c80f4d70830b70de2fc66c007ec01"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ebe23028eac51251f22ba01dba6587d30aa9c320372ca0c14eeab67118ec3f"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96c715404a3825e37fe3966fe84c5f8a1f036e7640b2a02dbed96cac0c933451"}, + {file = "cytoolz-0.12.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bac0adffc1b6b6a4c5f1fd1dd2161afb720bcc771a91016dc6bdba59af0a5d3"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:37441bf4a2a4e2e0fe9c3b0ea5e72db352f5cca03903977ffc42f6f6c5467be9"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f04037302049cb30033f7fa4e1d0e44afe35ed6bfcf9b380fc11f2a27d3ed697"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f37b60e66378e7a116931d7220f5352186abfcc950d64856038aa2c01944929c"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ec9be3e4b6f86ea8b294d34c990c99d2ba6c526ef1e8f46f1d52c263d4f32cd7"}, + {file = "cytoolz-0.12.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e9199c9e3fbf380a92b8042c677eb9e7ed4bccb126de5e9c0d26f5888d96788"}, + {file = "cytoolz-0.12.3-cp38-cp38-win32.whl", hash = "sha256:18cd61e078bd6bffe088e40f1ed02001387c29174750abce79499d26fa57f5eb"}, + {file = "cytoolz-0.12.3-cp38-cp38-win_amd64.whl", hash = "sha256:765b8381d4003ceb1a07896a854eee2c31ebc950a4ae17d1e7a17c2a8feb2a68"}, + {file = "cytoolz-0.12.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b4a52dd2a36b0a91f7aa50ca6c8509057acc481a24255f6cb07b15d339a34e0f"}, + {file = "cytoolz-0.12.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:581f1ce479769fe7eeb9ae6d87eadb230df8c7c5fff32138162cdd99d7fb8fc3"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46f505d4c6eb79585c8ad0b9dc140ef30a138c880e4e3b40230d642690e36366"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59276021619b432a5c21c01cda8320b9cc7dbc40351ffc478b440bfccd5bbdd3"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e44f4c25e1e7cf6149b499c74945a14649c8866d36371a2c2d2164e4649e7755"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c64f8e60c1dd69e4d5e615481f2d57937746f4a6be2d0f86e9e7e3b9e2243b5e"}, + {file = "cytoolz-0.12.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33c63186f3bf9d7ef1347bc0537bb9a0b4111a0d7d6e619623cabc18fef0dc3b"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fdddb9d988405f24035234f1e8d1653ab2e48cc2404226d21b49a129aefd1d25"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6986632d8a969ea1e720990c818dace1a24c11015fd7c59b9fea0b65ef71f726"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0ba1cbc4d9cd7571c917f88f4a069568e5121646eb5d82b2393b2cf84712cf2a"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7d267ffc9a36c0a9a58c7e0adc9fa82620f22e4a72533e15dd1361f57fc9accf"}, + {file = "cytoolz-0.12.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95e878868a172a41fbf6c505a4b967309e6870e22adc7b1c3b19653d062711fa"}, + {file = "cytoolz-0.12.3-cp39-cp39-win32.whl", hash = "sha256:8e21932d6d260996f7109f2a40b2586070cb0a0cf1d65781e156326d5ebcc329"}, + {file = "cytoolz-0.12.3-cp39-cp39-win_amd64.whl", hash = "sha256:0d8edfbc694af6c9bda4db56643fb8ed3d14e47bec358c2f1417de9a12d6d1fb"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:55f9bd1ae6c2a27eda5abe2a0b65a83029d2385c5a1da7b8ef47af5905d7e905"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2d271393c378282727f1231d40391ae93b93ddc0997448acc21dd0cb6a1e56d"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee98968d6a66ee83a8ceabf31182189ab5d8598998c8ce69b6d5843daeb2db60"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01cfb8518828c1189200c02a5010ea404407fb18fd5589e29c126e84bbeadd36"}, + {file = "cytoolz-0.12.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:456395d7aec01db32bf9e6db191d667347c78d8d48e77234521fa1078f60dabb"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:cd88028bb897fba99ddd84f253ca6bef73ecb7bdf3f3cf25bc493f8f97d3c7c5"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59b19223e7f7bd7a73ec3aa6fdfb73b579ff09c2bc0b7d26857eec2d01a58c76"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a79d72b08048a0980a59457c239555f111ac0c8bdc140c91a025f124104dbb4"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1dd70141b32b717696a72b8876e86bc9c6f8eff995c1808e299db3541213ff82"}, + {file = "cytoolz-0.12.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:a1445c91009eb775d479e88954c51d0b4cf9a1e8ce3c503c2672d17252882647"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ca6a9a9300d5bda417d9090107c6d2b007683efc59d63cc09aca0e7930a08a85"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be6feb903d2a08a4ba2e70e950e862fd3be9be9a588b7c38cee4728150a52918"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b6f43f086e5a965d33d62a145ae121b4ccb6e0789ac0acc895ce084fec8c65"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:534fa66db8564d9b13872d81d54b6b09ae592c585eb826aac235bd6f1830f8ad"}, + {file = "cytoolz-0.12.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:fea649f979def23150680de1bd1d09682da3b54932800a0f90f29fc2a6c98ba8"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a447247ed312dd64e3a8d9483841ecc5338ee26d6e6fbd29cd373ed030db0240"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba3f843aa89f35467b38c398ae5b980a824fdbdb94065adc6ec7c47a0a22f4c7"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:582c22f97a380211fb36a7b65b1beeb84ea11d82015fa84b054be78580390082"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47feb089506fc66e1593cd9ade3945693a9d089a445fbe9a11385cab200b9f22"}, + {file = "cytoolz-0.12.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ba9002d2f043943744a9dc8e50a47362bcb6e6f360dc0a1abcb19642584d87bb"}, + {file = "cytoolz-0.12.3.tar.gz", hash = "sha256:4503dc59f4ced53a54643272c61dc305d1dbbfbd7d6bdf296948de9f34c3a282"}, +] + +[package.dependencies] +toolz = ">=0.8.0" + +[package.extras] +cython = ["cython"] + [[package]] name = "h11" version = "0.14.0" @@ -832,6 +951,17 @@ files = [ doc = ["reno", "sphinx"] test = ["pytest", "tornado (>=4.5)", "typeguard"] +[[package]] +name = "toolz" +version = "0.12.1" +description = "List processing tools and functional utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "toolz-0.12.1-py3-none-any.whl", hash = "sha256:d22731364c07d72eea0a0ad45bafb2c2937ab6fd38a3507bf55eae8744aa7d85"}, + {file = "toolz-0.12.1.tar.gz", hash = "sha256:ecca342664893f177a13dac0e6b41cbd8ac25a358e5f215316d43e2100224f4d"}, +] + [[package]] name = "typing-extensions" version = "4.12.2" @@ -958,4 +1088,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "e0a9d47c4e76cdde1984322adda378b684f347fbdc1d94c38d30ceff6c9517eb" +content-hash = "46fe1252c525d86143311058f55b72af49f3121e8d1015096876131295707efc" diff --git a/pyproject.toml b/pyproject.toml index 0418149..63b1a24 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ rich = "13.8.1" stamina = "24.3.0" structlog = "24.4.0" setuptools = "75.1.0" +cytoolz = "^0.12.3" [tool.poetry.group.docs] optional = true