quizcomp.parser.renderer.base

  1import typing
  2
  3import markdown_it.renderer
  4import markdown_it.token
  5import markdown_it.utils
  6
  7import quizcomp.parser.ast
  8import quizcomp.parser.common
  9import quizcomp.parser.style
 10
 11class QuizComposerRendererBase(markdown_it.renderer.RendererProtocol):
 12    """ The base class for renderers (objects that take tokens and output strings in a specific format. """
 13
 14    def render(self,
 15            tokens: typing.Sequence[markdown_it.token.Token],
 16            options: markdown_it.utils.OptionsDict,
 17            env: typing.MutableMapping[str, typing.Any],
 18            ) -> str:
 19        """ Render tokens to text. """
 20
 21        context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT])
 22
 23        # Work with an AST instead of tokens.
 24        ast = quizcomp.parser.ast.build(tokens)
 25
 26        return self._render_node(ast, context)
 27
 28    def clean_final(self, text: str, context: quizcomp.parser.common.RenderContext) -> str:
 29        """
 30        Last chance for cleaning before leaving the renderer.
 31        """
 32
 33        return text.strip()
 34
 35    def _render_node(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 36        """
 37        Route rendering to the method '_render_<type>(self, node, context)', e.g.: '_image'.
 38        """
 39
 40        method_name = '_' + node.type
 41        method = getattr(self, method_name, None)
 42        if (method is None):
 43            raise TypeError(f"Could not find TeX render method: '{method_name}'.")
 44
 45        return str(method(node, context))
 46
 47    def _root(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 48        """ Render the 'root' token. """
 49
 50        content = "\n\n".join([self._render_node(child, context) for child in node.children])
 51        content = self.clean_final(content, context)
 52        return content
 53
 54    def _container_block(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 55        """ Render the 'container_block' token. """
 56
 57        # Pull any style attatched to this block and put it in a copy of the context.
 58        context, _ = quizcomp.parser.common.handle_block_style(node.attributes, context)
 59        return "\n\n".join([self._render_node(child, context) for child in node.children])
 60
 61    def _paragraph(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 62        """ Render the 'paragraph' token. """
 63
 64        return "\n".join([self._render_node(child, context) for child in node.children])
 65
 66    def _inline(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 67        """ Render the 'inline' token. """
 68
 69        return ''.join([self._render_node(child, context) for child in node.children])
 70
 71    def _text(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 72        """ Render the 'text' token. """
 73
 74        raise NotImplementedError()
 75
 76    def _softbreak(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 77        """ Render the 'softbreak' token. """
 78
 79        raise NotImplementedError()
 80
 81    def _hardbreak(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 82        """ Render the 'hardbreak' token. """
 83
 84        raise NotImplementedError()
 85
 86    def _em(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 87        """ Render the 'em' token. """
 88
 89        raise NotImplementedError()
 90
 91    def _strong(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 92        """ Render the 'strong' token. """
 93
 94        raise NotImplementedError()
 95
 96    def _fence(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 97        """ Render the 'fence' token. """
 98
 99        raise NotImplementedError()
100
101    def _code_block(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
102        """
103        Render the 'code_block' token.
104
105        This token is poorly named, it is actually an indented code block.
106        Treat it like a fence with no info string.
107        """
108
109        raise NotImplementedError()
110
111    def _code_inline(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
112        """ Render the 'code_inline' token. """
113
114        raise NotImplementedError()
115
116    def _math_block(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
117        """ Render the 'math_block' token. """
118
119        raise NotImplementedError()
120
121    def _math_inline(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
122        """ Render the 'math_inline' token. """
123
124        raise NotImplementedError()
125
126    def _image(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
127        """ Render the 'image' token. """
128
129        raise NotImplementedError()
130
131    def _link(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
132        """ Render the 'link' token. """
133
134        raise NotImplementedError()
135
136    def _placeholder(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
137        """ Render the 'placeholder' token. """
138
139        raise NotImplementedError()
140
141    def _table(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
142        """ Render the 'table' token. """
143
144        raise NotImplementedError()
145
146    def _thead(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
147        """ Render the 'thead' token. """
148
149        raise NotImplementedError()
150
151    def _tbody(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
152        """ Render the 'tbody' token. """
153
154        raise NotImplementedError()
155
156    def _tr(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
157        """ Render the 'tr' token. """
158
159        raise NotImplementedError()
160
161    def _th(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
162        """ Render the 'th' token. """
163
164        raise NotImplementedError()
165
166    def _td(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
167        """ Render the 'td' token. """
168
169        raise NotImplementedError()
170
171    def _bullet_list(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
172        """ Render the 'bullet_list' token. """
173
174        raise NotImplementedError()
175
176    def _ordered_list(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
177        """ Render the 'ordered_list' token. """
178
179        raise NotImplementedError()
180
181    def _list_item(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
182        """ Render the 'list_item' token. """
183
184        raise NotImplementedError()
185
186    def _hr(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
187        """ Render the 'hr' token. """
188
189        raise NotImplementedError()
190
191    def _heading(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
192        """ Render the 'heading' token. """
193
194        raise NotImplementedError()
195
196    def _blockquote(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
197        """ Render the 'blockquote' token. """
198
199        raise NotImplementedError()
200
201    def parse_heading_level(self, node: quizcomp.parser.ast.ASTNode) -> int:
202        """ Parse the level out of the HTML tag. """
203
204        tag = node.get('tag', None)
205        if (tag is None):
206            raise ValueError("Failed to find a heading's level.")
207
208        try:
209            level = int(tag[1])
210        except Exception as ex:
211            raise ValueError(f"Failed to parse heading level from '{tag}'.") from ex
212
213        return level
class QuizComposerRendererBase(markdown_it.renderer.RendererProtocol):
 12class QuizComposerRendererBase(markdown_it.renderer.RendererProtocol):
 13    """ The base class for renderers (objects that take tokens and output strings in a specific format. """
 14
 15    def render(self,
 16            tokens: typing.Sequence[markdown_it.token.Token],
 17            options: markdown_it.utils.OptionsDict,
 18            env: typing.MutableMapping[str, typing.Any],
 19            ) -> str:
 20        """ Render tokens to text. """
 21
 22        context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT])
 23
 24        # Work with an AST instead of tokens.
 25        ast = quizcomp.parser.ast.build(tokens)
 26
 27        return self._render_node(ast, context)
 28
 29    def clean_final(self, text: str, context: quizcomp.parser.common.RenderContext) -> str:
 30        """
 31        Last chance for cleaning before leaving the renderer.
 32        """
 33
 34        return text.strip()
 35
 36    def _render_node(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 37        """
 38        Route rendering to the method '_render_<type>(self, node, context)', e.g.: '_image'.
 39        """
 40
 41        method_name = '_' + node.type
 42        method = getattr(self, method_name, None)
 43        if (method is None):
 44            raise TypeError(f"Could not find TeX render method: '{method_name}'.")
 45
 46        return str(method(node, context))
 47
 48    def _root(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 49        """ Render the 'root' token. """
 50
 51        content = "\n\n".join([self._render_node(child, context) for child in node.children])
 52        content = self.clean_final(content, context)
 53        return content
 54
 55    def _container_block(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 56        """ Render the 'container_block' token. """
 57
 58        # Pull any style attatched to this block and put it in a copy of the context.
 59        context, _ = quizcomp.parser.common.handle_block_style(node.attributes, context)
 60        return "\n\n".join([self._render_node(child, context) for child in node.children])
 61
 62    def _paragraph(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 63        """ Render the 'paragraph' token. """
 64
 65        return "\n".join([self._render_node(child, context) for child in node.children])
 66
 67    def _inline(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 68        """ Render the 'inline' token. """
 69
 70        return ''.join([self._render_node(child, context) for child in node.children])
 71
 72    def _text(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 73        """ Render the 'text' token. """
 74
 75        raise NotImplementedError()
 76
 77    def _softbreak(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 78        """ Render the 'softbreak' token. """
 79
 80        raise NotImplementedError()
 81
 82    def _hardbreak(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 83        """ Render the 'hardbreak' token. """
 84
 85        raise NotImplementedError()
 86
 87    def _em(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 88        """ Render the 'em' token. """
 89
 90        raise NotImplementedError()
 91
 92    def _strong(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 93        """ Render the 'strong' token. """
 94
 95        raise NotImplementedError()
 96
 97    def _fence(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
 98        """ Render the 'fence' token. """
 99
100        raise NotImplementedError()
101
102    def _code_block(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
103        """
104        Render the 'code_block' token.
105
106        This token is poorly named, it is actually an indented code block.
107        Treat it like a fence with no info string.
108        """
109
110        raise NotImplementedError()
111
112    def _code_inline(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
113        """ Render the 'code_inline' token. """
114
115        raise NotImplementedError()
116
117    def _math_block(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
118        """ Render the 'math_block' token. """
119
120        raise NotImplementedError()
121
122    def _math_inline(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
123        """ Render the 'math_inline' token. """
124
125        raise NotImplementedError()
126
127    def _image(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
128        """ Render the 'image' token. """
129
130        raise NotImplementedError()
131
132    def _link(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
133        """ Render the 'link' token. """
134
135        raise NotImplementedError()
136
137    def _placeholder(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
138        """ Render the 'placeholder' token. """
139
140        raise NotImplementedError()
141
142    def _table(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
143        """ Render the 'table' token. """
144
145        raise NotImplementedError()
146
147    def _thead(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
148        """ Render the 'thead' token. """
149
150        raise NotImplementedError()
151
152    def _tbody(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
153        """ Render the 'tbody' token. """
154
155        raise NotImplementedError()
156
157    def _tr(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
158        """ Render the 'tr' token. """
159
160        raise NotImplementedError()
161
162    def _th(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
163        """ Render the 'th' token. """
164
165        raise NotImplementedError()
166
167    def _td(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
168        """ Render the 'td' token. """
169
170        raise NotImplementedError()
171
172    def _bullet_list(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
173        """ Render the 'bullet_list' token. """
174
175        raise NotImplementedError()
176
177    def _ordered_list(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
178        """ Render the 'ordered_list' token. """
179
180        raise NotImplementedError()
181
182    def _list_item(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
183        """ Render the 'list_item' token. """
184
185        raise NotImplementedError()
186
187    def _hr(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
188        """ Render the 'hr' token. """
189
190        raise NotImplementedError()
191
192    def _heading(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
193        """ Render the 'heading' token. """
194
195        raise NotImplementedError()
196
197    def _blockquote(self, node: quizcomp.parser.ast.ASTNode, context: quizcomp.parser.common.RenderContext) -> str:
198        """ Render the 'blockquote' token. """
199
200        raise NotImplementedError()
201
202    def parse_heading_level(self, node: quizcomp.parser.ast.ASTNode) -> int:
203        """ Parse the level out of the HTML tag. """
204
205        tag = node.get('tag', None)
206        if (tag is None):
207            raise ValueError("Failed to find a heading's level.")
208
209        try:
210            level = int(tag[1])
211        except Exception as ex:
212            raise ValueError(f"Failed to parse heading level from '{tag}'.") from ex
213
214        return level

The base class for renderers (objects that take tokens and output strings in a specific format.

def render( self, tokens: Sequence[markdown_it.token.Token], options: markdown_it.utils.OptionsDict, env: MutableMapping[str, Any]) -> str:
15    def render(self,
16            tokens: typing.Sequence[markdown_it.token.Token],
17            options: markdown_it.utils.OptionsDict,
18            env: typing.MutableMapping[str, typing.Any],
19            ) -> str:
20        """ Render tokens to text. """
21
22        context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT])
23
24        # Work with an AST instead of tokens.
25        ast = quizcomp.parser.ast.build(tokens)
26
27        return self._render_node(ast, context)

Render tokens to text.

def clean_final(self, text: str, context: quizcomp.parser.common.RenderContext) -> str:
29    def clean_final(self, text: str, context: quizcomp.parser.common.RenderContext) -> str:
30        """
31        Last chance for cleaning before leaving the renderer.
32        """
33
34        return text.strip()

Last chance for cleaning before leaving the renderer.

def parse_heading_level(self, node: quizcomp.parser.ast.ASTNode) -> int:
202    def parse_heading_level(self, node: quizcomp.parser.ast.ASTNode) -> int:
203        """ Parse the level out of the HTML tag. """
204
205        tag = node.get('tag', None)
206        if (tag is None):
207            raise ValueError("Failed to find a heading's level.")
208
209        try:
210            level = int(tag[1])
211        except Exception as ex:
212            raise ValueError(f"Failed to parse heading level from '{tag}'.") from ex
213
214        return level

Parse the level out of the HTML tag.