notionary 0.3.1__py3-none-any.whl → 0.4.1__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 (201) hide show
  1. notionary/__init__.py +49 -1
  2. notionary/blocks/client.py +37 -11
  3. notionary/blocks/enums.py +0 -6
  4. notionary/blocks/rich_text/markdown_rich_text_converter.py +49 -15
  5. notionary/blocks/rich_text/models.py +13 -4
  6. notionary/blocks/rich_text/name_id_resolver/data_source.py +9 -3
  7. notionary/blocks/rich_text/name_id_resolver/person.py +6 -2
  8. notionary/blocks/rich_text/rich_text_markdown_converter.py +10 -3
  9. notionary/blocks/schemas.py +33 -78
  10. notionary/comments/client.py +19 -6
  11. notionary/comments/factory.py +10 -3
  12. notionary/comments/schemas.py +10 -31
  13. notionary/comments/service.py +12 -4
  14. notionary/data_source/http/data_source_instance_client.py +59 -17
  15. notionary/data_source/properties/schemas.py +156 -115
  16. notionary/data_source/query/builder.py +67 -18
  17. notionary/data_source/query/resolver.py +16 -5
  18. notionary/data_source/query/schema.py +24 -6
  19. notionary/data_source/query/validator.py +18 -6
  20. notionary/data_source/schema/registry.py +31 -12
  21. notionary/data_source/schema/service.py +66 -20
  22. notionary/data_source/schemas.py +2 -2
  23. notionary/data_source/service.py +103 -43
  24. notionary/database/client.py +27 -9
  25. notionary/database/database_metadata_update_client.py +12 -4
  26. notionary/database/schemas.py +2 -2
  27. notionary/database/service.py +14 -9
  28. notionary/exceptions/__init__.py +20 -4
  29. notionary/exceptions/api.py +2 -2
  30. notionary/exceptions/base.py +1 -1
  31. notionary/exceptions/block_parsing.py +9 -5
  32. notionary/exceptions/data_source/builder.py +13 -7
  33. notionary/exceptions/data_source/properties.py +6 -4
  34. notionary/exceptions/file_upload.py +76 -0
  35. notionary/exceptions/properties.py +7 -5
  36. notionary/exceptions/search.py +10 -6
  37. notionary/file_upload/__init__.py +4 -0
  38. notionary/file_upload/client.py +128 -210
  39. notionary/file_upload/config/__init__.py +17 -0
  40. notionary/file_upload/config/config.py +39 -0
  41. notionary/file_upload/config/constants.py +16 -0
  42. notionary/file_upload/file/reader.py +28 -0
  43. notionary/file_upload/query/__init__.py +7 -0
  44. notionary/file_upload/query/builder.py +58 -0
  45. notionary/file_upload/query/models.py +37 -0
  46. notionary/file_upload/schemas.py +80 -0
  47. notionary/file_upload/service.py +182 -291
  48. notionary/file_upload/validation/factory.py +66 -0
  49. notionary/file_upload/validation/impl/file_name_length.py +25 -0
  50. notionary/file_upload/validation/models.py +134 -0
  51. notionary/file_upload/validation/port.py +7 -0
  52. notionary/file_upload/validation/service.py +17 -0
  53. notionary/file_upload/validation/validators/__init__.py +11 -0
  54. notionary/file_upload/validation/validators/file_exists.py +15 -0
  55. notionary/file_upload/validation/validators/file_extension.py +131 -0
  56. notionary/file_upload/validation/validators/file_name_length.py +21 -0
  57. notionary/file_upload/validation/validators/upload_limit.py +31 -0
  58. notionary/http/client.py +33 -30
  59. notionary/page/content/__init__.py +9 -0
  60. notionary/page/content/factory.py +21 -7
  61. notionary/page/content/markdown/builder.py +85 -23
  62. notionary/page/content/markdown/nodes/audio.py +8 -4
  63. notionary/page/content/markdown/nodes/base.py +3 -3
  64. notionary/page/content/markdown/nodes/bookmark.py +5 -3
  65. notionary/page/content/markdown/nodes/breadcrumb.py +2 -2
  66. notionary/page/content/markdown/nodes/bulleted_list.py +5 -3
  67. notionary/page/content/markdown/nodes/callout.py +2 -2
  68. notionary/page/content/markdown/nodes/code.py +5 -3
  69. notionary/page/content/markdown/nodes/columns.py +3 -3
  70. notionary/page/content/markdown/nodes/container.py +9 -5
  71. notionary/page/content/markdown/nodes/divider.py +2 -2
  72. notionary/page/content/markdown/nodes/embed.py +8 -4
  73. notionary/page/content/markdown/nodes/equation.py +4 -2
  74. notionary/page/content/markdown/nodes/file.py +8 -4
  75. notionary/page/content/markdown/nodes/heading.py +2 -2
  76. notionary/page/content/markdown/nodes/image.py +8 -4
  77. notionary/page/content/markdown/nodes/mixins/caption.py +5 -3
  78. notionary/page/content/markdown/nodes/numbered_list.py +5 -3
  79. notionary/page/content/markdown/nodes/paragraph.py +4 -2
  80. notionary/page/content/markdown/nodes/pdf.py +8 -4
  81. notionary/page/content/markdown/nodes/quote.py +2 -2
  82. notionary/page/content/markdown/nodes/space.py +2 -2
  83. notionary/page/content/markdown/nodes/table.py +8 -5
  84. notionary/page/content/markdown/nodes/table_of_contents.py +2 -2
  85. notionary/page/content/markdown/nodes/todo.py +15 -7
  86. notionary/page/content/markdown/nodes/toggle.py +2 -2
  87. notionary/page/content/markdown/nodes/video.py +8 -4
  88. notionary/page/content/markdown/structured_output/__init__.py +73 -0
  89. notionary/page/content/markdown/structured_output/models.py +391 -0
  90. notionary/page/content/markdown/structured_output/service.py +211 -0
  91. notionary/page/content/parser/context.py +1 -1
  92. notionary/page/content/parser/factory.py +26 -8
  93. notionary/page/content/parser/parsers/audio.py +12 -32
  94. notionary/page/content/parser/parsers/base.py +2 -2
  95. notionary/page/content/parser/parsers/bookmark.py +2 -2
  96. notionary/page/content/parser/parsers/breadcrumb.py +2 -2
  97. notionary/page/content/parser/parsers/bulleted_list.py +19 -6
  98. notionary/page/content/parser/parsers/callout.py +15 -5
  99. notionary/page/content/parser/parsers/caption.py +9 -3
  100. notionary/page/content/parser/parsers/code.py +21 -7
  101. notionary/page/content/parser/parsers/column.py +8 -4
  102. notionary/page/content/parser/parsers/column_list.py +19 -7
  103. notionary/page/content/parser/parsers/divider.py +2 -2
  104. notionary/page/content/parser/parsers/embed.py +2 -4
  105. notionary/page/content/parser/parsers/equation.py +8 -4
  106. notionary/page/content/parser/parsers/file.py +12 -34
  107. notionary/page/content/parser/parsers/file_like_block.py +109 -0
  108. notionary/page/content/parser/parsers/heading.py +31 -10
  109. notionary/page/content/parser/parsers/image.py +12 -34
  110. notionary/page/content/parser/parsers/numbered_list.py +18 -6
  111. notionary/page/content/parser/parsers/paragraph.py +3 -1
  112. notionary/page/content/parser/parsers/pdf.py +12 -34
  113. notionary/page/content/parser/parsers/quote.py +28 -9
  114. notionary/page/content/parser/parsers/space.py +2 -2
  115. notionary/page/content/parser/parsers/table.py +31 -10
  116. notionary/page/content/parser/parsers/table_of_contents.py +7 -3
  117. notionary/page/content/parser/parsers/todo.py +15 -5
  118. notionary/page/content/parser/parsers/toggle.py +15 -5
  119. notionary/page/content/parser/parsers/video.py +12 -34
  120. notionary/page/content/parser/post_processing/handlers/rich_text_length.py +8 -2
  121. notionary/page/content/parser/post_processing/handlers/rich_text_length_truncation.py +8 -2
  122. notionary/page/content/parser/post_processing/service.py +3 -1
  123. notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +21 -7
  124. notionary/page/content/parser/pre_processsing/handlers/indentation.py +11 -4
  125. notionary/page/content/parser/pre_processsing/handlers/video_syntax.py +13 -6
  126. notionary/page/content/parser/service.py +4 -1
  127. notionary/page/content/renderer/context.py +15 -5
  128. notionary/page/content/renderer/factory.py +12 -6
  129. notionary/page/content/renderer/post_processing/handlers/numbered_list.py +19 -9
  130. notionary/page/content/renderer/renderers/audio.py +20 -23
  131. notionary/page/content/renderer/renderers/base.py +3 -3
  132. notionary/page/content/renderer/renderers/bookmark.py +3 -1
  133. notionary/page/content/renderer/renderers/bulleted_list.py +11 -5
  134. notionary/page/content/renderer/renderers/callout.py +19 -7
  135. notionary/page/content/renderer/renderers/captioned_block.py +11 -5
  136. notionary/page/content/renderer/renderers/code.py +6 -2
  137. notionary/page/content/renderer/renderers/column.py +3 -1
  138. notionary/page/content/renderer/renderers/column_list.py +3 -1
  139. notionary/page/content/renderer/renderers/embed.py +3 -1
  140. notionary/page/content/renderer/renderers/equation.py +3 -1
  141. notionary/page/content/renderer/renderers/file.py +20 -23
  142. notionary/page/content/renderer/renderers/file_like_block.py +47 -0
  143. notionary/page/content/renderer/renderers/heading.py +22 -8
  144. notionary/page/content/renderer/renderers/image.py +20 -23
  145. notionary/page/content/renderer/renderers/numbered_list.py +8 -3
  146. notionary/page/content/renderer/renderers/paragraph.py +12 -4
  147. notionary/page/content/renderer/renderers/pdf.py +20 -23
  148. notionary/page/content/renderer/renderers/quote.py +14 -6
  149. notionary/page/content/renderer/renderers/table.py +15 -5
  150. notionary/page/content/renderer/renderers/todo.py +16 -6
  151. notionary/page/content/renderer/renderers/toggle.py +8 -4
  152. notionary/page/content/renderer/renderers/video.py +20 -23
  153. notionary/page/content/renderer/service.py +9 -3
  154. notionary/page/content/service.py +21 -7
  155. notionary/page/content/syntax/definition/__init__.py +11 -0
  156. notionary/page/content/syntax/definition/models.py +57 -0
  157. notionary/page/content/syntax/definition/registry.py +371 -0
  158. notionary/page/content/syntax/prompts/__init__.py +4 -0
  159. notionary/page/content/syntax/prompts/models.py +11 -0
  160. notionary/page/content/syntax/prompts/registry.py +703 -0
  161. notionary/page/page_metadata_update_client.py +12 -4
  162. notionary/page/properties/client.py +46 -16
  163. notionary/page/properties/factory.py +6 -2
  164. notionary/page/properties/{models.py → schemas.py} +93 -107
  165. notionary/page/properties/service.py +111 -37
  166. notionary/page/schemas.py +3 -3
  167. notionary/page/service.py +21 -7
  168. notionary/shared/entity/client.py +6 -2
  169. notionary/shared/entity/dto_parsers.py +4 -37
  170. notionary/shared/entity/entity_metadata_update_client.py +25 -5
  171. notionary/shared/entity/schemas.py +6 -6
  172. notionary/shared/entity/service.py +89 -35
  173. notionary/shared/models/file.py +36 -6
  174. notionary/shared/models/icon.py +5 -12
  175. notionary/user/base.py +6 -2
  176. notionary/user/bot.py +22 -14
  177. notionary/user/client.py +3 -1
  178. notionary/user/person.py +3 -1
  179. notionary/user/schemas.py +3 -1
  180. notionary/user/service.py +6 -2
  181. notionary/utils/decorators.py +13 -9
  182. notionary/utils/fuzzy.py +6 -2
  183. notionary/utils/mixins/logging.py +3 -1
  184. notionary/utils/pagination.py +14 -4
  185. notionary/workspace/__init__.py +6 -2
  186. notionary/workspace/query/__init__.py +2 -1
  187. notionary/workspace/query/service.py +42 -13
  188. notionary/workspace/service.py +74 -46
  189. {notionary-0.3.1.dist-info → notionary-0.4.1.dist-info}/METADATA +1 -1
  190. notionary-0.4.1.dist-info/RECORD +236 -0
  191. notionary/file_upload/models.py +0 -69
  192. notionary/page/blocks/client.py +0 -1
  193. notionary/page/content/syntax/__init__.py +0 -4
  194. notionary/page/content/syntax/models.py +0 -66
  195. notionary/page/content/syntax/registry.py +0 -393
  196. notionary/page/page_context.py +0 -50
  197. notionary/shared/models/cover.py +0 -20
  198. notionary-0.3.1.dist-info/RECORD +0 -211
  199. /notionary/page/content/syntax/{grammar.py → definition/grammar.py} +0 -0
  200. {notionary-0.3.1.dist-info → notionary-0.4.1.dist-info}/WHEEL +0 -0
  201. {notionary-0.3.1.dist-info → notionary-0.4.1.dist-info}/licenses/LICENSE +0 -0
