From 664bd5d23a7ff5de461958114b7b4f18b3fae55d Mon Sep 17 00:00:00 2001 From: Satoshi <6world4trigger@gmail.com> Date: Fri, 18 Oct 2024 05:16:05 +0800 Subject: [PATCH 1/5] fix: `abstractclassmethod` -> `abstractmethod` --- notion_client/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notion_client/client.py b/notion_client/client.py index 4c2bb2f..cd1dc81 100644 --- a/notion_client/client.py +++ b/notion_client/client.py @@ -1,7 +1,7 @@ """Synchronous and asynchronous clients for Notion's API.""" import json import logging -from abc import abstractclassmethod +from abc import abstractmethod from dataclasses import dataclass from types import TracebackType from typing import Any, Dict, List, Optional, Type, Union @@ -131,7 +131,7 @@ def _parse_response(self, response: Response) -> Any: return body - @abstractclassmethod + @abstractmethod def request( self, path: str, From d1626f7fe73c1322418fdcd0697393d87190632c Mon Sep 17 00:00:00 2001 From: Satoshi <6world4trigger@gmail.com> Date: Sat, 26 Oct 2024 10:52:38 +0800 Subject: [PATCH 2/5] Add `ClientType` and `ResponseType` --- notion_client/typing.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/notion_client/typing.py b/notion_client/typing.py index 97ebc80..66c6eb8 100644 --- a/notion_client/typing.py +++ b/notion_client/typing.py @@ -1,5 +1,11 @@ """Custom type definitions for notion-sdk-py.""" -from typing import Awaitable, TypeVar, Union +from typing import TYPE_CHECKING, Awaitable, TypeVar, Union + +if TYPE_CHECKING: # pragma: no cover + from notion_client.client import BaseClient T = TypeVar("T") SyncAsync = Union[T, Awaitable[T]] + +ClientType = TypeVar("ClientType", bound=BaseClient) +ResponseType = TypeVar("ResponseType", bound=dict) \ No newline at end of file From ee3e34d7364dfbb68d2433c3ec9f9860d1433292 Mon Sep 17 00:00:00 2001 From: Satoshi <6world4trigger@gmail.com> Date: Sat, 26 Oct 2024 10:55:40 +0800 Subject: [PATCH 3/5] use generic in BaseClient and cast request output to `ResponseType` --- notion_client/client.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/notion_client/client.py b/notion_client/client.py index cd1dc81..8ba9475 100644 --- a/notion_client/client.py +++ b/notion_client/client.py @@ -4,7 +4,7 @@ from abc import abstractmethod from dataclasses import dataclass from types import TracebackType -from typing import Any, Dict, List, Optional, Type, Union +from typing import Any, Dict, List, Generic, Optional, Type, Union import httpx from httpx import Request, Response @@ -24,7 +24,7 @@ is_api_error_code, ) from notion_client.logging import make_console_logger -from notion_client.typing import SyncAsync +from notion_client.typing import ClientType, ResponseType, SyncAsync @dataclass @@ -52,7 +52,7 @@ class ClientOptions: notion_version: str = "2022-06-28" -class BaseClient: +class BaseClient(Generic[ClientType]): def __init__( self, client: Union[httpx.Client, httpx.AsyncClient], @@ -71,12 +71,12 @@ def __init__( self._clients: List[Union[httpx.Client, httpx.AsyncClient]] = [] self.client = client - self.blocks = BlocksEndpoint(self) - self.databases = DatabasesEndpoint(self) - self.users = UsersEndpoint(self) - self.pages = PagesEndpoint(self) - self.search = SearchEndpoint(self) - self.comments = CommentsEndpoint(self) + self.blocks = BlocksEndpoint[ClientType](self) + self.databases = DatabasesEndpoint[ClientType](self) + self.users = UsersEndpoint[ClientType](self) + self.pages = PagesEndpoint[ClientType](self) + self.search = SearchEndpoint[ClientType](self) + self.comments = CommentsEndpoint[ClientType](self) @property def client(self) -> Union[httpx.Client, httpx.AsyncClient]: @@ -136,10 +136,11 @@ def request( self, path: str, method: str, + cast_to: Type[ResponseType], query: Optional[Dict[Any, Any]] = None, body: Optional[Dict[Any, Any]] = None, auth: Optional[str] = None, - ) -> SyncAsync[Any]: + ) -> SyncAsync[ResponseType]: # noqa pass @@ -181,17 +182,18 @@ def request( self, path: str, method: str, + cast_to: Type[ResponseType], query: Optional[Dict[Any, Any]] = None, body: Optional[Dict[Any, Any]] = None, auth: Optional[str] = None, - ) -> Any: + ) -> ResponseType: """Send an HTTP request.""" request = self._build_request(method, path, query, body, auth) try: response = self.client.send(request) except httpx.TimeoutException: raise RequestTimeoutError() - return self._parse_response(response) + return cast_to(self._parse_response(response)) class AsyncClient(BaseClient): @@ -231,14 +233,15 @@ async def request( self, path: str, method: str, + cast_to: Type[ResponseType], query: Optional[Dict[Any, Any]] = None, body: Optional[Dict[Any, Any]] = None, auth: Optional[str] = None, - ) -> Any: + ) -> ResponseType: """Send an HTTP request asynchronously.""" request = self._build_request(method, path, query, body, auth) try: response = await self.client.send(request) except httpx.TimeoutException: raise RequestTimeoutError() - return self._parse_response(response) + return cast_to(self._parse_response(response)) From 74b3a606a4c2b84ad699c46defb9494ac8f0dd0b Mon Sep 17 00:00:00 2001 From: Satoshi <6world4trigger@gmail.com> Date: Sun, 27 Oct 2024 07:33:06 +0800 Subject: [PATCH 4/5] change bound of `ResponseType` to `Mapping` from `Dict` (`TypedDict` is not subclass of `Dict`) --- notion_client/typing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notion_client/typing.py b/notion_client/typing.py index 66c6eb8..9b14c81 100644 --- a/notion_client/typing.py +++ b/notion_client/typing.py @@ -1,5 +1,5 @@ """Custom type definitions for notion-sdk-py.""" -from typing import TYPE_CHECKING, Awaitable, TypeVar, Union +from typing import TYPE_CHECKING, Any, Awaitable, Mapping, TypeVar, Union if TYPE_CHECKING: # pragma: no cover from notion_client.client import BaseClient @@ -8,4 +8,4 @@ SyncAsync = Union[T, Awaitable[T]] ClientType = TypeVar("ClientType", bound=BaseClient) -ResponseType = TypeVar("ResponseType", bound=dict) \ No newline at end of file +ResponseType = TypeVar("ResponseType", bound=Mapping[Any, Any]) \ No newline at end of file From bd566ac631942bce6e8c03fb1692631d0f5904a5 Mon Sep 17 00:00:00 2001 From: Satoshi <6world4trigger@gmail.com> Date: Sun, 27 Oct 2024 07:35:46 +0800 Subject: [PATCH 5/5] use `Mapping` for `pick` too --- notion_client/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notion_client/helpers.py b/notion_client/helpers.py index 7ade264..c93866f 100644 --- a/notion_client/helpers.py +++ b/notion_client/helpers.py @@ -1,10 +1,10 @@ """Utility functions for notion-sdk-py.""" -from typing import Any, AsyncGenerator, Awaitable, Callable, Dict, Generator, List +from typing import Any, AsyncGenerator, Awaitable, Callable, Dict, Generator, Mapping, List from urllib.parse import urlparse from uuid import UUID -def pick(base: Dict[Any, Any], *keys: str) -> Dict[Any, Any]: +def pick(base: Mapping[Any, Any], *keys: str) -> Dict[Any, Any]: """Return a dict composed of key value pairs for keys passed as args.""" result = {} for key in keys: