notionary 0.2.19__py3-none-any.whl → 0.2.22__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. notionary/__init__.py +8 -4
  2. notionary/base_notion_client.py +3 -1
  3. notionary/blocks/__init__.py +2 -91
  4. notionary/blocks/_bootstrap.py +271 -0
  5. notionary/blocks/audio/__init__.py +8 -2
  6. notionary/blocks/audio/audio_element.py +69 -106
  7. notionary/blocks/audio/audio_markdown_node.py +13 -5
  8. notionary/blocks/audio/audio_models.py +6 -55
  9. notionary/blocks/base_block_element.py +42 -0
  10. notionary/blocks/bookmark/__init__.py +9 -2
  11. notionary/blocks/bookmark/bookmark_element.py +49 -139
  12. notionary/blocks/bookmark/bookmark_markdown_node.py +19 -18
  13. notionary/blocks/bookmark/bookmark_models.py +15 -0
  14. notionary/blocks/breadcrumbs/__init__.py +17 -0
  15. notionary/blocks/breadcrumbs/breadcrumb_element.py +39 -0
  16. notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +32 -0
  17. notionary/blocks/breadcrumbs/breadcrumb_models.py +12 -0
  18. notionary/blocks/bulleted_list/__init__.py +12 -2
  19. notionary/blocks/bulleted_list/bulleted_list_element.py +55 -53
  20. notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +2 -1
  21. notionary/blocks/bulleted_list/bulleted_list_models.py +18 -0
  22. notionary/blocks/callout/__init__.py +9 -2
  23. notionary/blocks/callout/callout_element.py +53 -86
  24. notionary/blocks/callout/callout_markdown_node.py +3 -1
  25. notionary/blocks/callout/callout_models.py +33 -0
  26. notionary/blocks/child_database/__init__.py +14 -0
  27. notionary/blocks/child_database/child_database_element.py +61 -0
  28. notionary/blocks/child_database/child_database_models.py +12 -0
  29. notionary/blocks/child_page/__init__.py +9 -0
  30. notionary/blocks/child_page/child_page_element.py +94 -0
  31. notionary/blocks/child_page/child_page_models.py +12 -0
  32. notionary/blocks/{shared/block_client.py → client.py} +54 -54
  33. notionary/blocks/code/__init__.py +6 -2
  34. notionary/blocks/code/code_element.py +96 -181
  35. notionary/blocks/code/code_markdown_node.py +64 -13
  36. notionary/blocks/code/code_models.py +94 -0
  37. notionary/blocks/column/__init__.py +25 -1
  38. notionary/blocks/column/column_element.py +44 -312
  39. notionary/blocks/column/column_list_element.py +52 -0
  40. notionary/blocks/column/column_list_markdown_node.py +50 -0
  41. notionary/blocks/column/column_markdown_node.py +59 -0
  42. notionary/blocks/column/column_models.py +26 -0
  43. notionary/blocks/divider/__init__.py +9 -2
  44. notionary/blocks/divider/divider_element.py +18 -49
  45. notionary/blocks/divider/divider_markdown_node.py +2 -1
  46. notionary/blocks/divider/divider_models.py +12 -0
  47. notionary/blocks/embed/__init__.py +9 -2
  48. notionary/blocks/embed/embed_element.py +65 -111
  49. notionary/blocks/embed/embed_markdown_node.py +3 -1
  50. notionary/blocks/embed/embed_models.py +14 -0
  51. notionary/blocks/equation/__init__.py +14 -0
  52. notionary/blocks/equation/equation_element.py +133 -0
  53. notionary/blocks/equation/equation_element_markdown_node.py +35 -0
  54. notionary/blocks/equation/equation_models.py +11 -0
  55. notionary/blocks/file/__init__.py +25 -0
  56. notionary/blocks/file/file_element.py +112 -0
  57. notionary/blocks/file/file_element_markdown_node.py +37 -0
  58. notionary/blocks/file/file_element_models.py +39 -0
  59. notionary/blocks/guards.py +22 -0
  60. notionary/blocks/heading/__init__.py +16 -2
  61. notionary/blocks/heading/heading_element.py +83 -69
  62. notionary/blocks/heading/heading_markdown_node.py +2 -1
  63. notionary/blocks/heading/heading_models.py +29 -0
  64. notionary/blocks/image_block/__init__.py +13 -0
  65. notionary/blocks/image_block/image_element.py +89 -0
  66. notionary/blocks/{image → image_block}/image_markdown_node.py +13 -6
  67. notionary/blocks/image_block/image_models.py +10 -0
  68. notionary/blocks/mixins/captions/__init__.py +4 -0
  69. notionary/blocks/mixins/captions/caption_markdown_node_mixin.py +31 -0
  70. notionary/blocks/mixins/captions/caption_mixin.py +92 -0
  71. notionary/blocks/models.py +174 -0
  72. notionary/blocks/numbered_list/__init__.py +12 -2
  73. notionary/blocks/numbered_list/numbered_list_element.py +48 -56
  74. notionary/blocks/numbered_list/numbered_list_markdown_node.py +3 -1
  75. notionary/blocks/numbered_list/numbered_list_models.py +17 -0
  76. notionary/blocks/paragraph/__init__.py +12 -2
  77. notionary/blocks/paragraph/paragraph_element.py +40 -66
  78. notionary/blocks/paragraph/paragraph_markdown_node.py +2 -1
  79. notionary/blocks/paragraph/paragraph_models.py +16 -0
  80. notionary/blocks/pdf/__init__.py +13 -0
  81. notionary/blocks/pdf/pdf_element.py +97 -0
  82. notionary/blocks/pdf/pdf_markdown_node.py +37 -0
  83. notionary/blocks/pdf/pdf_models.py +11 -0
  84. notionary/blocks/quote/__init__.py +11 -2
  85. notionary/blocks/quote/quote_element.py +45 -62
  86. notionary/blocks/quote/quote_markdown_node.py +6 -3
  87. notionary/blocks/quote/quote_models.py +18 -0
  88. notionary/blocks/registry/__init__.py +4 -0
  89. notionary/blocks/registry/block_registry.py +60 -121
  90. notionary/blocks/registry/block_registry_builder.py +115 -59
  91. notionary/blocks/rich_text/__init__.py +33 -0
  92. notionary/blocks/rich_text/name_to_id_resolver.py +205 -0
  93. notionary/blocks/rich_text/rich_text_models.py +221 -0
  94. notionary/blocks/rich_text/text_inline_formatter.py +456 -0
  95. notionary/blocks/syntax_prompt_builder.py +137 -0
  96. notionary/blocks/table/__init__.py +16 -2
  97. notionary/blocks/table/table_element.py +136 -228
  98. notionary/blocks/table/table_markdown_node.py +2 -1
  99. notionary/blocks/table/table_models.py +28 -0
  100. notionary/blocks/table_of_contents/__init__.py +19 -0
  101. notionary/blocks/table_of_contents/table_of_contents_element.py +68 -0
  102. notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +35 -0
  103. notionary/blocks/table_of_contents/table_of_contents_models.py +18 -0
  104. notionary/blocks/todo/__init__.py +9 -2
  105. notionary/blocks/todo/todo_element.py +52 -92
  106. notionary/blocks/todo/todo_markdown_node.py +2 -1
  107. notionary/blocks/todo/todo_models.py +19 -0
  108. notionary/blocks/toggle/__init__.py +13 -3
  109. notionary/blocks/toggle/toggle_element.py +69 -260
  110. notionary/blocks/toggle/toggle_markdown_node.py +25 -15
  111. notionary/blocks/toggle/toggle_models.py +17 -0
  112. notionary/blocks/toggleable_heading/__init__.py +6 -2
  113. notionary/blocks/toggleable_heading/toggleable_heading_element.py +86 -241
  114. notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +26 -18
  115. notionary/blocks/types.py +130 -0
  116. notionary/blocks/video/__init__.py +8 -2
  117. notionary/blocks/video/video_element.py +70 -141
  118. notionary/blocks/video/video_element_models.py +10 -0
  119. notionary/blocks/video/video_markdown_node.py +13 -6
  120. notionary/database/client.py +26 -8
  121. notionary/database/database.py +13 -14
  122. notionary/database/database_filter_builder.py +2 -2
  123. notionary/database/database_provider.py +5 -4
  124. notionary/database/models.py +337 -0
  125. notionary/database/notion_database.py +6 -7
  126. notionary/file_upload/client.py +5 -7
  127. notionary/file_upload/models.py +3 -2
  128. notionary/file_upload/notion_file_upload.py +2 -3
  129. notionary/markdown/markdown_builder.py +729 -0
  130. notionary/markdown/markdown_document_model.py +228 -0
  131. notionary/{blocks → markdown}/markdown_node.py +1 -0
  132. notionary/models/notion_database_response.py +0 -338
  133. notionary/page/client.py +34 -15
  134. notionary/page/models.py +327 -0
  135. notionary/page/notion_page.py +136 -58
  136. notionary/page/{content/page_content_writer.py → page_content_deleting_service.py} +25 -59
  137. notionary/page/page_content_writer.py +177 -0
  138. notionary/page/page_context.py +65 -0
  139. notionary/page/reader/handler/__init__.py +19 -0
  140. notionary/page/reader/handler/base_block_renderer.py +44 -0
  141. notionary/page/reader/handler/block_processing_context.py +35 -0
  142. notionary/page/reader/handler/block_rendering_context.py +48 -0
  143. notionary/page/reader/handler/column_list_renderer.py +51 -0
  144. notionary/page/reader/handler/column_renderer.py +60 -0
  145. notionary/page/reader/handler/line_renderer.py +73 -0
  146. notionary/page/reader/handler/numbered_list_renderer.py +85 -0
  147. notionary/page/reader/handler/toggle_renderer.py +69 -0
  148. notionary/page/reader/handler/toggleable_heading_renderer.py +89 -0
  149. notionary/page/reader/page_content_retriever.py +81 -0
  150. notionary/page/search_filter_builder.py +2 -1
  151. notionary/page/writer/handler/__init__.py +24 -0
  152. notionary/page/writer/handler/code_handler.py +72 -0
  153. notionary/page/writer/handler/column_handler.py +141 -0
  154. notionary/page/writer/handler/column_list_handler.py +139 -0
  155. notionary/page/writer/handler/equation_handler.py +74 -0
  156. notionary/page/writer/handler/line_handler.py +35 -0
  157. notionary/page/writer/handler/line_processing_context.py +54 -0
  158. notionary/page/writer/handler/regular_line_handler.py +86 -0
  159. notionary/page/writer/handler/table_handler.py +66 -0
  160. notionary/page/writer/handler/toggle_handler.py +155 -0
  161. notionary/page/writer/handler/toggleable_heading_handler.py +173 -0
  162. notionary/page/writer/markdown_to_notion_converter.py +95 -0
  163. notionary/page/writer/markdown_to_notion_converter_context.py +30 -0
  164. notionary/page/writer/markdown_to_notion_formatting_post_processor.py +73 -0
  165. notionary/page/writer/notion_text_length_processor.py +150 -0
  166. notionary/telemetry/__init__.py +2 -2
  167. notionary/telemetry/service.py +3 -3
  168. notionary/user/__init__.py +2 -2
  169. notionary/user/base_notion_user.py +2 -1
  170. notionary/user/client.py +2 -3
  171. notionary/user/models.py +1 -0
  172. notionary/user/notion_bot_user.py +4 -5
  173. notionary/user/notion_user.py +3 -4
  174. notionary/user/notion_user_manager.py +23 -95
  175. notionary/util/__init__.py +3 -2
  176. notionary/util/fuzzy.py +2 -1
  177. notionary/util/logging_mixin.py +2 -2
  178. notionary/util/singleton_metaclass.py +1 -1
  179. notionary/workspace.py +6 -5
  180. notionary-0.2.22.dist-info/METADATA +237 -0
  181. notionary-0.2.22.dist-info/RECORD +200 -0
  182. notionary/blocks/document/__init__.py +0 -7
  183. notionary/blocks/document/document_element.py +0 -102
  184. notionary/blocks/document/document_markdown_node.py +0 -31
  185. notionary/blocks/image/__init__.py +0 -7
  186. notionary/blocks/image/image_element.py +0 -151
  187. notionary/blocks/markdown_builder.py +0 -356
  188. notionary/blocks/mention/__init__.py +0 -7
  189. notionary/blocks/mention/mention_element.py +0 -229
  190. notionary/blocks/mention/mention_markdown_node.py +0 -38
  191. notionary/blocks/prompts/element_prompt_builder.py +0 -83
  192. notionary/blocks/prompts/element_prompt_content.py +0 -41
  193. notionary/blocks/shared/models.py +0 -713
  194. notionary/blocks/shared/notion_block_element.py +0 -37
  195. notionary/blocks/shared/text_inline_formatter.py +0 -262
  196. notionary/blocks/shared/text_inline_formatter_new.py +0 -139
  197. notionary/database/models/page_result.py +0 -10
  198. notionary/models/notion_block_response.py +0 -264
  199. notionary/models/notion_page_response.py +0 -78
  200. notionary/models/search_response.py +0 -0
  201. notionary/page/__init__.py +0 -0
  202. notionary/page/content/markdown_whitespace_processor.py +0 -80
  203. notionary/page/content/notion_text_length_utils.py +0 -87
  204. notionary/page/content/page_content_retriever.py +0 -60
  205. notionary/page/formatting/line_processor.py +0 -153
  206. notionary/page/formatting/markdown_to_notion_converter.py +0 -153
  207. notionary/page/markdown_syntax_prompt_generator.py +0 -114
  208. notionary/page/notion_to_markdown_converter.py +0 -179
  209. notionary/page/properites/property_value_extractor.py +0 -0
  210. notionary/user/notion_user_provider.py +0 -1
  211. notionary-0.2.19.dist-info/METADATA +0 -225
  212. notionary-0.2.19.dist-info/RECORD +0 -150
  213. /notionary/{blocks/document/document_models.py → markdown/___init__.py} +0 -0
  214. /notionary/{blocks/image/image_models.py → markdown/makdown_document_model.py} +0 -0
  215. /notionary/{blocks/mention/mention_models.py → page/reader/handler/equation_renderer.py} +0 -0
  216. /notionary/{blocks/shared/__init__.py → page/writer/markdown_to_notion_post_processor.py} +0 -0
  217. /notionary/{blocks/toggleable_heading/toggleable_heading_models.py → page/writer/markdown_to_notion_text_length_post_processor.py} +0 -0
  218. /notionary/{elements/__init__.py → util/concurrency_limiter.py} +0 -0
  219. {notionary-0.2.19.dist-info → notionary-0.2.22.dist-info}/LICENSE +0 -0
  220. {notionary-0.2.19.dist-info → notionary-0.2.22.dist-info}/WHEEL +0 -0
