##// END OF EJS Templates
Add argument parsing, and ability to convert an HTML file from command line
Anton I. Sipos -
Show More
@@ -24,6 +24,11 b' The latest development version of doctest is required. This can be installed via'
24 $ curl http://docutils.svn.sourceforge.net/viewvc/docutils/trunk/docutils/?view=tar > docutils.gz
24 $ curl http://docutils.svn.sourceforge.net/viewvc/docutils/trunk/docutils/?view=tar > docutils.gz
25 $ pip install -U docutils.gz
25 $ pip install -U docutils.gz
26
26
27 For conversion to HTML, pygments is also required
28 ::
29
30 $ pip install pygments
31
27 Running Tests
32 Running Tests
28 =============
33 =============
29 ::
34 ::
@@ -13,7 +13,7 b' called nb_figure_NN.png.'
13 import os
13 import os
14 import subprocess
14 import subprocess
15 import sys
15 import sys
16
16 import argparse
17 from IPython.nbformat import current as nbformat
17 from IPython.nbformat import current as nbformat
18 from IPython.utils.text import indent
18 from IPython.utils.text import indent
19
19
@@ -36,6 +36,7 b" def rst_directive(directive, text=''):"
36
36
37 # Converters for parts of a cell.
37 # Converters for parts of a cell.
38
38
39
39 class ConversionException(Exception):
40 class ConversionException(Exception):
40 pass
41 pass
41
42
@@ -43,9 +44,9 b' class ConversionException(Exception):'
43 class Converter(object):
44 class Converter(object):
44 default_encoding = 'utf-8'
45 default_encoding = 'utf-8'
45
46
46 def __init__(self, fname):
47 def __init__(self, infile):
47 self.fname = fname
48 self.infile = infile
48 self.dirpath = os.path.dirname(fname)
49 self.dirpath = os.path.dirname(infile)
49
50
50 @property
51 @property
51 def extension(self):
52 def extension(self):
@@ -66,25 +67,25 b' class Converter(object):'
66 return '\n'.join(lines)
67 return '\n'.join(lines)
67
68
68 def render(self):
69 def render(self):
69 "read, convert, and save self.fname"
70 "read, convert, and save self.infile"
70 self.read()
71 self.read()
71 self.output = self.convert()
72 self.output = self.convert()
72 return self.save()
73 return self.save()
73
74
74 def read(self):
75 def read(self):
75 "read and parse notebook into NotebookNode called self.nb"
76 "read and parse notebook into NotebookNode called self.nb"
76 with open(self.fname) as f:
77 with open(self.infile) as f:
77 self.nb = nbformat.read(f, 'json')
78 self.nb = nbformat.read(f, 'json')
78
79
79 def save(self, fname=None, encoding=None):
80 def save(self, infile=None, encoding=None):
80 "read and parse notebook into self.nb"
81 "read and parse notebook into self.nb"
81 if fname is None:
82 if infile is None:
82 fname = os.path.splitext(self.fname)[0] + '.' + self.extension
83 infile = os.path.splitext(self.infile)[0] + '.' + self.extension
83 if encoding is None:
84 if encoding is None:
84 encoding = self.default_encoding
85 encoding = self.default_encoding
85 with open(fname, 'w') as f:
86 with open(infile, 'w') as f:
86 f.write(self.output.encode(encoding))
87 f.write(self.output.encode(encoding))
87 return fname
88 return infile
88
89
89 def render_heading(self, cell):
90 def render_heading(self, cell):
90 raise NotImplementedError
91 raise NotImplementedError
@@ -170,13 +171,13 b' class ConverterRST(Converter):'
170 lines = []
171 lines = []
171
172
172 if 'png' in output:
173 if 'png' in output:
173 fname = 'nb_figure_%s.png' % self.figures_counter
174 infile = 'nb_figure_%s.png' % self.figures_counter
174 fullname = os.path.join(self.dirpath, fname)
175 fullname = os.path.join(self.dirpath, infile)
175 with open(fullname, 'w') as f:
176 with open(fullname, 'w') as f:
176 f.write(output.png.decode('base64'))
177 f.write(output.png.decode('base64'))
177
178
178 self.figures_counter += 1
179 self.figures_counter += 1
179 lines.append('.. image:: %s' % fname)
180 lines.append('.. image:: %s' % infile)
180 lines.append('')
181 lines.append('')
181
182
182 return lines
183 return lines
@@ -194,7 +195,7 b' class ConverterRST(Converter):'
194 return lines
195 return lines
195
196
196
197
197 def rst2simplehtml(fname):
198 def rst2simplehtml(infile):
198 """Convert a rst file to simplified html suitable for blogger.
199 """Convert a rst file to simplified html suitable for blogger.
199
200
200 This just runs rst2html with certain parameters to produce really simple
201 This just runs rst2html with certain parameters to produce really simple
@@ -211,7 +212,7 b' def rst2simplehtml(fname):'
211 "--no-toc-backlinks --no-section-numbering "
212 "--no-toc-backlinks --no-section-numbering "
212 "--strip-comments ")
213 "--strip-comments ")
213
214
214 cmd = "%s %s" % (cmd_template, fname)
215 cmd = "%s %s" % (cmd_template, infile)
215 proc = subprocess.Popen(cmd,
216 proc = subprocess.Popen(cmd,
216 stdout=subprocess.PIPE,
217 stdout=subprocess.PIPE,
217 stderr=subprocess.PIPE,
218 stderr=subprocess.PIPE,
@@ -232,7 +233,7 b' def rst2simplehtml(fname):'
232 if line.startswith('<body>'):
233 if line.startswith('<body>'):
233 break
234 break
234
235
235 newfname = os.path.splitext(fname)[0] + '.html'
236 newfname = os.path.splitext(infile)[0] + '.html'
236 with open(newfname, 'w') as f:
237 with open(newfname, 'w') as f:
237 for line in walker:
238 for line in walker:
238 if line.startswith('</body>'):
239 if line.startswith('</body>'):
@@ -243,11 +244,27 b' def rst2simplehtml(fname):'
243 return newfname
244 return newfname
244
245
245
246
246 def main(fname):
247 def main(infile, format='rst'):
247 """Convert a notebook to html in one step"""
248 """Convert a notebook to html in one step"""
248 converter = ConverterRST(fname)
249 if format == 'rst':
249 converter.render()
250 converter = ConverterRST(infile)
251 converter.render()
252 elif format == 'html':
253 #Currently, conversion to html is a 2 step process, nb->rst->html
254 converter = ConverterRST(infile)
255 rstfname = converter.render()
256 rst2simplehtml(rstfname)
250
257
251
258
252 if __name__ == '__main__':
259 if __name__ == '__main__':
253 main(sys.argv[1])
260 parser = argparse.ArgumentParser(description='nbconvert: Convert IPython notebooks to other formats')
261
262 # TODO: consider passing file like object around, rather than filenames
263 # would allow us to process stdin, or even http streams
264 #parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin)
265
266 #Require a filename as a positional argument
267 parser.add_argument('infile', nargs=1)
268 parser.add_argument('-f', '--format', default='rst')
269 args = parser.parse_args()
270 main(infile=args.infile[0], format=args.format)
@@ -13,6 +13,8 b' def clean_dir():'
13 "Remove .rst files created during conversion"
13 "Remove .rst files created during conversion"
14 map(os.remove, glob.glob("./tests/*.rst"))
14 map(os.remove, glob.glob("./tests/*.rst"))
15 map(os.remove, glob.glob("./tests/*.png"))
15 map(os.remove, glob.glob("./tests/*.png"))
16 map(os.remove, glob.glob("./tests/*.html"))
17
16
18
17 @nt.with_setup(clean_dir, clean_dir)
19 @nt.with_setup(clean_dir, clean_dir)
18 def test_simple():
20 def test_simple():
@@ -20,6 +22,7 b' def test_simple():'
20 f = c.render()
22 f = c.render()
21 nt.assert_true('rst' in f, 'changed file extension to rst')
23 nt.assert_true('rst' in f, 'changed file extension to rst')
22
24
25
23 @nt.with_setup(clean_dir, clean_dir)
26 @nt.with_setup(clean_dir, clean_dir)
24 def test_main():
27 def test_main():
25 """
28 """
@@ -28,14 +31,15 b' def test_main():'
28 main(fname)
31 main(fname)
29 nt.assert_true(os.path.exists(out_fname))
32 nt.assert_true(os.path.exists(out_fname))
30
33
34
31 def test_render_heading():
35 def test_render_heading():
32 """ Unit test for cell type "heading" """
36 """ Unit test for cell type "heading" """
33 # Generate and test heading cells level 1-6
37 # Generate and test heading cells level 1-6
34 for level in xrange(1,7):
38 for level in xrange(1, 7):
35 cell = {
39 cell = {
36 'cell_type': 'heading',
40 'cell_type': 'heading',
37 'level' : level,
41 'level' : level,
38 'source' : ['Test for heading type H{0}'.format(level)]
42 'source' : ['Test for heading type H{0}'.format(level)]
39 }
43 }
40 # Convert cell dictionaries to NotebookNode
44 # Convert cell dictionaries to NotebookNode
41 cell_nb = nbformat.NotebookNode(cell)
45 cell_nb = nbformat.NotebookNode(cell)
@@ -48,10 +52,19 b' def test_render_heading():'
48 # Render to rst
52 # Render to rst
49 c = ConverterRST('')
53 c = ConverterRST('')
50 rst_list = c.render_heading(cell_nb)
54 rst_list = c.render_heading(cell_nb)
51 nt.assert_true(isinstance(rst_list,list)) # render should return a list
55 nt.assert_true(isinstance(rst_list, list)) # render should return a list
52 rst_str = "".join(rst_list)
56 rst_str = "".join(rst_list)
53 # Confirm rst content
57 # Confirm rst content
54 heading_level = {1:'=', 2:'-', 3:'`', 4:'\'', 5:'.',6:'~'}
58 heading_level = {1: '=', 2: '-', 3: '`', 4: '\'', 5: '.', 6: '~'}
55 chk_str = "Test for heading type H{0}\n{1}\n".format(
59 chk_str = "Test for heading type H{0}\n{1}\n".format(
56 level,heading_level[level]*24)
60 level, heading_level[level] * 24)
57 nt.assert_equal(rst_str,chk_str)
61 nt.assert_equal(rst_str, chk_str)
62
63
64 @nt.with_setup(clean_dir, clean_dir)
65 def test_main_html():
66 """
67 Test main entry point
68 """
69 main(fname, format='html')
70 nt.assert_true(os.path.exists('tests/test.html'))
General Comments 0
You need to be logged in to leave comments. Login now