File: base_renderer.py

package info (click to toggle)
python-mistletoe 1.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 864 kB
  • sloc: python: 5,649; sh: 66; makefile: 40
file content (205 lines) | stat: -rw-r--r-- 7,370 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
"""
Base class for renderers.
"""

import re
from mistletoe import block_token, span_token


class BaseRenderer(object):
    """
    Base class for renderers.

    All renderers should ...
    *   ... define all render functions specified in self.render_map;
    *   ... be a context manager (by inheriting __enter__ and __exit__);

    Custom renderers could ...
    *   ... add additional tokens into the parsing process by passing custom
        tokens to super().__init__();
    *   ... add additional render functions by appending to self.render_map;

    Usage:
        Suppose SomeRenderer inherits BaseRenderer, and fin is the input file.
        The syntax looks something like this:

            >>> from mistletoe import Document
            >>> from some_renderer import SomeRenderer
            >>> with SomeRenderer() as renderer:
            ...     rendered = renderer.render(Document(fin))

        See mistletoe.html_renderer for an implementation example.

    Naming conventions:
        *   The keys of self.render_map should exactly match the class
            name of tokens;
        *   Render function names should be of form: "render_" + the
            "snake-case" form of token's class name.

    Attributes:
        render_map (dict): maps tokens to their corresponding render functions.
        _extras (list): a list of custom tokens to be added to the
                        parsing process.
    """
    _parse_name = re.compile(r"([A-Z][a-z]+|[A-Z]+(?![a-z]))")

    def __init__(self, *extras, **kwargs):
        self.render_map = {
            'Strong':         self.render_strong,
            'Emphasis':       self.render_emphasis,
            'InlineCode':     self.render_inline_code,
            'RawText':        self.render_raw_text,
            'Strikethrough':  self.render_strikethrough,
            'Image':          self.render_image,
            'Link':           self.render_link,
            'AutoLink':       self.render_auto_link,
            'EscapeSequence': self.render_escape_sequence,
            'Heading':        self.render_heading,
            'SetextHeading':  self.render_heading,
            'Quote':          self.render_quote,
            'Paragraph':      self.render_paragraph,
            'CodeFence':      self.render_block_code,
            'BlockCode':      self.render_block_code,
            'List':           self.render_list,
            'ListItem':       self.render_list_item,
            'Table':          self.render_table,
            'TableRow':       self.render_table_row,
            'TableCell':      self.render_table_cell,
            'ThematicBreak':  self.render_thematic_break,
            'LineBreak':      self.render_line_break,
            'Document':       self.render_document,
        }
        self._extras = extras

        for token in extras:
            if issubclass(token, span_token.SpanToken):
                token_module = span_token
            else:
                token_module = block_token
            token_module.add_token(token)
            render_func = getattr(self, self._cls_to_func(token.__name__))
            self.render_map[token.__name__] = render_func

        self.footnotes = {}

    def render(self, token):
        """
        Grabs the class name from input token and finds its corresponding
        render function.

        Basically a janky way to do polymorphism.

        Arguments:
            token: whose __class__.__name__ is in self.render_map.
        """
        return self.render_map[token.__class__.__name__](token)

    def render_inner(self, token) -> str:
        """
        Recursively renders child tokens. Joins the rendered
        strings with no space in between.

        If newlines / spaces are needed between tokens, add them
        in their respective templates, or override this function
        in the renderer subclass, so that whitespace won't seem to
        appear magically for anyone reading your program.

        Arguments:
            token: a branch node who has children attribute.
        """
        return ''.join(map(self.render, token.children))

    def __enter__(self):
        """
        Make renderer classes into context managers.
        """
        return self

    def __exit__(self, exception_type, exception_val, traceback):
        """
        Make renderer classes into context managers.

        Reset block_token._token_types and span_token._token_types.
        """
        block_token.reset_tokens()
        span_token.reset_tokens()

    @classmethod
    def _cls_to_func(cls, cls_name):
        snake = '_'.join(map(str.lower, cls._parse_name.findall(cls_name)))
        return 'render_{}'.format(snake)

    @staticmethod
    def _tokens_from_module(module):
        """
        Helper method; takes a module and returns a list of all token classes
        specified in module.__all__. Useful when custom tokens are defined in a
        separate module.
        """
        return [getattr(module, name) for name in module.__all__]

    def render_raw_text(self, token) -> str:
        """
        Default render method for RawText. Simply return token.content.
        """
        return token.content

    def render_strong(self, token: span_token.Strong) -> str:
        return self.render_inner(token)

    def render_emphasis(self, token: span_token.Emphasis) -> str:
        return self.render_inner(token)

    def render_inline_code(self, token: span_token.InlineCode) -> str:
        return self.render_inner(token)

    def render_strikethrough(self, token: span_token.Strikethrough) -> str:
        return self.render_inner(token)

    def render_image(self, token: span_token.Image) -> str:
        return self.render_inner(token)

    def render_link(self, token: span_token.Link) -> str:
        return self.render_inner(token)

    def render_auto_link(self, token: span_token.AutoLink) -> str:
        return self.render_inner(token)

    def render_escape_sequence(self, token: span_token.EscapeSequence) -> str:
        return self.render_inner(token)

    def render_line_break(self, token: span_token.LineBreak) -> str:
        return self.render_inner(token)

    def render_heading(self, token: block_token.Heading) -> str:
        return self.render_inner(token)

    def render_quote(self, token: block_token.Quote) -> str:
        return self.render_inner(token)

    def render_paragraph(self, token: block_token.Paragraph) -> str:
        return self.render_inner(token)

    def render_block_code(self, token: block_token.BlockCode) -> str:
        return self.render_inner(token)

    def render_list(self, token: block_token.List) -> str:
        return self.render_inner(token)

    def render_list_item(self, token: block_token.ListItem) -> str:
        return self.render_inner(token)

    def render_table(self, token: block_token.Table) -> str:
        return self.render_inner(token)

    def render_table_cell(self, token: block_token.TableCell) -> str:
        return self.render_inner(token)

    def render_table_row(self, token: block_token.TableRow) -> str:
        return self.render_inner(token)

    def render_thematic_break(self, token: block_token.ThematicBreak) -> str:
        return self.render_inner(token)

    def render_document(self, token: block_token.Document) -> str:
        return self.render_inner(token)