@@ -1,7 +1,7 @@
1
- from typing import Any, Dict, Optional, List
1
+ from typing import Optional
2
+
2
3
  from notionary.user.client import NotionUserClient
3
4
  from notionary.user.notion_user import NotionUser
4
- from notionary.user.models import NotionUsersListResponse
5
5
  from notionary.util import LoggingMixin
6
6
 
7
7
 
@@ -17,72 +17,52 @@ class NotionUserManager(LoggingMixin):
17
17
  """Initialize the user manager."""
18
18
  self.client = NotionUserClient(token=token)
19
19
 
20
- async def get_current_bot_user(self) -> Optional[NotionUser]:
21
- """
22
- Get the current bot user from the API token.
23
- """
24
- return await NotionUser.current_bot_user(token=self.client.token)
25
-
26
20
  async def get_user_by_id(self, user_id: str) -> Optional[NotionUser]:
27
21
  """
28
22
  Get a specific user by their ID.
29
23
  """
30
24
  return await NotionUser.from_user_id(user_id, token=self.client.token)
31
25
 
32
- async def list_users(
33
- self, page_size: int = 100, start_cursor: Optional[str] = None
34
- ) -> Optional[NotionUsersListResponse]:
35
- """
36
- List users in the workspace (paginated).
37
-
38
- Note: Guests are not included in the response.
39
- """
40
- try:
41
- response = await self.client.list_users(page_size, start_cursor)
42
- if response is None:
43
- self.logger.error("Failed to list users")
44
- return None
45
-
46
- self.logger.info(
47
- "Retrieved %d users (has_more: %s)",
48
- len(response.results),
49
- response.has_more,
50
- )
51
- return response
52
-
53
- except Exception as e:
54
- self.logger.error("Error listing users: %s", str(e))
55
- return None
56
-
57
- async def get_all_users(self) -> List[NotionUser]:
26
+ async def get_all_users(self) -> list[NotionUser]:
58
27
  """
59
28
  Get all users in the workspace as NotionUser objects.
60
29
  Automatically handles pagination and converts responses to NotionUser instances.
30
+ Only returns person users, excludes bots and integrations.
61
31
  """