@@ -4,12 +4,12 @@ from abc import ABC, abstractmethod
4
4
 
5
5
  from notionary.blocks.schemas import Block
6
6
  from notionary.page.content.renderer.context import MarkdownRenderingContext
7
- from notionary.page.content.syntax import SyntaxRegistry
7
+ from notionary.page.content.syntax.definition import SyntaxDefinitionRegistry
8
8
 
9
9
 
10
10
  class BlockRenderer(ABC):
11
- def __init__(self, syntax_registry: SyntaxRegistry | None = None) -> None:
12
- self._syntax_registry = syntax_registry or SyntaxRegistry()
11
+ def __init__(self, syntax_registry: SyntaxDefinitionRegistry | None = None) -> None:
12
+ self._syntax_registry = syntax_registry or SyntaxDefinitionRegistry()
13
13
  self._next_handler: BlockRenderer | None = None
14
14
 
15
15
  def set_next(self, handler: BlockRenderer) -> BlockRenderer:
@@ -1,7 +1,9 @@
1
1
  from typing import override
2
2
 
3
3
  from notionary.blocks.schemas import Block, BlockType
4
- from notionary.page.content.renderer.renderers.captioned_block import CaptionedBlockRenderer
4
+ from notionary.page.content.renderer.renderers.captioned_block import (
5
+ CaptionedBlockRenderer,
6
+ )
5
7
 
