Completed
Push — master ( f43d3b...32f29f )
by Dongxin
01:09
created

test_token_tree()   B

Complexity

Conditions 3

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 3
dl 0
loc 28
rs 8.8571
1
# coding: utf-8
2
3
import os
4
import re
5
import copy
6
import mistune
7
8
root = os.path.dirname(__file__)
9
10
11
class MathBlockGrammar(mistune.BlockGrammar):
12
    block_math = re.compile("^\$\$(.*?)\$\$", re.DOTALL)
13
    latex_environment = re.compile(
14
        r"^\\begin\{([a-z]*\*?)\}(.*?)\\end\{\1\}",
15
        re.DOTALL
16
    )
17
18
19
class MathBlockLexer(mistune.BlockLexer):
20
    default_rules = ['block_math', 'latex_environment'] + \
21
        mistune.BlockLexer.default_rules
22
23
    def __init__(self, rules=None, **kwargs):
24
        if rules is None:
25
            rules = MathBlockGrammar()
26
        super(MathBlockLexer, self).__init__(rules, **kwargs)
27
28
    def parse_block_math(self, m):
29
        """Parse a $$math$$ block"""
30
        self.tokens.append({
31
            'type': 'block_math',
32
            'text': m.group(1)
33
        })
34
35
    def parse_latex_environment(self, m):
36
        self.tokens.append({
37
            'type': 'latex_environment',
38
            'name': m.group(1),
39
            'text': m.group(2)
40
        })
41
42
43
class MathInlineGrammar(mistune.InlineGrammar):
44
    math = re.compile("^\$(.+?)\$")
45
    text = re.compile(r'^[\s\S]+?(?=[\\<!\[_*`~$]|https?://| {2,}\n|$)')
46
47
48
class MathInlineLexer(mistune.InlineLexer):
49
    default_rules = ['math'] + mistune.InlineLexer.default_rules
50
51
    def __init__(self, renderer, rules=None, **kwargs):
52
        if rules is None:
53
            rules = MathInlineGrammar()
54
        super(MathInlineLexer, self).__init__(renderer, rules, **kwargs)
55
56
    def output_math(self, m):
57
        return self.renderer.inline_math(m.group(1))
58
59
60
class MarkdownWithMath(mistune.Markdown):
61
    def __init__(self, renderer, **kwargs):
62
        if 'inline' not in kwargs:
63
            kwargs['inline'] = MathInlineLexer
64
        if 'block' not in kwargs:
65
            kwargs['block'] = MathBlockLexer
66
        super(MarkdownWithMath, self).__init__(renderer, **kwargs)
67
68
    def output_block_math(self):
69
        return self.renderer.block_math(self.token['text'])
70
71
    def output_latex_environment(self):
72
        return self.renderer.latex_environment(
73
            self.token['name'], self.token['text']
74
        )
75
76
77
class MathRenderer(mistune.Renderer):
78
    def block_math(self, text):
79
        return '$$%s$$' % text
80
81
    def latex_environment(self, name, text):
82
        return r'\begin{%s}%s\end{%s}' % (name, text, name)
83
84
    def inline_math(self, text):
85
        return '$%s$' % text
86
87
88
def assert_data(filename):
89
    if filename.endswith('.md'):
90
        filename = os.path.join(root, 'fixtures', 'data', filename)
91
        with open(filename) as f:
92
            text = f.read().strip()
93
    else:
94
        text = filename
95
96
    rv = MarkdownWithMath(renderer=MathRenderer()).render(text)
97
    assert text in rv
98
99
100
def test_simple_math():
101
    assert_data("$$\na = 1 *3* 5\n$$")
102
    assert_data("$ a = 1 *3* 5 $")
103
104
105
def test_markdown2html_math():
106
    # Mathematical expressions should be passed through unaltered
107
    assert_data('math.md')
108
109
110
def test_math_paragraph():
111
    # https://github.com/ipython/ipython/issues/6724
112
    assert_data('math-paragraph.md')
113
114
115
class WikiInlineGrammar(mistune.InlineGrammar):
116
    # it would take a while for creating the right regex
117
    wiki_link = re.compile(
118
        r'\[\['                   # [[
119
        r'([\s\S]+?\|[\s\S]+?)'   # Page 2|Page 2
120
        r'\]\](?!\])'             # ]]
121
    )
122
123
124
class WikiInlineLexer(mistune.InlineLexer):
125
    default_rules = copy.copy(mistune.InlineLexer.default_rules)
126
    default_rules.insert(3, 'wiki_link')
127
128
    def __init__(self, renderer, rules=None, **kwargs):
129
        if rules is None:
130
            rules = WikiInlineGrammar()
131
132
        super(WikiInlineLexer, self).__init__(renderer, rules, **kwargs)
133
134
    def output_wiki_link(self, m):
135
        text = m.group(1)
136
        alt, link = text.split('|')
137
        return '<a href="%s">%s</a>' % (link, alt)
138
139
140
def test_custom_lexer():
141
    markdown = mistune.Markdown(inline=WikiInlineLexer)
142
    ret = markdown('[[Link Text|Wiki Link]]')
143
    assert '<a href' in ret
144
145
146
class TokenTreeRenderer(mistune.Renderer):
147
    # options is required
148
    options = {}
149
150
    def placeholder(self):
151
        return []
152
153
    def __getattribute__(self, name):
154
        """Saves the arguments to each Markdown handling method."""
155
        found = TokenTreeRenderer.__dict__.get(name)
156
        if found is not None:
157
            return object.__getattribute__(self, name)
158
159
        def fake_method(*args, **kwargs):
160
            return [(name, args, kwargs)]
161
        return fake_method
162
163
164
def test_token_tree():
165
    """Tests a Renderer that returns a list from the placeholder method."""
166
    with open(os.path.join(root, 'fixtures', 'data', 'tree.md')) as f:
167
        content = f.read()
168
169
    expected = [
170
        ('header', ([('text', ('Title here',), {})], 2, 'Title here'), {}),
171
        ('paragraph', ([('text', ('Some text.',), {})],), {}),
172
        ('paragraph',
173
         ([('text', ('In two paragraphs. And then a list.',), {})],),
174
         {}),
175
        ('list',
176
         ([('list_item', ([('text', ('foo',), {})],), {}),
177
             ('list_item',
178
              ([('text', ('bar',), {}),
179
                  ('list',
180
                   ([('list_item', ([('text', ('meep',), {})],), {}),
181
                       ('list_item', ([('text', ('stuff',), {})],), {})],
182
                    True),
183
                   {})],),
184
              {})],
185
          False),
186
         {})
187
    ]
188
189
    processor = mistune.Markdown(renderer=TokenTreeRenderer())
190
    found = processor.render(content)
191
    assert expected == found, "Expected:\n%r\n\nFound:\n%r" % (expected, found)
192