62
32
  try:
63
33
  # Get raw user responses
64
34
  user_responses = await self.client.get_all_users()
65
35
 
66
- # Convert to NotionUser objects
36
+ # Filter for person users only and convert to NotionUser objects
67
37
  notion_users = []
68
38
  for user_response in user_responses:
39
+ # Skip bot users and integrations
40
+ if user_response.type != "person":
41
+ self.logger.debug(
42
+ "Skipping non-person user %s (type: %s)",
43
+ user_response.id,
44
+ user_response.type,
45
+ )
46
+ continue
47
+
69
48
  try:
70
49
  # Use the internal creation method to convert response to NotionUser
71
- notion_user = NotionUser.from_notion_user_response(
50
+ notion_user = NotionUser.from_user_response(
72
51
  user_response, self.client.token
73
52
  )
74
53
  notion_users.append(notion_user)
75
54
  except Exception as e:
76
55
  self.logger.warning(
77
- "Failed to convert user %s to NotionUser: %s",
56
+ "Failed to convert person user %s to NotionUser: %s",
78
57
  user_response.id,
79
58
  str(e),
80
59
  )
81
60
  continue
82
61
 
83
62
  self.logger.info(
84
- "Successfully converted %d users to NotionUser objects",
63
+ "Successfully converted %d person users to NotionUser objects (skipped %d non-person users)",
85
64
  len(notion_users),
65
+ len(user_responses) - len(notion_users),
86
66
  )
87
67
  return notion_users
88
68
 
@@ -90,68 +70,16 @@ class NotionUserManager(LoggingMixin):
90
70
  self.logger.error("Error getting all users: %s", str(e))
91
71
  return []
92
72
 
93
- async def get_users_by_type(self, user_type: str = "person") -> List[NotionUser]:
94
- """
95
- Get all users of a specific type (person or bot).
96
- """
97
- try:
98
- all_users = await self.get_all_users()
99
- filtered_users = [user for user in all_users if user.user_type == user_type]
100
-
101
- self.logger.info(
102
- "Found %d users of type '%s' out of %d total users",
103
- len(filtered_users),
104
- user_type,
105
- len(all_users),
106
- )
107
- return filtered_users
108
-
109
- except Exception as e:
110
- self.logger.error("Error filtering users by type: %s", str(e))
111
- return []
112
-
113
- # TODO: Type this
114
- async def get_workspace_info(self) -> Optional[Dict[str, Any]]:
115
- """
116
- Get available workspace information from the bot user.
117
- """
118
- bot_user = await self.get_current_bot_user()
119
- if bot_user is None:
120
- self.logger.error("Failed to get bot user for workspace info")
121
- return None
122
-
123
- workspace_info = {
124
- "workspace_name": bot_user.workspace_name,
125
- "bot_user_id": bot_user.id,
126
- "bot_user_name": bot_user.name,
127
- "bot_user_type": bot_user.user_type,
128
- }
129
-
130
- # Add workspace limits if available
131
- if bot_user.is_bot:
132
- limits = await bot_user.get_workspace_limits()
133
- if limits:
134
- workspace_info["workspace_limits"] = limits
135
-
136
- # Add user count statistics
137
- try:
138
- all_users = await self.get_all_users()
139
- workspace_info["total_users"] = len(all_users)
140
- workspace_info["person_users"] = len([u for u in all_users if u.is_person])
141
- workspace_info["bot_users"] = len([u for u in all_users if u.is_bot])
142
- except Exception as e:
143
- self.logger.warning("Could not get user statistics: %s", str(e))
144
-
145
- return workspace_info
146
-
147
- async def find_users_by_name(self, name_pattern: str) -> List[NotionUser]:
73
+ async def find_users_by_name(self, name_pattern: str) -> list[NotionUser]:
148
74
  """
149
- Find users by name pattern (case-insensitive partial match).
75
+ Find person users by name pattern (case-insensitive partial match).
76
+ Only returns person users, excludes bots and integrations.
150
77
 
151
78
  Note: The API doesn't support server-side filtering, so this fetches all users
152
79
  and filters client-side.
153
80
  """
154
81
  try:
82
+ # get_all_users() already filters for person users only
155
83
  all_users = await self.get_all_users()
156
84
  pattern_lower = name_pattern.lower()
157
85
 
@@ -162,7 +90,7 @@ class NotionUserManager(LoggingMixin):
162
90
  ]