6
8
 
7
9
  class BookmarkRenderer(CaptionedBlockRenderer):
@@ -1,20 +1,24 @@
1
1
  from typing import override
2
2
 
3
- from notionary.blocks.rich_text.rich_text_markdown_converter import RichTextToMarkdownConverter
3
+ from notionary.blocks.rich_text.rich_text_markdown_converter import (
4
+ RichTextToMarkdownConverter,
5
+ )
4
6
  from notionary.blocks.schemas import Block, BlockType
5
7
  from notionary.page.content.renderer.context import MarkdownRenderingContext
6
8
  from notionary.page.content.renderer.renderers.base import BlockRenderer
7
- from notionary.page.content.syntax import SyntaxRegistry
9
+ from notionary.page.content.syntax.definition import SyntaxDefinitionRegistry
8
10
 
9
11
 
10
12
  class BulletedListRenderer(BlockRenderer):
11
13
  def __init__(
12
14
  self,
13
- syntax_registry: SyntaxRegistry | None = None,
15
+ syntax_registry: SyntaxDefinitionRegistry | None = None,
14
16
  rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
15
17
  ) -> None:
16
18
  super().__init__(syntax_registry=syntax_registry)
17
- self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
19
+ self._rich_text_markdown_converter = (
20
+ rich_text_markdown_converter or RichTextToMarkdownConverter()
21
+ )
18
22
 
