Coverage for notion_client/helpers.py: 100%
66 statements
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-26 10:36 +0000
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-26 10:36 +0000
1"""Utility functions for notion-sdk-py."""
3from typing import Any, AsyncGenerator, Awaitable, Callable, Dict, Generator, List
4from urllib.parse import urlparse
5from uuid import UUID
8def pick(base: Dict[Any, Any], *keys: str) -> Dict[Any, Any]:
9 """Return a dict composed of key value pairs for keys passed as args."""
10 result = {}
11 for key in keys:
12 if key not in base:
13 continue
14 value = base.get(key)
15 if value is None and key == "start_cursor":
16 continue
17 result[key] = value
18 return result
21def get_url(object_id: str) -> str:
22 """Return the URL for the object with the given id."""
23 return f"https://notion.so/{UUID(object_id).hex}"
26def get_id(url: str) -> str:
27 """Return the id of the object behind the given URL."""
28 parsed = urlparse(url)
29 if parsed.netloc not in ("notion.so", "www.notion.so"):
30 raise ValueError("Not a valid Notion URL.")
31 path = parsed.path
32 if len(path) < 32:
33 raise ValueError("The path in the URL seems to be incorrect.")
34 raw_id = path[-32:]
35 return str(UUID(raw_id))
38def iterate_paginated_api(
39 function: Callable[..., Any], **kwargs: Any
40) -> Generator[Any, None, None]:
41 """Return an iterator over the results of any paginated Notion API."""
42 next_cursor = kwargs.pop("start_cursor", None)
44 while True:
45 response = function(**kwargs, start_cursor=next_cursor)
46 for result in response.get("results"):
47 yield result
49 next_cursor = response.get("next_cursor")
50 if not response.get("has_more") or not next_cursor:
51 return
54def collect_paginated_api(function: Callable[..., Any], **kwargs: Any) -> List[Any]:
55 """Collect all the results of paginating an API into a list."""
56 return [result for result in iterate_paginated_api(function, **kwargs)]
59async def async_iterate_paginated_api(
60 function: Callable[..., Awaitable[Any]], **kwargs: Any
61) -> AsyncGenerator[Any, None]:
62 """Return an async iterator over the results of any paginated Notion API."""
63 next_cursor = kwargs.pop("start_cursor", None)
65 while True:
66 response = await function(**kwargs, start_cursor=next_cursor)
67 for result in response.get("results"):
68 yield result
70 next_cursor = response.get("next_cursor")
71 if (not response["has_more"]) | (next_cursor is None):
72 return
75async def async_collect_paginated_api(
76 function: Callable[..., Awaitable[Any]], **kwargs: Any
77) -> List[Any]:
78 """Collect asynchronously all the results of paginating an API into a list."""
79 return [result async for result in async_iterate_paginated_api(function, **kwargs)]
82def is_full_block(response: Dict[Any, Any]) -> bool:
83 """Return `True` if response is a full block."""
84 return response.get("object") == "block" and "type" in response
87def is_full_page(response: Dict[Any, Any]) -> bool:
88 """Return `True` if response is a full page."""
89 return response.get("object") == "page" and "url" in response
92def is_full_database(response: Dict[Any, Any]) -> bool:
93 """Return `True` if response is a full database."""
94 return response.get("object") == "database" and "title" in response
97def is_full_page_or_database(response: Dict[Any, Any]) -> bool:
98 """Return `True` if `response` is a full database or a full page."""
99 if response.get("object") == "database":
100 return is_full_database(response)
101 return is_full_page(response)
104def is_full_user(response: Dict[Any, Any]) -> bool:
105 """Return `True` if response is a full user."""
106 return "type" in response
109def is_full_comment(response: Dict[Any, Any]) -> bool:
110 """Return `True` if response is a full comment."""
111 return "type" in response
114def is_text_rich_text_item_response(rich_text: Dict[Any, Any]) -> bool:
115 """Return `True` if `rich_text` is a text."""
116 return rich_text.get("type") == "text"
119def is_equation_rich_text_item_response(rich_text: Dict[Any, Any]) -> bool:
120 """Return `True` if `rich_text` is an equation."""
121 return rich_text.get("type") == "equation"
124def is_mention_rich_text_item_response(rich_text: Dict[Any, Any]) -> bool:
125 """Return `True` if `rich_text` is a mention."""
126 return rich_text.get("type") == "mention"