163
91
 
164
92
  self.logger.info(
165
- "Found %d users matching pattern '%s'",
93
+ "Found %d person users matching pattern '%s'",
166
94
  len(matching_users),
167
95
  name_pattern,
168
96
  )
@@ -1,13 +1,14 @@
1
+ from .factory_only import factory_only
1
2
  from .logging_mixin import LoggingMixin
3
+ from .page_id_utils import extract_uuid, format_uuid
2
4
  from .singleton import singleton
3
- from .page_id_utils import format_uuid
4
- from .factory_only import factory_only
5
5
  from .singleton_metaclass import SingletonMetaClass
6
6
 
7
7
  __all__ = [
8
8
  "LoggingMixin",
9
9
  "singleton",
10
10
  "format_uuid",
11
+ "extract_uuid",
11
12
  "factory_only",
12
13
  "singleton",
13
14
  "SingletonMetaClass",
notionary/util/fuzzy.py CHANGED
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
+
2
3
  import difflib
3
- from typing import List, Any, TypeVar, Callable, Optional
4
4
  from dataclasses import dataclass
5
+ from typing import Any, Callable, List, Optional, TypeVar
5
6
 
6
7
  T = TypeVar("T")
7
8
 
@@ -1,7 +1,7 @@
1
1
  import logging
2
- from typing import Optional, ClassVar
3
-
4
2
  import os
3
+ from typing import ClassVar, Optional
4
+
5
5
  from dotenv import load_dotenv
6
6
 
7
7
  load_dotenv()
@@ -1,4 +1,4 @@
1
- from typing import TypeVar, Dict, Any, Type, cast
1
+ from typing import Any, Dict, Type, TypeVar, cast
2
2
 
3
3
  T = TypeVar("T")
4
4
 
notionary/workspace.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import asyncio
2
- from typing import Optional, List
3
- from notionary import NotionPage, NotionDatabase
2
+ from typing import Optional
3
+
4
+ from notionary import NotionDatabase, NotionPage
4
5
  from notionary.database.client import NotionDatabaseClient
5
6
  from notionary.page.client import NotionPageClient
6
7
  from notionary.user import NotionUser, NotionUserManager
@@ -23,7 +24,7 @@ class NotionWorkspace(LoggingMixin):
23
24
  self.page_client = NotionPageClient(token=token)
24
25
  self.user_manager = NotionUserManager(token=token)
25
26
 
26
- async def search_pages(self, query: str, limit=100) -> List[NotionPage]:
27
+ async def search_pages(self, query: str, limit=100) -> list[NotionPage]:
27
28
  """
28
29
  Search for pages globally across Notion workspace.
29
30
  """
@@ -34,7 +35,7 @@ class NotionWorkspace(LoggingMixin):
34
35
 
35
36
  async def search_databases(
36
37
  self, query: str, limit: int = 100
37
- ) -> List[NotionDatabase]:
38
+ ) -> list[NotionDatabase]:
38
39
  """
39
40
  Search for databases globally across the Notion workspace.
40
41
  """