19
23
  @override
20
24
  def _can_handle(self, block: Block) -> bool:
@@ -45,4 +49,6 @@ class BulletedListRenderer(BlockRenderer):
45
49
  if not block.bulleted_list_item or not block.bulleted_list_item.rich_text:
46
50
  return None
47
51
 
48
- return await self._rich_text_markdown_converter.to_markdown(block.bulleted_list_item.rich_text)
52
+ return await self._rich_text_markdown_converter.to_markdown(
53
+ block.bulleted_list_item.rich_text
54
+ )
@@ -1,20 +1,24 @@
1
1
  from typing import override
2
2
 
3
- from notionary.blocks.rich_text.rich_text_markdown_converter import RichTextToMarkdownConverter
3
+ from notionary.blocks.rich_text.rich_text_markdown_converter import (
4
+ RichTextToMarkdownConverter,
5
+ )
4
6
  from notionary.blocks.schemas import Block, BlockType
5
7
  from notionary.page.content.renderer.context import MarkdownRenderingContext
6
8
  from notionary.page.content.renderer.renderers.base import BlockRenderer
7
- from notionary.page.content.syntax import SyntaxRegistry
9
+ from notionary.page.content.syntax.definition import SyntaxDefinitionRegistry
8
10
 
9
11
 
10
12
  class CalloutRenderer(BlockRenderer):
11
13
  def __init__(
12
14
  self,
13
- syntax_registry: SyntaxRegistry | None = None,
15
+ syntax_registry: SyntaxDefinitionRegistry | None = None,
14
16
  rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
15
17
  ) -> None:
16
18
  super().__init__(syntax_registry=syntax_registry)
17
- self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
19
+ self._rich_text_markdown_converter = (
20
+ rich_text_markdown_converter or RichTextToMarkdownConverter()
21
+ )
18
22
 
19
23
  @override
20
24
  def _can_handle(self, block: Block) -> bool:
@@ -30,9 +34,15 @@ class CalloutRenderer(BlockRenderer):
30
34
 
31
35
  icon = await self._extract_callout_icon(context.block)
32
36
 
33
- callout_start_delimiter = self._syntax_registry.get_callout_syntax().start_delimiter
37
+ callout_start_delimiter = (
38
+ self._syntax_registry.get_callout_syntax().start_delimiter
39
+ )
34
40
 
35
- result = f'{callout_start_delimiter}({content} "{icon}")' if icon else f"{callout_start_delimiter}({content})"
41
+ result = (
42
+ f'{callout_start_delimiter}({content} "{icon}")'
43
+ if icon
44
+ else f"{callout_start_delimiter}({content})"
45
+ )
36
46
 
37
47
  if context.indent_level > 0:
38
48
  result = context.indent_text(result)
@@ -47,4 +57,6 @@ class CalloutRenderer(BlockRenderer):
47
57
  async def _extract_callout_content(self, block: Block) -> str:
48
58
  if not block.callout or not block.callout.rich_text:
49
59
  return ""
50
- return await self._rich_text_markdown_converter.to_markdown(block.callout.rich_text)
60
+ return await self._rich_text_markdown_converter.to_markdown(
61
+ block.callout.rich_text
62
+ )
@@ -1,21 +1,25 @@
1
1
  from abc import abstractmethod
2
2
  from typing import override
3
3
 
4
- from notionary.blocks.rich_text.rich_text_markdown_converter import RichTextToMarkdownConverter
4
+ from notionary.blocks.rich_text.rich_text_markdown_converter import (
5
+ RichTextToMarkdownConverter,
6
+ )
5
7
  from notionary.blocks.schemas import Block
6
8
  from notionary.page.content.renderer.context import MarkdownRenderingContext
7
9
  from notionary.page.content.renderer.renderers.base import BlockRenderer
8
- from notionary.page.content.syntax import SyntaxRegistry
10
+ from notionary.page.content.syntax.definition import SyntaxDefinitionRegistry
9
11
 
10
12
 
11
13
  class CaptionedBlockRenderer(BlockRenderer):
12
14
  def __init__(
13
15
  self,
14
- syntax_registry: SyntaxRegistry | None = None,
16
+ syntax_registry: SyntaxDefinitionRegistry | None = None,
15
17
  rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
16
18
  ) -> None:
17
19
  super().__init__(syntax_registry=syntax_registry)
18
- self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
20
+ self._rich_text_markdown_converter = (
21
+ rich_text_markdown_converter or RichTextToMarkdownConverter()
22
+ )
19
23
 
20
24
  @abstractmethod
21
25
  async def _render_main_content(self, block: Block) -> str:
@@ -53,6 +57,8 @@ class CaptionedBlockRenderer(BlockRenderer):
53
57
  if not caption_rich_text:
54
58
  return ""
55
59
 
56
- caption_markdown = await self._rich_text_markdown_converter.to_markdown(caption_rich_text)
60
+ caption_markdown = await self._rich_text_markdown_converter.to_markdown(
61
+ caption_rich_text
62
+ )
57
63
 
58
64
  return f"\n[caption] {caption_markdown}"
@@ -2,7 +2,9 @@ from typing import override
2
2
 
