##// END OF EJS Templates
Merge pull request #6028 from takluyver/markdown-mistune...
Paul Ivanov -
r17260:fc996540 merge
parent child Browse files
Show More
@@ -13,11 +13,10 b' before_install:'
13 # Pierre Carrier's PPA for PhantomJS and CasperJS
13 # Pierre Carrier's PPA for PhantomJS and CasperJS
14 - time sudo add-apt-repository -y ppa:pcarrier/ppa
14 - time sudo add-apt-repository -y ppa:pcarrier/ppa
15 - time sudo apt-get update
15 - time sudo apt-get update
16 - time sudo apt-get install pandoc casperjs nodejs libzmq3-dev
16 - time sudo apt-get install pandoc casperjs libzmq3-dev
17 # pin tornado < 4 for js tests while phantom is on super old webkit
17 # pin tornado < 4 for js tests while phantom is on super old webkit
18 - if [[ $GROUP == 'js' ]]; then pip install 'tornado<4'; fi
18 - if [[ $GROUP == 'js' ]]; then pip install 'tornado<4'; fi
19 - time pip install -f https://nipy.bic.berkeley.edu/wheelhouse/travis jinja2 sphinx pygments tornado requests mock pyzmq jsonschema jsonpointer
19 - time pip install -f https://nipy.bic.berkeley.edu/wheelhouse/travis jinja2 sphinx pygments tornado requests mock pyzmq jsonschema jsonpointer mistune
20 - time npm install -g requirejs jquery
21 install:
20 install:
22 - time python setup.py install -q
21 - time python setup.py install -q
23 script:
22 script:
@@ -1,25 +1,24 b''
1 """Markdown filters
1 """Markdown filters
2
2 This file contains a collection of utility filters for dealing with
3 This file contains a collection of utility filters for dealing with
3 markdown within Jinja templates.
4 markdown within Jinja templates.
4 """
5 """
5 #-----------------------------------------------------------------------------
6 # Copyright (c) IPython Development Team.
6 # Copyright (c) 2013, the IPython Development Team.
7 #
8 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
12
8
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
9 from __future__ import print_function
17
10
18 # Stdlib imports
11 # Stdlib imports
19 import os
12 import os
20 import subprocess
13 import subprocess
21 import warnings
22 from io import TextIOWrapper, BytesIO
14 from io import TextIOWrapper, BytesIO
15 import re
16
17 import mistune
18 from pygments import highlight
19 from pygments.lexers import get_lexer_by_name
20 from pygments.formatters import HtmlFormatter
21 from pygments.util import ClassNotFound
23
22
24 # IPython imports
23 # IPython imports
25 from IPython.nbconvert.utils.pandoc import pandoc
24 from IPython.nbconvert.utils.pandoc import pandoc
@@ -28,9 +27,7 b' from IPython.utils.process import get_output_error_code'
28 from IPython.utils.py3compat import cast_bytes
27 from IPython.utils.py3compat import cast_bytes
29 from IPython.utils.version import check_version
28 from IPython.utils.version import check_version
30
29
31 #-----------------------------------------------------------------------------
30
32 # Functions
33 #-----------------------------------------------------------------------------
34 marked = os.path.join(os.path.dirname(__file__), "marked.js")
31 marked = os.path.join(os.path.dirname(__file__), "marked.js")
35 _node = None
32 _node = None
36
33
@@ -38,6 +35,7 b' __all__ = ['
38 'markdown2html',
35 'markdown2html',
39 'markdown2html_pandoc',
36 'markdown2html_pandoc',
40 'markdown2html_marked',
37 'markdown2html_marked',
38 'markdown2html_mistune',
41 'markdown2latex',
39 'markdown2latex',
42 'markdown2rst',
40 'markdown2rst',
43 ]
41 ]
@@ -64,8 +62,96 b' def markdown2latex(source):'
64 """
62 """
65 return pandoc(source, 'markdown', 'latex')
63 return pandoc(source, 'markdown', 'latex')
66
64
67 def markdown2html(source):
65 class MathBlockGrammar(mistune.BlockGrammar):
68 """Convert a markdown string to HTML"""
66 block_math = re.compile("^\$\$(.*?)\$\$", re.DOTALL)
67 latex_environment = re.compile(r"^\\begin\{([a-z]*\*?)\}(.*?)\\end\{\1\}",
68 re.DOTALL)
69
70 class MathBlockLexer(mistune.BlockLexer):
71 default_features = ['block_math', 'latex_environment'] + mistune.BlockLexer.default_features
72
73 def __init__(self, rules=None, **kwargs):
74 if rules is None:
75 rules = MathBlockGrammar()
76 super(MathBlockLexer, self).__init__(rules, **kwargs)
77
78 def parse_block_math(self, m):
79 """Parse a $$math$$ block"""
80 self.tokens.append({
81 'type': 'block_math',
82 'text': m.group(1)
83 })
84
85 def parse_latex_environment(self, m):
86 self.tokens.append({
87 'type': 'latex_environment',
88 'name': m.group(1),
89 'text': m.group(2)
90 })
91
92 class MathInlineGrammar(mistune.InlineGrammar):
93 math = re.compile("^\$(.+?)\$")
94
95 class MathInlineLexer(mistune.InlineLexer):
96 default_features = ['math'] + mistune.InlineLexer.default_features
97
98 def __init__(self, renderer, rules=None, **kwargs):
99 if rules is None:
100 rules = MathInlineGrammar()
101 super(MathInlineLexer, self).__init__(renderer, rules, **kwargs)
102
103 def output_math(self, m):
104 return self.renderer.inline_math(m.group(1))
105
106 class MarkdownWithMath(mistune.Markdown):
107 def __init__(self, renderer, **kwargs):
108 if 'inline' not in kwargs:
109 kwargs['inline'] = MathInlineLexer(renderer, **kwargs)
110 if 'block' not in kwargs:
111 kwargs['block'] = MathBlockLexer(**kwargs)
112 super(MarkdownWithMath, self).__init__(renderer, **kwargs)
113
114 def parse_block_math(self):
115 return self.renderer.block_math(self.token['text'])
116
117 def parse_latex_environment(self):
118 return self.renderer.latex_environment(self.token['name'], self.token['text'])
119
120 class IPythonRenderer(mistune.Renderer):
121 def block_code(self, code, lang):
122 if lang:
123 try:
124 lexer = get_lexer_by_name(lang, stripall=True)
125 except ClassNotFound:
126 code = lang + '\n' + code
127 lang = None
128
129 if not lang:
130 return '\n<pre><code>%s</code></pre>\n' % \
131 mistune.escape(code)
132
133 formatter = HtmlFormatter()
134 return highlight(code, lexer, formatter)
135
136 # Pass math through unaltered - mathjax does the rendering in the browser
137 def block_math(self, text):
138 return '$$%s$$' % text
139
140 def latex_environment(self, name, text):
141 return r'\begin{%s}%s\end{%s}' % (name, text, name)
142
143 def inline_math(self, text):
144 return '$%s$' % text
145
146 def markdown2html_mistune(source):
147 """Convert a markdown string to HTML using mistune"""
148 return MarkdownWithMath(renderer=IPythonRenderer()).render(source)
149
150 def markdown2html_pandoc(source):
151 """Convert a markdown string to HTML via pandoc"""
152 return pandoc(source, 'markdown', 'html', extra_args=['--mathjax'])
153
154 def _find_nodejs():
69 global _node
155 global _node
70 if _node is None:
156 if _node is None:
71 # prefer md2html via marked if node.js >= 0.9.12 is available
157 # prefer md2html via marked if node.js >= 0.9.12 is available
@@ -73,22 +159,11 b' def markdown2html(source):'
73 _node = 'nodejs'
159 _node = 'nodejs'
74 if not _verify_node(_node):
160 if not _verify_node(_node):
75 _node = 'node'
161 _node = 'node'
76 if not _verify_node(_node):
162 return _node
77 warnings.warn( "Node.js 0.9.12 or later wasn't found.\n" +
78 "Nbconvert will try to use Pandoc instead.")
79 _node = False
80 if _node:
81 return markdown2html_marked(source)
82 else:
83 return markdown2html_pandoc(source)
84
85 def markdown2html_pandoc(source):
86 """Convert a markdown string to HTML via pandoc"""
87 return pandoc(source, 'markdown', 'html', extra_args=['--mathjax'])
88
163
89 def markdown2html_marked(source, encoding='utf-8'):
164 def markdown2html_marked(source, encoding='utf-8'):
90 """Convert a markdown string to HTML via marked"""
165 """Convert a markdown string to HTML via marked"""
91 command = [_node, marked]
166 command = [_find_nodejs(), marked]
92 try:
167 try:
93 p = subprocess.Popen(command,
168 p = subprocess.Popen(command,
94 stdin=subprocess.PIPE, stdout=subprocess.PIPE
169 stdin=subprocess.PIPE, stdout=subprocess.PIPE
@@ -102,6 +177,9 b" def markdown2html_marked(source, encoding='utf-8'):"
102 out = TextIOWrapper(BytesIO(out), encoding, 'replace').read()
177 out = TextIOWrapper(BytesIO(out), encoding, 'replace').read()
103 return out.rstrip('\n')
178 return out.rstrip('\n')
104
179
180 # The mistune renderer is the default, because it's simple to depend on it
181 markdown2html = markdown2html_mistune
182
105 def markdown2rst(source):
183 def markdown2rst(source):
106 """Convert a markdown string to ReST via pandoc.
184 """Convert a markdown string to ReST via pandoc.
107
185
@@ -1,19 +1,7 b''
1 """Tests for conversions from markdown to other formats"""
1
2
2 """
3 # Copyright (c) IPython Development Team.
3 Module with tests for Markdown
4 """
5
6 #-----------------------------------------------------------------------------
7 # Copyright (c) 2013, the IPython Development Team.
8 #
9 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
10 #
11 # The full license is in the file COPYING.txt, distributed with this software.
12 #-----------------------------------------------------------------------------
13
14 #-----------------------------------------------------------------------------
15 # Imports
16 #-----------------------------------------------------------------------------
17
5
18 from copy import copy
6 from copy import copy
19
7
@@ -24,10 +12,6 b' from ...tests.base import TestsBase'
24 from ..markdown import markdown2latex, markdown2html, markdown2rst
12 from ..markdown import markdown2latex, markdown2html, markdown2rst
25
13
26
14
27 #-----------------------------------------------------------------------------
28 # Class
29 #-----------------------------------------------------------------------------
30
31 class TestMarkdown(TestsBase):
15 class TestMarkdown(TestsBase):
32
16
33 tests = [
17 tests = [
@@ -63,13 +47,24 b' class TestMarkdown(TestsBase):'
63 for index, test in enumerate(self.tests):
47 for index, test in enumerate(self.tests):
64 self._try_markdown(markdown2latex, test, self.tokens[index])
48 self._try_markdown(markdown2latex, test, self.tokens[index])
65
49
66
67 @dec.onlyif_cmds_exist('pandoc')
68 def test_markdown2html(self):
50 def test_markdown2html(self):
69 """markdown2html test"""
51 """markdown2html test"""
70 for index, test in enumerate(self.tests):
52 for index, test in enumerate(self.tests):
71 self._try_markdown(markdown2html, test, self.tokens[index])
53 self._try_markdown(markdown2html, test, self.tokens[index])
72
54
55 def test_markdown2html_math(self):
56 # Mathematical expressions should be passed through unaltered
57 cases = [("\\begin{equation*}\n"
58 "\\left( \\sum_{k=1}^n a_k b_k \\right)^2 \\leq \\left( \\sum_{k=1}^n a_k^2 \\right) \\left( \\sum_{k=1}^n b_k^2 \\right)\n"
59 "\\end{equation*}"),
60 ("$$\n"
61 "a = 1 *3* 5\n"
62 "$$"),
63 "$ a = 1 *3* 5 $",
64 ]
65 for case in cases:
66 self.assertIn(case, markdown2html(case))
67
73
68
74 @dec.onlyif_cmds_exist('pandoc')
69 @dec.onlyif_cmds_exist('pandoc')
75 def test_markdown2rst(self):
70 def test_markdown2rst(self):
@@ -273,8 +273,8 b' extras_require = dict('
273 test = ['nose>=0.10.1'],
273 test = ['nose>=0.10.1'],
274 terminal = [],
274 terminal = [],
275 nbformat = ['jsonschema>=2.0', 'jsonpointer>=1.3'],
275 nbformat = ['jsonschema>=2.0', 'jsonpointer>=1.3'],
276 notebook = ['tornado>=3.1', 'pyzmq>=2.1.11', 'jinja2'],
276 notebook = ['tornado>=3.1', 'pyzmq>=2.1.11', 'jinja2', 'pygments', 'mistune>=0.3'],
277 nbconvert = ['pygments', 'jinja2', 'Sphinx>=0.3']
277 nbconvert = ['pygments', 'jinja2', 'mistune>=0.3']
278 )
278 )
279
279
280 if sys.version_info < (3, 3):
280 if sys.version_info < (3, 3):
General Comments 0
You need to be logged in to leave comments. Login now