@@ -57,7 +58,7 @@ class NotionWorkspace(LoggingMixin):
57
58
 
58
59
  return databases[0] if databases else None
59
60
 
60
- async def list_all_databases(self, limit: int = 100) -> List[NotionDatabase]:
61
+ async def list_all_databases(self, limit: int = 100) -> list[NotionDatabase]:
61
62
  """
62
63
  List all databases in the workspace.
63
64
  Returns a list of NotionDatabase instances.
@@ -0,0 +1,237 @@
1
+ Metadata-Version: 2.3
2
+ Name: notionary
3
+ Version: 0.2.22
4
+ Summary: Python library for programmatic Notion workspace management - databases, pages, and content with advanced Markdown support
5
+ License: MIT
6
+ Author: Mathis Arends
7
+ Author-email: mathisarends27@gmail.com
8
+ Requires-Python: >=3.9
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Requires-Dist: aiofiles (>=24.1.0,<25.0.0)
17
+ Requires-Dist: httpx (>=0.28.0)
18
+ Requires-Dist: isort (>=6.0.1,<7.0.0)
19
+ Requires-Dist: posthog (>=6.3.1,<7.0.0)
20
+ Requires-Dist: pydantic (>=2.11.4)
21
+ Requires-Dist: python-dotenv (>=1.1.0)
22
+ Project-URL: Homepage, https://github.com/mathisarends/notionary
23
+ Description-Content-Type: text/markdown
24
+
25
+ <picture>
26
+ <source media="(prefers-color-scheme: dark)" srcset="./static/notionary-dark.png">
27
+ <source media="(prefers-color-scheme: light)" srcset="./static/notionary-light.png">
28
+ <img alt="Notionary logo: dark mode shows a white logo, light mode shows a black logo." src="./static/browser-use.png" width="full">
29
+ </picture>
30
+
31
+ <h1 align="center">Notion API simplified for Python developers 🐍</h1>
32
+
33
+ <div align="center">
34
+
35
+ [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue.svg)](https://www.python.org/downloads/)
36
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
37
+ [![Documentation](https://img.shields.io/badge/docs-mathisarends.github.io-blue.svg)](https://mathisarends.github.io/notionary/)
38
+
39
+ Transform complex Notion API interactions into simple, Pythonic code. Build AI agents, automate workflows, and create dynamic content with ease.
40
+
41
+ </div>
42
+
43
+ ---
44
+
45
+ ## Why Notionary?
46
+
47
+ - **Smart Discovery**: Find pages and databases by name—no more hunting for URLs or IDs
48
+ - **Rich Markdown**: Convert extended Markdown (callouts, toggles, columns) directly into beautiful Notion blocks
49
+ - **Async-First**: Built for modern Python with full async/await support and high performance
50
+ - **AI-Ready**: Perfect foundation for AI agents that generate and manage Notion content
51
+ - **Round-Trip**: Read existing content, modify it, and write it back while preserving formatting
52
+
53
+ ---
54
+
55
+ ## Quick Start
56
+
57
+ ```bash
58
+ pip install notionary
59
+ ```
60
+
61
+ Set up your [Notion integration](https://www.notion.so/profile/integrations) and add your token:
62
+
63
+ ```bash
64
+ NOTION_SECRET=your_integration_key
65
+ ```
66
+
67
+ ### Simple Flow: Find → Create → Update
68
+
69
+ ```python
70
+ import asyncio
71
+ from notionary import NotionPage, NotionDatabase
72
+
73
+ async def main():
74
+ # Work with pages - find by name, no exact match needed!
75
+ page = await NotionPage.from_page_name("Meeting Notes")
76
+
77
+ # Direct Markdown - quick & intuitive
78
+ await page.append_markdown("""
79
+ ## Action Items
80
+ - Review project proposal
81
+ - Schedule team meeting
82
+ - Update documentation
83
+
84
+ [callout](Important meeting decisions require follow-up "💡")
85
+ """)
86
+
87
+ # Builder Pattern - type-safe & powerful for complex layouts
88
+ await page.append_markdown(lambda builder: (
89
+ builder
90
+ .h2("Project Status")
91
+ .callout("Project milestone reached!", "🎉")
92
+ .columns(
93
+ lambda col: (col
94
+ .h3("Completed")
95
+ .bulleted_list(["API design", "Database setup", "Authentication"])
96
+ ),
97
+ lambda col: (col
98
+ .h3("In Progress")
99
+ .bulleted_list(["Frontend UI", "Testing", "Documentation"])
100
+ )
101
+ )
102
+ .table(
103
+ headers=["Task", "Owner", "Due Date"],
104
+ rows=[
105
+ ["Launch prep", "Alice", "2024-03-15"],
106
+ ["Marketing", "Bob", "2024-03-20"]
107
+ ]
108
+ )
109
+ ))
110
+
111
+ asyncio.run(main())
112
+ ```
113
+
114
+ ### Create Rich Database Entries
115
+
116
+ ```python
117
+ # Work with databases - connect and create styled entries
118
+ db = await NotionDatabase.from_database_name("Projects")
119
+
120
+ # Create new project with full styling
121
+ project = await db.create_blank_page()
122
+ await project.set_title("New Marketing Campaign")
123
+ await project.set_emoji_icon("🚀")
124
+ await project.set_random_gradient_cover()
125
+
126
+ # Set database properties
127
+ await project.set_property_value_by_name("Status", "Planning")
128
+ await project.set_property_value_by_name("Priority", "High")
129
+ await project.set_property_value_by_name("Team Lead", "sarah@company.com")
130
+
131
+ # Add rich content to the new page
132
+ await project.replace_content(lambda builder: (
133
+ builder
134
+ .h1("Campaign Overview")
135
+ .callout("New marketing initiative targeting Q2 growth", "🎯")
136
+ .h2("Goals & Objectives")
137
+ .numbered_list([
138
+ "Increase brand awareness by 25%",
139
+ "Generate 500 qualified leads",
140
+ "Launch in 3 target markets"
141
+ ])
142
+ .h2("Budget Breakdown")
143
+ .table(
144
+ headers=["Category", "Allocated", "Spent", "Remaining"],
145
+ rows=[
146
+ ["Digital Ads", "$15,000", "$3,200", "$11,800"],
147
+ ["Content Creation", "$8,000", "$1,500", "$6,500"],
148
+ ["Events", "$12,000", "$0", "$12,000"]
149
+ ]
150
+ )
151
+ .divider()
152
+ .toggle("Technical Requirements", lambda toggle: (
153
+ toggle
154
+ .paragraph("Platform specifications and integration details.")
155
+ .bulleted_list([
156
+ "CRM integration with Salesforce",
157
+ "Analytics tracking setup",
158
+ "Landing page development"
159
+ ])
160
+ ))
161
+ ))
162
+
163
+ print(f"✅ Created styled project: {project.url}")
164
+ ```
165
+
166
+ ### Extended Markdown Syntax
167
+
168
+ Notionary supports rich formatting with callouts, toggles, multi-column layouts, tables, media embeds, and more. Use either direct markdown syntax or the type-safe builder pattern.
169
+
170
+ See the complete [Block Types documentation](https://mathisarends.github.io/notionary/blocks/) for all available formatting options and syntax examples.
171
+
172
+ ## What You Can Build
173
+
174
+ - **AI Content Generation** - Perfect for AI agents that create structured reports and documentation
175
+ - **Workflow Automation** - Update project status, sync data between databases, generate reports
176
+ - **Dynamic Documentation** - Auto-generate team docs, API references, and knowledge bases
177
+ - **Content Management** - Bulk page updates, template generation, and content migration
178
+
179
+ ## Core Features
180
+
181
+ | Feature | Description |
182
+ | -------------------- | ------------------------------------------------------ |
183
+ | **Smart Discovery** | Find pages/databases by name with fuzzy matching |
184
+ | **Rich Markdown** | Extended syntax for callouts, toggles, columns, tables |
185
+ | **Async-First** | Modern Python with full async/await support |
186
+ | **Round-Trip** | Read content as markdown, edit, and write back |
187
+ | **AI-Ready** | Generate system prompts for AI content creation |
188
+ | **All Block Types** | Support for every Notion block type |
189
+ | **Type Safety** | Full type hints for better IDE support |
190
+ | **High Performance** | Efficient batch operations and caching |
191
+
192
+ ## Examples & Documentation
193
+
194
+ Explore comprehensive guides and real-world examples:
195
+
196
+ - **[📖 Full Documentation](https://mathisarends.github.io/notionary/)** - Complete API reference and guides
197
+ - **[Getting Started](https://mathisarends.github.io/notionary/get-started/)** - Quick setup and first steps
198
+ - **[Page Management](https://mathisarends.github.io/notionary/page/)** - Work with page content and properties
199
+ - **[Database Operations](https://mathisarends.github.io/notionary/database/)** - Query and manage databases
200
+ - **[Block Types](https://mathisarends.github.io/notionary/blocks/)** - Complete formatting reference
201
+
202
+ Check out the `examples/` directory for hands-on tutorials:
203
+
204
+ ### Core Examples
205
+
206
+ - **[Page Management](examples/page_example.py)** - Create, update, and manage pages
207
+ - **[Database Operations](examples/database.py)** - Connect to and query databases
208
+ - **[Workspace Discovery](examples/workspace_discovery.py)** - Explore your workspace
209
+
210
+ ### Markdown Examples
211
+
212
+ - **[Basic Formatting](examples/markdown/basic.py)** - Text, lists, and links
213
+ - **[Callouts](examples/markdown/callout.py)** - Eye-catching information boxes
214
+ - **[Toggles](examples/markdown/toggle.py)** - Collapsible content sections
215
+ - **[Multi-Column](examples/markdown/columns.py)** - Side-by-side layouts
216
+ - **[Tables](examples/markdown/table.py)** - Structured data presentation
217
+
218
+ ## Contributing
219
+
220
+ We'd love your help making Notionary even better!
221
+
222
+ Whether it's fixing bugs, adding features, improving docs, or sharing examples - all contributions are welcome.
223
+
224
+ See our [Contributing Guide](https://mathisarends.github.io/notionary/contributing/) to get started.
225
+
226
+ ---
227
+
228
+ <div align="center">
229
+
230
+ **Ready to transform your Notion workflow?**
231
+
232
+ [📖 Read the Docs](https://mathisarends.github.io/notionary/) • [Getting Started](https://mathisarends.github.io/notionary/get-started/) • [Examples](examples/)
233
+
234
+ Built with ❤️ for the Python community
235
+
236
+ </div>
237
+