3
3
  from notionary.blocks.enums import BlockType
4
4
  from notionary.blocks.schemas import Block
5
- from notionary.page.content.renderer.renderers.captioned_block import CaptionedBlockRenderer
5
+ from notionary.page.content.renderer.renderers.captioned_block import (
6
+ CaptionedBlockRenderer,
7
+ )
6
8
 
7
9
 
8
10
  class CodeRenderer(CaptionedBlockRenderer):
@@ -31,4 +33,6 @@ class CodeRenderer(CaptionedBlockRenderer):
31
33
  async def _extract_code_content(self, block: Block) -> str:
32
34
  if not block.code or not block.code.rich_text:
33
35
  return ""
34
- return await self._rich_text_markdown_converter.to_markdown(block.code.rich_text)
36
+ return await self._rich_text_markdown_converter.to_markdown(
37
+ block.code.rich_text
38
+ )
@@ -42,7 +42,9 @@ class ColumnRenderer(BlockRenderer):
42
42
 
43
43
  return delimiter
44
44
 
45
- async def _render_children_with_indentation(self, context: MarkdownRenderingContext) -> str:
45
+ async def _render_children_with_indentation(
46
+ self, context: MarkdownRenderingContext
47
+ ) -> str:
46
48
  original_indent = context.indent_level
47
49
  context.indent_level += 1
48
50
 
@@ -33,7 +33,9 @@ class ColumnListRenderer(BlockRenderer):
33
33
  def _get_column_list_delimiter(self) -> str:
34
34
  return self._syntax_registry.get_column_list_syntax().start_delimiter
35
35
 
36
- async def _render_children_with_indentation(self, context: MarkdownRenderingContext) -> str:
36
+ async def _render_children_with_indentation(
37
+ self, context: MarkdownRenderingContext
38
+ ) -> str:
37
39
  original_indent = context.indent_level
38
40
  context.indent_level += 1
39
41
 
@@ -1,7 +1,9 @@
1
1
  from typing import override
2
2
 
3
3
  from notionary.blocks.schemas import Block, BlockType
4
- from notionary.page.content.renderer.renderers.captioned_block import CaptionedBlockRenderer
4
+ from notionary.page.content.renderer.renderers.captioned_block import (
5
+ CaptionedBlockRenderer,
6
+ )
5
7
 
6
8
 
7
9
  class EmbedRenderer(CaptionedBlockRenderer):
@@ -19,7 +19,9 @@ class EquationRenderer(BlockRenderer):
19
19
  return
20
20
 
21
21
  syntax = self._syntax_registry.get_equation_syntax()
22
- equation_markdown = f"{syntax.start_delimiter}{expression}{syntax.end_delimiter}"
22
+ equation_markdown = (
23
+ f"{syntax.start_delimiter}{expression}{syntax.end_delimiter}"
24
+ )
23
25
 
24
26
  if context.indent_level > 0:
25
27
  equation_markdown = context.indent_text(equation_markdown)
@@ -1,34 +1,31 @@
1
1
  from typing import override
2
2
 
3
- from notionary.blocks.schemas import Block, BlockType
4
- from notionary.page.content.renderer.renderers.captioned_block import CaptionedBlockRenderer
5
-
6
-
7
- class FileRenderer(CaptionedBlockRenderer):
3
+ from notionary.blocks.schemas import (
4
+ Block,
5
+ BlockType,
6
+ ExternalFileWithCaption,
7
+ NotionHostedFileWithCaption,
8
+ )
9
+ from notionary.page.content.renderer.renderers.file_like_block import (
10
+ FileLikeBlockRenderer,
11
+ )
12
+ from notionary.page.content.syntax.definition import EnclosedSyntaxDefinition
13
+
14
+
15
+ class FileRenderer(FileLikeBlockRenderer):
8
16
  @override
9
17
  def _can_handle(self, block: Block) -> bool:
10
18
  return block.type == BlockType.FILE
11
19
 
12
20
  @override
13
- async def _render_main_content(self, block: Block) -> str:
14
- url = self._extract_file_url(block)
15
-
16
- if not url:
17
- return ""
18
-
19
- syntax = self._syntax_registry.get_file_syntax()
20
- return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
21
+ def _get_syntax(self) -> EnclosedSyntaxDefinition:
22
+ return self._syntax_registry.get_file_syntax()
21
23
 
22
- def _extract_file_url(self, block: Block) -> str:
23
- if not block.file:
24
- return ""
25
-
26
- if block.file.external:
27
- return block.file.external.url or ""
28
- elif block.file.file:
29
- return block.file.file.url or ""
30
-
31
- return ""
24
+ @override
25
+ def _get_file_data(
26
+ self, block: Block
27
+ ) -> ExternalFileWithCaption | NotionHostedFileWithCaption | None:
28
+ return block.file
32
29
 
33
30
  def _extract_file_name(self, block: Block) -> str:
34
31
  if not block.file:
@@ -0,0 +1,47 @@
1
+ from abc import abstractmethod
2
+ from typing import override
3
+
4
+ from notionary.blocks.schemas import (
5
+ Block,
6
+ ExternalFileWithCaption,
7
+ NotionHostedFileWithCaption,
8
+ )
9
+ from notionary.page.content.renderer.renderers.captioned_block import (
10
+ CaptionedBlockRenderer,
11
+ )
12
+ from notionary.page.content.syntax.definition import EnclosedSyntaxDefinition
13
+
14
+
15
+ class FileLikeBlockRenderer(CaptionedBlockRenderer):
16
+ @abstractmethod
17
+ def _get_syntax(self) -> EnclosedSyntaxDefinition:
18
+ pass
19
+
20
+ @abstractmethod
21
+ def _get_file_data(
22
+ self, block: Block
23
+ ) -> ExternalFileWithCaption | NotionHostedFileWithCaption | None:
24
+ pass
25
+
26
+ @override
27
+ async def _render_main_content(self, block: Block) -> str:
28
+ url = self._extract_url(block)
29
+
30
+ if not url:
31
+ return ""
32
+
33
+ syntax = self._get_syntax()
34
+ return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
35
+
36
+ def _extract_url(self, block: Block) -> str:
37
+ file_data = self._get_file_data(block)
38
+
39
+ if not file_data:
40
+ return ""
41
+
42
+ if isinstance(file_data, ExternalFileWithCaption):
43
+ return file_data.external.url or ""
44
+ elif isinstance(file_data, NotionHostedFileWithCaption):
45
+ return file_data.file.url or ""
46
+
47
+ return ""
@@ -1,10 +1,12 @@
1
1
  from typing import override
2
2
 
3
- from notionary.blocks.rich_text.rich_text_markdown_converter import RichTextToMarkdownConverter
3
+ from notionary.blocks.rich_text.rich_text_markdown_converter import (
4
+ RichTextToMarkdownConverter,
5
+ )
4
6
  from notionary.blocks.schemas import Block, BlockType, HeadingData
5
7
  from notionary.page.content.renderer.context import MarkdownRenderingContext
6
8
  from notionary.page.content.renderer.renderers.base import BlockRenderer
7
- from notionary.page.content.syntax import SyntaxRegistry
9
+ from notionary.page.content.syntax.definition import SyntaxDefinitionRegistry
8
10
 
9
11
 
10
12
  class HeadingRenderer(BlockRenderer):
@@ -13,16 +15,22 @@ class HeadingRenderer(BlockRenderer):
13
15
 
14
16
  def __init__(
15
17
  self,
16
- syntax_registry: SyntaxRegistry | None = None,
18
+ syntax_registry: SyntaxDefinitionRegistry | None = None,
17
19
  rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
18
20
  ) -> None:
19
21
  super().__init__(syntax_registry=syntax_registry)
20
22
  self._syntax = self._syntax_registry.get_heading_syntax()
21
- self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
23
+ self._rich_text_markdown_converter = (
24
+ rich_text_markdown_converter or RichTextToMarkdownConverter()
25
+ )
22
26
 
23
27
  @override
24
28
  def _can_handle(self, block: Block) -> bool:
25
- return block.type in (BlockType.HEADING_1, BlockType.HEADING_2, BlockType.HEADING_3)
29
+ return block.type in (
30
+ BlockType.HEADING_1,
31
+ BlockType.HEADING_2,
32
+ BlockType.HEADING_3,
33
+ )
26
34
 
27
35
  @override
28
36
  async def _process(self, context: MarkdownRenderingContext) -> None:
@@ -35,7 +43,9 @@ class HeadingRenderer(BlockRenderer):
35
43
  heading_markdown = self._format_heading(level, title, context.indent_level)
36
44
 
37
45
  if self._is_toggleable(context.block):
38
- context.markdown_result = await self._render_toggleable_heading(heading_markdown, context)
46
+ context.markdown_result = await self._render_toggleable_heading(
47
+ heading_markdown, context
48
+ )
39
49
  else:
40
50
  context.markdown_result = heading_markdown
41
51
 
@@ -52,7 +62,9 @@ class HeadingRenderer(BlockRenderer):
52
62
 
53
63
  return heading_markdown
54
64
 
55
- async def _render_toggleable_heading(self, heading_markdown: str, context: MarkdownRenderingContext) -> str:
65
+ async def _render_toggleable_heading(
66
+ self, heading_markdown: str, context: MarkdownRenderingContext
67
+ ) -> str:
56
68
  original_indent = context.indent_level
57
69
  context.indent_level += 1
58
70
 
@@ -83,7 +95,9 @@ class HeadingRenderer(BlockRenderer):
83
95
  if not heading_data or not heading_data.rich_text:
84
96
  return ""
85
97
 
86
- return await self._rich_text_markdown_converter.to_markdown(heading_data.rich_text)
98
+ return await self._rich_text_markdown_converter.to_markdown(
99
+ heading_data.rich_text
100
+ )
87
101
 
88
102
  def _get_heading_data(self, block: Block) -> HeadingData | None:
89
103
  if block.type == BlockType.HEADING_1:
@@ -1,31 +1,28 @@
1
1
  from typing import override
2
2
 
3
- from notionary.blocks.schemas import Block, BlockType
4
- from notionary.page.content.renderer.renderers.captioned_block import CaptionedBlockRenderer
5
-
6
-
7
- class ImageRenderer(CaptionedBlockRenderer):
3
+ from notionary.blocks.schemas import (
4
+ Block,
5
+ BlockType,
6
+ ExternalFileWithCaption,
7
+ NotionHostedFileWithCaption,
8
+ )
9
+ from notionary.page.content.renderer.renderers.file_like_block import (
10
+ FileLikeBlockRenderer,
11
+ )
12
+ from notionary.page.content.syntax.definition import SyntaxDefinition
13
+
14
+
15
+ class ImageRenderer(FileLikeBlockRenderer):
8
16
  @override
9
17
  def _can_handle(self, block: Block) -> bool:
10
18
  return block.type == BlockType.IMAGE
11
19
 
12
20
  @override
13
- async def _render_main_content(self, block: Block) -> str:
14
- url = self._extract_image_url(block)
15
-
16
- if not url:
17
- return ""
18
-
19
- syntax = self._syntax_registry.get_image_syntax()
20
- return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
21
+ def _get_syntax(self) -> SyntaxDefinition:
22
+ return self._syntax_registry.get_image_syntax()
21
23
 
22
- def _extract_image_url(self, block: Block) -> str:
23
- if not block.image:
24
- return ""
25
-
26
- if block.image.external:
27
- return block.image.external.url or ""
28
- elif block.image.file:
29
- return block.image.file.url or ""
30
-
31
- return ""
24
+ @override
25
+ def _get_file_data(
26
+ self, block: Block
27
+ ) -> ExternalFileWithCaption | NotionHostedFileWithCaption | None:
28
+ return block.image
@@ -6,18 +6,23 @@ from notionary.blocks.rich_text.rich_text_markdown_converter import (
6
6
  from notionary.blocks.schemas import Block, BlockType
7
7
  from notionary.page.content.renderer.context import MarkdownRenderingContext
8
8
  from notionary.page.content.renderer.renderers.base import BlockRenderer
9
- from notionary.page.content.syntax import MarkdownGrammar, SyntaxRegistry
9
+ from notionary.page.content.syntax.definition import (
10
+ MarkdownGrammar,
11
+ SyntaxDefinitionRegistry,
12
+ )
10
13
 
11
14
 
12
15
  class NumberedListRenderer(BlockRenderer):
13
16
  def __init__(
14
17
  self,
15
- syntax_registry: SyntaxRegistry | None = None,
18
+ syntax_registry: SyntaxDefinitionRegistry | None = None,
16
19
  rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
17
20
  markdown_grammar: MarkdownGrammar | None = None,
18
21
  ) -> None:
19
22
  super().__init__(syntax_registry=syntax_registry)
20
- self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
23
+ self._rich_text_markdown_converter = (
24
+ rich_text_markdown_converter or RichTextToMarkdownConverter()
25
+ )
21
26
 
22
27
  markdown_grammar = markdown_grammar or MarkdownGrammar()
23
28
  self._numbered_list_placeholder = markdown_grammar.numbered_list_placeholder
@@ -1,15 +1,21 @@
1
1
  from typing import override
2
2
 
3
- from notionary.blocks.rich_text.rich_text_markdown_converter import RichTextToMarkdownConverter
3
+ from notionary.blocks.rich_text.rich_text_markdown_converter import (
4
+ RichTextToMarkdownConverter,
5
+ )
4
6
  from notionary.blocks.schemas import Block, BlockType
5
7
  from notionary.page.content.renderer.context import MarkdownRenderingContext
6
8
  from notionary.page.content.renderer.renderers.base import BlockRenderer
7
9
 
8
10
 
9
11
  class ParagraphRenderer(BlockRenderer):
10
- def __init__(self, rich_text_markdown_converter: RichTextToMarkdownConverter | None = None) -> None:
12
+ def __init__(
13
+ self, rich_text_markdown_converter: RichTextToMarkdownConverter | None = None
14
+ ) -> None:
11
15
  super().__init__()
12
- self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
16
+ self._rich_text_markdown_converter = (
17
+ rich_text_markdown_converter or RichTextToMarkdownConverter()
18
+ )
13
19
 
14
20
  @override
15
21
  def _can_handle(self, block: Block) -> bool:
@@ -37,4 +43,6 @@ class ParagraphRenderer(BlockRenderer):
37
43
  if not block.paragraph or not block.paragraph.rich_text:
38
44
  return None
39
45
 
40
- return await self._rich_text_markdown_converter.to_markdown(block.paragraph.rich_text)
46
+ return await self._rich_text_markdown_converter.to_markdown(
47
+ block.paragraph.rich_text
48
+ )
@@ -1,31 +1,28 @@
1
1
  from typing import override
2
2
 
3
- from notionary.blocks.schemas import Block, BlockType
4
- from notionary.page.content.renderer.renderers.captioned_block import CaptionedBlockRenderer
5
-
6
-
7
- class PdfRenderer(CaptionedBlockRenderer):
3
+ from notionary.blocks.schemas import (
4
+ Block,
5
+ BlockType,
6
+ ExternalFileWithCaption,
7
+ NotionHostedFileWithCaption,
8
+ )
9
+ from notionary.page.content.renderer.renderers.file_like_block import (
10
+ FileLikeBlockRenderer,
11
+ )
12
+ from notionary.page.content.syntax.definition import EnclosedSyntaxDefinition
13
+
14
+
15
+ class PdfRenderer(FileLikeBlockRenderer):
8
16
  @override
9
17
  def _can_handle(self, block: Block) -> bool:
10
18
  return block.type == BlockType.PDF
11
19
 
12
20
  @override
13
- async def _render_main_content(self, block: Block) -> str:
14
- url = self._extract_pdf_url(block)
15
-
16
- if not url:
17
- return ""
18
-
19
- syntax = self._syntax_registry.get_pdf_syntax()
20
- return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
21
+ def _get_syntax(self) -> EnclosedSyntaxDefinition:
22
+ return self._syntax_registry.get_pdf_syntax()
21
23
 
22
- def _extract_pdf_url(self, block: Block) -> str:
23
- if not block.pdf:
24
- return ""
25
-
26
- if block.pdf.external:
27
- return block.pdf.external.url or ""
28
- elif block.pdf.file:
29
- return block.pdf.file.url or ""
30
-
31
- return ""
24
+ @override
25
+ def _get_file_data(
26
+ self, block: Block
27
+ ) -> ExternalFileWithCaption | NotionHostedFileWithCaption | None:
28
+ return block.pdf
@@ -1,20 +1,24 @@
1
1
  from typing import override
2
2
 
3
- from notionary.blocks.rich_text.rich_text_markdown_converter import RichTextToMarkdownConverter
3
+ from notionary.blocks.rich_text.rich_text_markdown_converter import (
4
+ RichTextToMarkdownConverter,
5
+ )
4
6
  from notionary.blocks.schemas import Block, BlockType
5
7
  from notionary.page.content.renderer.context import MarkdownRenderingContext
6
8
  from notionary.page.content.renderer.renderers.base import BlockRenderer
7
- from notionary.page.content.syntax import SyntaxRegistry
9
+ from notionary.page.content.syntax.definition import SyntaxDefinitionRegistry
8
10
 
9
11
 
10
12
  class QuoteRenderer(BlockRenderer):
11
13
  def __init__(
12
14
  self,
13
- syntax_registry: SyntaxRegistry | None = None,
15
+ syntax_registry: SyntaxDefinitionRegistry | None = None,
14
16
  rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
15
17
  ) -> None:
16
18
  super().__init__(syntax_registry=syntax_registry)
17
- self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
19
+ self._rich_text_markdown_converter = (
20
+ rich_text_markdown_converter or RichTextToMarkdownConverter()
21
+ )
18
22
 
19
23
  @override
20
24
  def _can_handle(self, block: Block) -> bool:
@@ -30,7 +34,9 @@ class QuoteRenderer(BlockRenderer):
30
34
 
31
35
  syntax = self._syntax_registry.get_quote_syntax()
32
36
  quote_lines = markdown.split("\n")
33
- quote_markdown = "\n".join(f"{syntax.start_delimiter}{line}" for line in quote_lines)
37
+ quote_markdown = "\n".join(
38
+ f"{syntax.start_delimiter}{line}" for line in quote_lines
39
+ )
34
40
 
35
41
  if context.indent_level > 0:
36
42
  quote_markdown = context.indent_text(quote_markdown)
@@ -46,4 +52,6 @@ class QuoteRenderer(BlockRenderer):
46
52
  if not block.quote or not block.quote.rich_text:
47
53
  return None
48
54
 
49
- return await self._rich_text_markdown_converter.to_markdown(block.quote.rich_text)
55
+ return await self._rich_text_markdown_converter.to_markdown(
56
+ block.quote.rich_text
57
+ )
@@ -1,6 +1,8 @@
1
1
  from typing import override
2
2
 
3
- from notionary.blocks.rich_text.rich_text_markdown_converter import RichTextToMarkdownConverter
3
+ from notionary.blocks.rich_text.rich_text_markdown_converter import (
4
+ RichTextToMarkdownConverter,
5
+ )
4
6
  from notionary.blocks.schemas import Block, BlockType
5
7
  from notionary.page.content.renderer.context import MarkdownRenderingContext
6
8
  from notionary.page.content.renderer.renderers.base import BlockRenderer
@@ -9,9 +11,13 @@ from notionary.page.content.renderer.renderers.base import BlockRenderer
9
11
  class TableRenderer(BlockRenderer):
10
12
  MINIMUM_COLUMN_WIDTH = 3
11
13
 
12
- def __init__(self, rich_text_markdown_converter: RichTextToMarkdownConverter | None = None) -> None:
14
+ def __init__(
15
+ self, rich_text_markdown_converter: RichTextToMarkdownConverter | None = None
16
+ ) -> None:
13
17
  super().__init__()
14
- self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
18
+ self._rich_text_markdown_converter = (
19
+ rich_text_markdown_converter or RichTextToMarkdownConverter()
20
+ )
15
21
  self._table_syntax = self._syntax_registry.get_table_syntax()
16
22
 
17
23
  @override
@@ -71,10 +77,14 @@ class TableRenderer(BlockRenderer):
71
77
 
72
78
  return "\n".join(markdown_lines)
73
79
 
74
- def _normalize_row_lengths(self, rows: list[list[str]], target_length: int) -> list[list[str]]:
80
+ def _normalize_row_lengths(
81
+ self, rows: list[list[str]], target_length: int
82
+ ) -> list[list[str]]:
75
83
  return [row + [""] * (target_length - len(row)) for row in rows]
76
84
 
77
- def _calculate_column_widths(self, rows: list[list[str]], num_columns: int) -> list[int]:
85
+ def _calculate_column_widths(
86
+ self, rows: list[list[str]], num_columns: int
87
+ ) -> list[int]:
78
88
  widths = [max(len(row[i]) for row in rows) for i in range(num_columns)]
79
89
  return [max(width, self.MINIMUM_COLUMN_WIDTH) for width in widths]
80
90