##// END OF EJS Templates
Merge pull request #5586 from minrk/pdf-exporter...
Thomas Kluyver -
r16421:fee8aced merge
parent child Browse files
Show More
@@ -0,0 +1,36 b''
1 """Tests for PDF export"""
2
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
5
6 import logging
7 import os
8
9 from IPython.testing import decorators as dec
10
11 from .base import ExportersTestsBase
12 from ..pdf import PDFExporter
13
14
15 #-----------------------------------------------------------------------------
16 # Class
17 #-----------------------------------------------------------------------------
18
19 class TestPDF(ExportersTestsBase):
20 """Test PDF export"""
21
22 exporter_class = PDFExporter
23
24 def test_constructor(self):
25 """Can a PDFExporter be constructed?"""
26 self.exporter_class()
27
28
29 @dec.onlyif_cmds_exist('pdflatex')
30 @dec.onlyif_cmds_exist('pandoc')
31 def test_export(self):
32 """Smoke test PDFExporter"""
33 (output, resources) = self.exporter_class(latex_count=1).from_filename(self._get_notebook())
34 self.assertIsInstance(output, bytes)
35 assert len(output) > 0
36
@@ -0,0 +1,2 b''
1 Creating PDFs with LaTeX no longer uses a post processor.
2 Use `nbconvert --to pdf` instead of `nbconvert --to latex --post pdf`.
@@ -1,140 +1,140 b''
1 import io
1 import io
2 import os
2 import os
3 import zipfile
3 import zipfile
4
4
5 from tornado import web
5 from tornado import web
6
6
7 from ..base.handlers import IPythonHandler, notebook_path_regex
7 from ..base.handlers import IPythonHandler, notebook_path_regex
8 from IPython.nbformat.current import to_notebook_json
8 from IPython.nbformat.current import to_notebook_json
9
9
10 from IPython.utils import tz
10 from IPython.utils import tz
11 from IPython.utils.py3compat import cast_bytes
11 from IPython.utils.py3compat import cast_bytes
12
12
13 import sys
13 import sys
14
14
15 def find_resource_files(output_files_dir):
15 def find_resource_files(output_files_dir):
16 files = []
16 files = []
17 for dirpath, dirnames, filenames in os.walk(output_files_dir):
17 for dirpath, dirnames, filenames in os.walk(output_files_dir):
18 files.extend([os.path.join(dirpath, f) for f in filenames])
18 files.extend([os.path.join(dirpath, f) for f in filenames])
19 return files
19 return files
20
20
21 def respond_zip(handler, name, output, resources):
21 def respond_zip(handler, name, output, resources):
22 """Zip up the output and resource files and respond with the zip file.
22 """Zip up the output and resource files and respond with the zip file.
23
23
24 Returns True if it has served a zip file, False if there are no resource
24 Returns True if it has served a zip file, False if there are no resource
25 files, in which case we serve the plain output file.
25 files, in which case we serve the plain output file.
26 """
26 """
27 # Check if we have resource files we need to zip
27 # Check if we have resource files we need to zip
28 output_files = resources.get('outputs', None)
28 output_files = resources.get('outputs', None)
29 if not output_files:
29 if not output_files:
30 return False
30 return False
31
31
32 # Headers
32 # Headers
33 zip_filename = os.path.splitext(name)[0] + '.zip'
33 zip_filename = os.path.splitext(name)[0] + '.zip'
34 handler.set_header('Content-Disposition',
34 handler.set_header('Content-Disposition',
35 'attachment; filename="%s"' % zip_filename)
35 'attachment; filename="%s"' % zip_filename)
36 handler.set_header('Content-Type', 'application/zip')
36 handler.set_header('Content-Type', 'application/zip')
37
37
38 # Prepare the zip file
38 # Prepare the zip file
39 buffer = io.BytesIO()
39 buffer = io.BytesIO()
40 zipf = zipfile.ZipFile(buffer, mode='w', compression=zipfile.ZIP_DEFLATED)
40 zipf = zipfile.ZipFile(buffer, mode='w', compression=zipfile.ZIP_DEFLATED)
41 output_filename = os.path.splitext(name)[0] + '.' + resources['output_extension']
41 output_filename = os.path.splitext(name)[0] + '.' + resources['output_extension']
42 zipf.writestr(output_filename, cast_bytes(output, 'utf-8'))
42 zipf.writestr(output_filename, cast_bytes(output, 'utf-8'))
43 for filename, data in output_files.items():
43 for filename, data in output_files.items():
44 zipf.writestr(os.path.basename(filename), data)
44 zipf.writestr(os.path.basename(filename), data)
45 zipf.close()
45 zipf.close()
46
46
47 handler.finish(buffer.getvalue())
47 handler.finish(buffer.getvalue())
48 return True
48 return True
49
49
50 def get_exporter(format, **kwargs):
50 def get_exporter(format, **kwargs):
51 """get an exporter, raising appropriate errors"""
51 """get an exporter, raising appropriate errors"""
52 # if this fails, will raise 500
52 # if this fails, will raise 500
53 try:
53 try:
54 from IPython.nbconvert.exporters.export import exporter_map
54 from IPython.nbconvert.exporters.export import exporter_map
55 except ImportError as e:
55 except ImportError as e:
56 raise web.HTTPError(500, "Could not import nbconvert: %s" % e)
56 raise web.HTTPError(500, "Could not import nbconvert: %s" % e)
57
57
58 try:
58 try:
59 Exporter = exporter_map[format]
59 Exporter = exporter_map[format]
60 except KeyError:
60 except KeyError:
61 # should this be 400?
61 # should this be 400?
62 raise web.HTTPError(404, u"No exporter for format: %s" % format)
62 raise web.HTTPError(404, u"No exporter for format: %s" % format)
63
63
64 try:
64 try:
65 return Exporter(**kwargs)
65 return Exporter(**kwargs)
66 except Exception as e:
66 except Exception as e:
67 raise web.HTTPError(500, "Could not construct Exporter: %s" % e)
67 raise web.HTTPError(500, "Could not construct Exporter: %s" % e)
68
68
69 class NbconvertFileHandler(IPythonHandler):
69 class NbconvertFileHandler(IPythonHandler):
70
70
71 SUPPORTED_METHODS = ('GET',)
71 SUPPORTED_METHODS = ('GET',)
72
72
73 @web.authenticated
73 @web.authenticated
74 def get(self, format, path='', name=None):
74 def get(self, format, path='', name=None):
75
75
76 exporter = get_exporter(format, config=self.config)
76 exporter = get_exporter(format, config=self.config, log=self.log)
77
77
78 path = path.strip('/')
78 path = path.strip('/')
79 model = self.notebook_manager.get_notebook(name=name, path=path)
79 model = self.notebook_manager.get_notebook(name=name, path=path)
80
80
81 self.set_header('Last-Modified', model['last_modified'])
81 self.set_header('Last-Modified', model['last_modified'])
82
82
83 try:
83 try:
84 output, resources = exporter.from_notebook_node(model['content'])
84 output, resources = exporter.from_notebook_node(model['content'])
85 except Exception as e:
85 except Exception as e:
86 raise web.HTTPError(500, "nbconvert failed: %s" % e)
86 raise web.HTTPError(500, "nbconvert failed: %s" % e)
87
87
88 if respond_zip(self, name, output, resources):
88 if respond_zip(self, name, output, resources):
89 return
89 return
90
90
91 # Force download if requested
91 # Force download if requested
92 if self.get_argument('download', 'false').lower() == 'true':
92 if self.get_argument('download', 'false').lower() == 'true':
93 filename = os.path.splitext(name)[0] + '.' + resources['output_extension']
93 filename = os.path.splitext(name)[0] + '.' + resources['output_extension']
94 self.set_header('Content-Disposition',
94 self.set_header('Content-Disposition',
95 'attachment; filename="%s"' % filename)
95 'attachment; filename="%s"' % filename)
96
96
97 # MIME type
97 # MIME type
98 if exporter.output_mimetype:
98 if exporter.output_mimetype:
99 self.set_header('Content-Type',
99 self.set_header('Content-Type',
100 '%s; charset=utf-8' % exporter.output_mimetype)
100 '%s; charset=utf-8' % exporter.output_mimetype)
101
101
102 self.finish(output)
102 self.finish(output)
103
103
104 class NbconvertPostHandler(IPythonHandler):
104 class NbconvertPostHandler(IPythonHandler):
105 SUPPORTED_METHODS = ('POST',)
105 SUPPORTED_METHODS = ('POST',)
106
106
107 @web.authenticated
107 @web.authenticated
108 def post(self, format):
108 def post(self, format):
109 exporter = get_exporter(format, config=self.config)
109 exporter = get_exporter(format, config=self.config)
110
110
111 model = self.get_json_body()
111 model = self.get_json_body()
112 nbnode = to_notebook_json(model['content'])
112 nbnode = to_notebook_json(model['content'])
113
113
114 try:
114 try:
115 output, resources = exporter.from_notebook_node(nbnode)
115 output, resources = exporter.from_notebook_node(nbnode)
116 except Exception as e:
116 except Exception as e:
117 raise web.HTTPError(500, "nbconvert failed: %s" % e)
117 raise web.HTTPError(500, "nbconvert failed: %s" % e)
118
118
119 if respond_zip(self, nbnode.metadata.name, output, resources):
119 if respond_zip(self, nbnode.metadata.name, output, resources):
120 return
120 return
121
121
122 # MIME type
122 # MIME type
123 if exporter.output_mimetype:
123 if exporter.output_mimetype:
124 self.set_header('Content-Type',
124 self.set_header('Content-Type',
125 '%s; charset=utf-8' % exporter.output_mimetype)
125 '%s; charset=utf-8' % exporter.output_mimetype)
126
126
127 self.finish(output)
127 self.finish(output)
128
128
129 #-----------------------------------------------------------------------------
129 #-----------------------------------------------------------------------------
130 # URL to handler mappings
130 # URL to handler mappings
131 #-----------------------------------------------------------------------------
131 #-----------------------------------------------------------------------------
132
132
133 _format_regex = r"(?P<format>\w+)"
133 _format_regex = r"(?P<format>\w+)"
134
134
135
135
136 default_handlers = [
136 default_handlers = [
137 (r"/nbconvert/%s%s" % (_format_regex, notebook_path_regex),
137 (r"/nbconvert/%s%s" % (_format_regex, notebook_path_regex),
138 NbconvertFileHandler),
138 NbconvertFileHandler),
139 (r"/nbconvert/%s" % _format_regex, NbconvertPostHandler),
139 (r"/nbconvert/%s" % _format_regex, NbconvertPostHandler),
140 ] No newline at end of file
140 ]
@@ -1,335 +1,335 b''
1 //----------------------------------------------------------------------------
1 // Copyright (c) IPython Development Team.
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Distributed under the terms of the Modified BSD License.
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
3
8 //============================================================================
4 //============================================================================
9 // MenuBar
5 // MenuBar
10 //============================================================================
6 //============================================================================
11
7
12 /**
8 /**
13 * @module IPython
9 * @module IPython
14 * @namespace IPython
10 * @namespace IPython
15 * @submodule MenuBar
11 * @submodule MenuBar
16 */
12 */
17
13
18
14
19 var IPython = (function (IPython) {
15 var IPython = (function (IPython) {
20 "use strict";
16 "use strict";
21
17
22 var utils = IPython.utils;
18 var utils = IPython.utils;
23
19
24 /**
20 /**
25 * A MenuBar Class to generate the menubar of IPython notebook
21 * A MenuBar Class to generate the menubar of IPython notebook
26 * @Class MenuBar
22 * @Class MenuBar
27 *
23 *
28 * @constructor
24 * @constructor
29 *
25 *
30 *
26 *
31 * @param selector {string} selector for the menubar element in DOM
27 * @param selector {string} selector for the menubar element in DOM
32 * @param {object} [options]
28 * @param {object} [options]
33 * @param [options.base_url] {String} String to use for the
29 * @param [options.base_url] {String} String to use for the
34 * base project url. Default is to inspect
30 * base project url. Default is to inspect
35 * $('body').data('baseUrl');
31 * $('body').data('baseUrl');
36 * does not support change for now is set through this option
32 * does not support change for now is set through this option
37 */
33 */
38 var MenuBar = function (selector, options) {
34 var MenuBar = function (selector, options) {
39 options = options || {};
35 options = options || {};
40 this.base_url = options.base_url || IPython.utils.get_body_data("baseUrl");
36 this.base_url = options.base_url || IPython.utils.get_body_data("baseUrl");
41 this.selector = selector;
37 this.selector = selector;
42 if (this.selector !== undefined) {
38 if (this.selector !== undefined) {
43 this.element = $(selector);
39 this.element = $(selector);
44 this.style();
40 this.style();
45 this.bind_events();
41 this.bind_events();
46 }
42 }
47 };
43 };
48
44
49 MenuBar.prototype.style = function () {
45 MenuBar.prototype.style = function () {
50 this.element.addClass('border-box-sizing');
46 this.element.addClass('border-box-sizing');
51 this.element.find("li").click(function (event, ui) {
47 this.element.find("li").click(function (event, ui) {
52 // The selected cell loses focus when the menu is entered, so we
48 // The selected cell loses focus when the menu is entered, so we
53 // re-select it upon selection.
49 // re-select it upon selection.
54 var i = IPython.notebook.get_selected_index();
50 var i = IPython.notebook.get_selected_index();
55 IPython.notebook.select(i);
51 IPython.notebook.select(i);
56 }
52 }
57 );
53 );
58 };
54 };
59
55
60 MenuBar.prototype._nbconvert = function (format, download) {
56 MenuBar.prototype._nbconvert = function (format, download) {
61 download = download || false;
57 download = download || false;
62 var notebook_path = IPython.notebook.notebook_path;
58 var notebook_path = IPython.notebook.notebook_path;
63 var notebook_name = IPython.notebook.notebook_name;
59 var notebook_name = IPython.notebook.notebook_name;
64 if (IPython.notebook.dirty) {
60 if (IPython.notebook.dirty) {
65 IPython.notebook.save_notebook({async : false});
61 IPython.notebook.save_notebook({async : false});
66 }
62 }
67 var url = utils.url_join_encode(
63 var url = utils.url_join_encode(
68 this.base_url,
64 this.base_url,
69 'nbconvert',
65 'nbconvert',
70 format,
66 format,
71 notebook_path,
67 notebook_path,
72 notebook_name
68 notebook_name
73 ) + "?download=" + download.toString();
69 ) + "?download=" + download.toString();
74
70
75 window.open(url);
71 window.open(url);
76 };
72 };
77
73
78 MenuBar.prototype.bind_events = function () {
74 MenuBar.prototype.bind_events = function () {
79 // File
75 // File
80 var that = this;
76 var that = this;
81 this.element.find('#new_notebook').click(function () {
77 this.element.find('#new_notebook').click(function () {
82 IPython.notebook.new_notebook();
78 IPython.notebook.new_notebook();
83 });
79 });
84 this.element.find('#open_notebook').click(function () {
80 this.element.find('#open_notebook').click(function () {
85 window.open(utils.url_join_encode(
81 window.open(utils.url_join_encode(
86 IPython.notebook.base_url,
82 IPython.notebook.base_url,
87 'tree',
83 'tree',
88 IPython.notebook.notebook_path
84 IPython.notebook.notebook_path
89 ));
85 ));
90 });
86 });
91 this.element.find('#copy_notebook').click(function () {
87 this.element.find('#copy_notebook').click(function () {
92 IPython.notebook.copy_notebook();
88 IPython.notebook.copy_notebook();
93 return false;
89 return false;
94 });
90 });
95 this.element.find('#download_ipynb').click(function () {
91 this.element.find('#download_ipynb').click(function () {
96 var base_url = IPython.notebook.base_url;
92 var base_url = IPython.notebook.base_url;
97 var notebook_path = IPython.notebook.notebook_path;
93 var notebook_path = IPython.notebook.notebook_path;
98 var notebook_name = IPython.notebook.notebook_name;
94 var notebook_name = IPython.notebook.notebook_name;
99 if (IPython.notebook.dirty) {
95 if (IPython.notebook.dirty) {
100 IPython.notebook.save_notebook({async : false});
96 IPython.notebook.save_notebook({async : false});
101 }
97 }
102
98
103 var url = utils.url_join_encode(
99 var url = utils.url_join_encode(
104 base_url,
100 base_url,
105 'files',
101 'files',
106 notebook_path,
102 notebook_path,
107 notebook_name
103 notebook_name
108 );
104 );
109 window.location.assign(url);
105 window.location.assign(url);
110 });
106 });
111
107
112 this.element.find('#print_preview').click(function () {
108 this.element.find('#print_preview').click(function () {
113 that._nbconvert('html', false);
109 that._nbconvert('html', false);
114 });
110 });
115
111
116 this.element.find('#download_py').click(function () {
112 this.element.find('#download_py').click(function () {
117 that._nbconvert('python', true);
113 that._nbconvert('python', true);
118 });
114 });
119
115
120 this.element.find('#download_html').click(function () {
116 this.element.find('#download_html').click(function () {
121 that._nbconvert('html', true);
117 that._nbconvert('html', true);
122 });
118 });
123
119
124 this.element.find('#download_rst').click(function () {
120 this.element.find('#download_rst').click(function () {
125 that._nbconvert('rst', true);
121 that._nbconvert('rst', true);
126 });
122 });
127
123
124 this.element.find('#download_pdf').click(function () {
125 that._nbconvert('pdf', true);
126 });
127
128 this.element.find('#rename_notebook').click(function () {
128 this.element.find('#rename_notebook').click(function () {
129 IPython.save_widget.rename_notebook();
129 IPython.save_widget.rename_notebook();
130 });
130 });
131 this.element.find('#save_checkpoint').click(function () {
131 this.element.find('#save_checkpoint').click(function () {
132 IPython.notebook.save_checkpoint();
132 IPython.notebook.save_checkpoint();
133 });
133 });
134 this.element.find('#restore_checkpoint').click(function () {
134 this.element.find('#restore_checkpoint').click(function () {
135 });
135 });
136 this.element.find('#trust_notebook').click(function () {
136 this.element.find('#trust_notebook').click(function () {
137 IPython.notebook.trust_notebook();
137 IPython.notebook.trust_notebook();
138 });
138 });
139 $([IPython.events]).on('trust_changed.Notebook', function (event, trusted) {
139 $([IPython.events]).on('trust_changed.Notebook', function (event, trusted) {
140 if (trusted) {
140 if (trusted) {
141 that.element.find('#trust_notebook')
141 that.element.find('#trust_notebook')
142 .addClass("disabled")
142 .addClass("disabled")
143 .find("a").text("Trusted Notebook");
143 .find("a").text("Trusted Notebook");
144 } else {
144 } else {
145 that.element.find('#trust_notebook')
145 that.element.find('#trust_notebook')
146 .removeClass("disabled")
146 .removeClass("disabled")
147 .find("a").text("Trust Notebook");
147 .find("a").text("Trust Notebook");
148 }
148 }
149 });
149 });
150 this.element.find('#kill_and_exit').click(function () {
150 this.element.find('#kill_and_exit').click(function () {
151 IPython.notebook.session.delete();
151 IPython.notebook.session.delete();
152 setTimeout(function(){
152 setTimeout(function(){
153 // allow closing of new tabs in Chromium, impossible in FF
153 // allow closing of new tabs in Chromium, impossible in FF
154 window.open('', '_self', '');
154 window.open('', '_self', '');
155 window.close();
155 window.close();
156 }, 500);
156 }, 500);
157 });
157 });
158 // Edit
158 // Edit
159 this.element.find('#cut_cell').click(function () {
159 this.element.find('#cut_cell').click(function () {
160 IPython.notebook.cut_cell();
160 IPython.notebook.cut_cell();
161 });
161 });
162 this.element.find('#copy_cell').click(function () {
162 this.element.find('#copy_cell').click(function () {
163 IPython.notebook.copy_cell();
163 IPython.notebook.copy_cell();
164 });
164 });
165 this.element.find('#delete_cell').click(function () {
165 this.element.find('#delete_cell').click(function () {
166 IPython.notebook.delete_cell();
166 IPython.notebook.delete_cell();
167 });
167 });
168 this.element.find('#undelete_cell').click(function () {
168 this.element.find('#undelete_cell').click(function () {
169 IPython.notebook.undelete_cell();
169 IPython.notebook.undelete_cell();
170 });
170 });
171 this.element.find('#split_cell').click(function () {
171 this.element.find('#split_cell').click(function () {
172 IPython.notebook.split_cell();
172 IPython.notebook.split_cell();
173 });
173 });
174 this.element.find('#merge_cell_above').click(function () {
174 this.element.find('#merge_cell_above').click(function () {
175 IPython.notebook.merge_cell_above();
175 IPython.notebook.merge_cell_above();
176 });
176 });
177 this.element.find('#merge_cell_below').click(function () {
177 this.element.find('#merge_cell_below').click(function () {
178 IPython.notebook.merge_cell_below();
178 IPython.notebook.merge_cell_below();
179 });
179 });
180 this.element.find('#move_cell_up').click(function () {
180 this.element.find('#move_cell_up').click(function () {
181 IPython.notebook.move_cell_up();
181 IPython.notebook.move_cell_up();
182 });
182 });
183 this.element.find('#move_cell_down').click(function () {
183 this.element.find('#move_cell_down').click(function () {
184 IPython.notebook.move_cell_down();
184 IPython.notebook.move_cell_down();
185 });
185 });
186 this.element.find('#edit_nb_metadata').click(function () {
186 this.element.find('#edit_nb_metadata').click(function () {
187 IPython.notebook.edit_metadata();
187 IPython.notebook.edit_metadata();
188 });
188 });
189
189
190 // View
190 // View
191 this.element.find('#toggle_header').click(function () {
191 this.element.find('#toggle_header').click(function () {
192 $('div#header').toggle();
192 $('div#header').toggle();
193 IPython.layout_manager.do_resize();
193 IPython.layout_manager.do_resize();
194 });
194 });
195 this.element.find('#toggle_toolbar').click(function () {
195 this.element.find('#toggle_toolbar').click(function () {
196 $('div#maintoolbar').toggle();
196 $('div#maintoolbar').toggle();
197 IPython.layout_manager.do_resize();
197 IPython.layout_manager.do_resize();
198 });
198 });
199 // Insert
199 // Insert
200 this.element.find('#insert_cell_above').click(function () {
200 this.element.find('#insert_cell_above').click(function () {
201 IPython.notebook.insert_cell_above('code');
201 IPython.notebook.insert_cell_above('code');
202 IPython.notebook.select_prev();
202 IPython.notebook.select_prev();
203 });
203 });
204 this.element.find('#insert_cell_below').click(function () {
204 this.element.find('#insert_cell_below').click(function () {
205 IPython.notebook.insert_cell_below('code');
205 IPython.notebook.insert_cell_below('code');
206 IPython.notebook.select_next();
206 IPython.notebook.select_next();
207 });
207 });
208 // Cell
208 // Cell
209 this.element.find('#run_cell').click(function () {
209 this.element.find('#run_cell').click(function () {
210 IPython.notebook.execute_cell();
210 IPython.notebook.execute_cell();
211 });
211 });
212 this.element.find('#run_cell_select_below').click(function () {
212 this.element.find('#run_cell_select_below').click(function () {
213 IPython.notebook.execute_cell_and_select_below();
213 IPython.notebook.execute_cell_and_select_below();
214 });
214 });
215 this.element.find('#run_cell_insert_below').click(function () {
215 this.element.find('#run_cell_insert_below').click(function () {
216 IPython.notebook.execute_cell_and_insert_below();
216 IPython.notebook.execute_cell_and_insert_below();
217 });
217 });
218 this.element.find('#run_all_cells').click(function () {
218 this.element.find('#run_all_cells').click(function () {
219 IPython.notebook.execute_all_cells();
219 IPython.notebook.execute_all_cells();
220 });
220 });
221 this.element.find('#run_all_cells_above').click(function () {
221 this.element.find('#run_all_cells_above').click(function () {
222 IPython.notebook.execute_cells_above();
222 IPython.notebook.execute_cells_above();
223 });
223 });
224 this.element.find('#run_all_cells_below').click(function () {
224 this.element.find('#run_all_cells_below').click(function () {
225 IPython.notebook.execute_cells_below();
225 IPython.notebook.execute_cells_below();
226 });
226 });
227 this.element.find('#to_code').click(function () {
227 this.element.find('#to_code').click(function () {
228 IPython.notebook.to_code();
228 IPython.notebook.to_code();
229 });
229 });
230 this.element.find('#to_markdown').click(function () {
230 this.element.find('#to_markdown').click(function () {
231 IPython.notebook.to_markdown();
231 IPython.notebook.to_markdown();
232 });
232 });
233 this.element.find('#to_raw').click(function () {
233 this.element.find('#to_raw').click(function () {
234 IPython.notebook.to_raw();
234 IPython.notebook.to_raw();
235 });
235 });
236 this.element.find('#to_heading1').click(function () {
236 this.element.find('#to_heading1').click(function () {
237 IPython.notebook.to_heading(undefined, 1);
237 IPython.notebook.to_heading(undefined, 1);
238 });
238 });
239 this.element.find('#to_heading2').click(function () {
239 this.element.find('#to_heading2').click(function () {
240 IPython.notebook.to_heading(undefined, 2);
240 IPython.notebook.to_heading(undefined, 2);
241 });
241 });
242 this.element.find('#to_heading3').click(function () {
242 this.element.find('#to_heading3').click(function () {
243 IPython.notebook.to_heading(undefined, 3);
243 IPython.notebook.to_heading(undefined, 3);
244 });
244 });
245 this.element.find('#to_heading4').click(function () {
245 this.element.find('#to_heading4').click(function () {
246 IPython.notebook.to_heading(undefined, 4);
246 IPython.notebook.to_heading(undefined, 4);
247 });
247 });
248 this.element.find('#to_heading5').click(function () {
248 this.element.find('#to_heading5').click(function () {
249 IPython.notebook.to_heading(undefined, 5);
249 IPython.notebook.to_heading(undefined, 5);
250 });
250 });
251 this.element.find('#to_heading6').click(function () {
251 this.element.find('#to_heading6').click(function () {
252 IPython.notebook.to_heading(undefined, 6);
252 IPython.notebook.to_heading(undefined, 6);
253 });
253 });
254
254
255 this.element.find('#toggle_current_output').click(function () {
255 this.element.find('#toggle_current_output').click(function () {
256 IPython.notebook.toggle_output();
256 IPython.notebook.toggle_output();
257 });
257 });
258 this.element.find('#toggle_current_output_scroll').click(function () {
258 this.element.find('#toggle_current_output_scroll').click(function () {
259 IPython.notebook.toggle_output_scroll();
259 IPython.notebook.toggle_output_scroll();
260 });
260 });
261 this.element.find('#clear_current_output').click(function () {
261 this.element.find('#clear_current_output').click(function () {
262 IPython.notebook.clear_output();
262 IPython.notebook.clear_output();
263 });
263 });
264
264
265 this.element.find('#toggle_all_output').click(function () {
265 this.element.find('#toggle_all_output').click(function () {
266 IPython.notebook.toggle_all_output();
266 IPython.notebook.toggle_all_output();
267 });
267 });
268 this.element.find('#toggle_all_output_scroll').click(function () {
268 this.element.find('#toggle_all_output_scroll').click(function () {
269 IPython.notebook.toggle_all_output_scroll();
269 IPython.notebook.toggle_all_output_scroll();
270 });
270 });
271 this.element.find('#clear_all_output').click(function () {
271 this.element.find('#clear_all_output').click(function () {
272 IPython.notebook.clear_all_output();
272 IPython.notebook.clear_all_output();
273 });
273 });
274
274
275 // Kernel
275 // Kernel
276 this.element.find('#int_kernel').click(function () {
276 this.element.find('#int_kernel').click(function () {
277 IPython.notebook.session.interrupt_kernel();
277 IPython.notebook.session.interrupt_kernel();
278 });
278 });
279 this.element.find('#restart_kernel').click(function () {
279 this.element.find('#restart_kernel').click(function () {
280 IPython.notebook.restart_kernel();
280 IPython.notebook.restart_kernel();
281 });
281 });
282 // Help
282 // Help
283 this.element.find('#notebook_tour').click(function () {
283 this.element.find('#notebook_tour').click(function () {
284 IPython.tour.start();
284 IPython.tour.start();
285 });
285 });
286 this.element.find('#keyboard_shortcuts').click(function () {
286 this.element.find('#keyboard_shortcuts').click(function () {
287 IPython.quick_help.show_keyboard_shortcuts();
287 IPython.quick_help.show_keyboard_shortcuts();
288 });
288 });
289
289
290 this.update_restore_checkpoint(null);
290 this.update_restore_checkpoint(null);
291
291
292 $([IPython.events]).on('checkpoints_listed.Notebook', function (event, data) {
292 $([IPython.events]).on('checkpoints_listed.Notebook', function (event, data) {
293 that.update_restore_checkpoint(IPython.notebook.checkpoints);
293 that.update_restore_checkpoint(IPython.notebook.checkpoints);
294 });
294 });
295
295
296 $([IPython.events]).on('checkpoint_created.Notebook', function (event, data) {
296 $([IPython.events]).on('checkpoint_created.Notebook', function (event, data) {
297 that.update_restore_checkpoint(IPython.notebook.checkpoints);
297 that.update_restore_checkpoint(IPython.notebook.checkpoints);
298 });
298 });
299 };
299 };
300
300
301 MenuBar.prototype.update_restore_checkpoint = function(checkpoints) {
301 MenuBar.prototype.update_restore_checkpoint = function(checkpoints) {
302 var ul = this.element.find("#restore_checkpoint").find("ul");
302 var ul = this.element.find("#restore_checkpoint").find("ul");
303 ul.empty();
303 ul.empty();
304 if (!checkpoints || checkpoints.length === 0) {
304 if (!checkpoints || checkpoints.length === 0) {
305 ul.append(
305 ul.append(
306 $("<li/>")
306 $("<li/>")
307 .addClass("disabled")
307 .addClass("disabled")
308 .append(
308 .append(
309 $("<a/>")
309 $("<a/>")
310 .text("No checkpoints")
310 .text("No checkpoints")
311 )
311 )
312 );
312 );
313 return;
313 return;
314 }
314 }
315
315
316 checkpoints.map(function (checkpoint) {
316 checkpoints.map(function (checkpoint) {
317 var d = new Date(checkpoint.last_modified);
317 var d = new Date(checkpoint.last_modified);
318 ul.append(
318 ul.append(
319 $("<li/>").append(
319 $("<li/>").append(
320 $("<a/>")
320 $("<a/>")
321 .attr("href", "#")
321 .attr("href", "#")
322 .text(d.format("mmm dd HH:MM:ss"))
322 .text(d.format("mmm dd HH:MM:ss"))
323 .click(function () {
323 .click(function () {
324 IPython.notebook.restore_checkpoint_dialog(checkpoint);
324 IPython.notebook.restore_checkpoint_dialog(checkpoint);
325 })
325 })
326 )
326 )
327 );
327 );
328 });
328 });
329 };
329 };
330
330
331 IPython.MenuBar = MenuBar;
331 IPython.MenuBar = MenuBar;
332
332
333 return IPython;
333 return IPython;
334
334
335 }(IPython));
335 }(IPython));
@@ -1,361 +1,362 b''
1 {% extends "page.html" %}
1 {% extends "page.html" %}
2
2
3 {% block stylesheet %}
3 {% block stylesheet %}
4
4
5 {% if mathjax_url %}
5 {% if mathjax_url %}
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
7 {% endif %}
7 {% endif %}
8 <script type="text/javascript">
8 <script type="text/javascript">
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
10 // where it will be undefined, and should prompt a dialog later.
10 // where it will be undefined, and should prompt a dialog later.
11 window.mathjax_url = "{{mathjax_url}}";
11 window.mathjax_url = "{{mathjax_url}}";
12 </script>
12 </script>
13
13
14 <link rel="stylesheet" href="{{ static_url("components/bootstrap-tour/build/css/bootstrap-tour.min.css") }}" type="text/css" />
14 <link rel="stylesheet" href="{{ static_url("components/bootstrap-tour/build/css/bootstrap-tour.min.css") }}" type="text/css" />
15 <link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
15 <link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
16
16
17 {{super()}}
17 {{super()}}
18
18
19 <link rel="stylesheet" href="{{ static_url("notebook/css/override.css") }}" type="text/css" />
19 <link rel="stylesheet" href="{{ static_url("notebook/css/override.css") }}" type="text/css" />
20
20
21 {% endblock %}
21 {% endblock %}
22
22
23 {% block params %}
23 {% block params %}
24
24
25 data-project="{{project}}"
25 data-project="{{project}}"
26 data-base-url="{{base_url}}"
26 data-base-url="{{base_url}}"
27 data-notebook-name="{{notebook_name}}"
27 data-notebook-name="{{notebook_name}}"
28 data-notebook-path="{{notebook_path}}"
28 data-notebook-path="{{notebook_path}}"
29 class="notebook_app"
29 class="notebook_app"
30
30
31 {% endblock %}
31 {% endblock %}
32
32
33
33
34 {% block header %}
34 {% block header %}
35
35
36 <span id="save_widget" class="nav pull-left">
36 <span id="save_widget" class="nav pull-left">
37 <span id="notebook_name"></span>
37 <span id="notebook_name"></span>
38 <span id="checkpoint_status"></span>
38 <span id="checkpoint_status"></span>
39 <span id="autosave_status"></span>
39 <span id="autosave_status"></span>
40 </span>
40 </span>
41
41
42 {% endblock %}
42 {% endblock %}
43
43
44
44
45 {% block site %}
45 {% block site %}
46
46
47 <div id="menubar-container" class="container">
47 <div id="menubar-container" class="container">
48 <div id="menubar">
48 <div id="menubar">
49 <div class="navbar">
49 <div class="navbar">
50 <div class="navbar-inner">
50 <div class="navbar-inner">
51 <div class="container">
51 <div class="container">
52 <ul id="menus" class="nav">
52 <ul id="menus" class="nav">
53 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
53 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
54 <ul id="file_menu" class="dropdown-menu">
54 <ul id="file_menu" class="dropdown-menu">
55 <li id="new_notebook"
55 <li id="new_notebook"
56 title="Make a new notebook (Opens a new window)">
56 title="Make a new notebook (Opens a new window)">
57 <a href="#">New</a></li>
57 <a href="#">New</a></li>
58 <li id="open_notebook"
58 <li id="open_notebook"
59 title="Opens a new window with the Dashboard view">
59 title="Opens a new window with the Dashboard view">
60 <a href="#">Open...</a></li>
60 <a href="#">Open...</a></li>
61 <!-- <hr/> -->
61 <!-- <hr/> -->
62 <li class="divider"></li>
62 <li class="divider"></li>
63 <li id="copy_notebook"
63 <li id="copy_notebook"
64 title="Open a copy of this notebook's contents and start a new kernel">
64 title="Open a copy of this notebook's contents and start a new kernel">
65 <a href="#">Make a Copy...</a></li>
65 <a href="#">Make a Copy...</a></li>
66 <li id="rename_notebook"><a href="#">Rename...</a></li>
66 <li id="rename_notebook"><a href="#">Rename...</a></li>
67 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
67 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
68 <!-- <hr/> -->
68 <!-- <hr/> -->
69 <li class="divider"></li>
69 <li class="divider"></li>
70 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
70 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
71 <ul class="dropdown-menu">
71 <ul class="dropdown-menu">
72 <li><a href="#"></a></li>
72 <li><a href="#"></a></li>
73 <li><a href="#"></a></li>
73 <li><a href="#"></a></li>
74 <li><a href="#"></a></li>
74 <li><a href="#"></a></li>
75 <li><a href="#"></a></li>
75 <li><a href="#"></a></li>
76 <li><a href="#"></a></li>
76 <li><a href="#"></a></li>
77 </ul>
77 </ul>
78 </li>
78 </li>
79 <li class="divider"></li>
79 <li class="divider"></li>
80 <li id="print_preview"><a href="#">Print Preview</a></li>
80 <li id="print_preview"><a href="#">Print Preview</a></li>
81 <li class="dropdown-submenu"><a href="#">Download as</a>
81 <li class="dropdown-submenu"><a href="#">Download as</a>
82 <ul class="dropdown-menu">
82 <ul class="dropdown-menu">
83 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
83 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
84 <li id="download_py"><a href="#">Python (.py)</a></li>
84 <li id="download_py"><a href="#">Python (.py)</a></li>
85 <li id="download_html"><a href="#">HTML (.html)</a></li>
85 <li id="download_html"><a href="#">HTML (.html)</a></li>
86 <li id="download_rst"><a href="#">reST (.rst)</a></li>
86 <li id="download_rst"><a href="#">reST (.rst)</a></li>
87 <li id="download_pdf"><a href="#">PDF (.pdf)</a></li>
87 </ul>
88 </ul>
88 </li>
89 </li>
89 <li class="divider"></li>
90 <li class="divider"></li>
90 <li id="trust_notebook"
91 <li id="trust_notebook"
91 title="Trust the output of this notebook">
92 title="Trust the output of this notebook">
92 <a href="#" >Trust Notebook</a></li>
93 <a href="#" >Trust Notebook</a></li>
93 <li class="divider"></li>
94 <li class="divider"></li>
94 <li id="kill_and_exit"
95 <li id="kill_and_exit"
95 title="Shutdown this notebook's kernel, and close this window">
96 title="Shutdown this notebook's kernel, and close this window">
96 <a href="#" >Close and halt</a></li>
97 <a href="#" >Close and halt</a></li>
97 </ul>
98 </ul>
98 </li>
99 </li>
99 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
100 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
100 <ul id="edit_menu" class="dropdown-menu">
101 <ul id="edit_menu" class="dropdown-menu">
101 <li id="cut_cell"><a href="#">Cut Cell</a></li>
102 <li id="cut_cell"><a href="#">Cut Cell</a></li>
102 <li id="copy_cell"><a href="#">Copy Cell</a></li>
103 <li id="copy_cell"><a href="#">Copy Cell</a></li>
103 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
104 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
104 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
105 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
105 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
106 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
106 <li id="delete_cell"><a href="#">Delete Cell</a></li>
107 <li id="delete_cell"><a href="#">Delete Cell</a></li>
107 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
108 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
108 <li class="divider"></li>
109 <li class="divider"></li>
109 <li id="split_cell"><a href="#">Split Cell</a></li>
110 <li id="split_cell"><a href="#">Split Cell</a></li>
110 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
111 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
111 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
112 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
112 <li class="divider"></li>
113 <li class="divider"></li>
113 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
114 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
114 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
115 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
115 <li class="divider"></li>
116 <li class="divider"></li>
116 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
117 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
117 </ul>
118 </ul>
118 </li>
119 </li>
119 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
120 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
120 <ul id="view_menu" class="dropdown-menu">
121 <ul id="view_menu" class="dropdown-menu">
121 <li id="toggle_header"
122 <li id="toggle_header"
122 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
123 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
123 <a href="#">Toggle Header</a></li>
124 <a href="#">Toggle Header</a></li>
124 <li id="toggle_toolbar"
125 <li id="toggle_toolbar"
125 title="Show/Hide the action icons (below menu bar)">
126 title="Show/Hide the action icons (below menu bar)">
126 <a href="#">Toggle Toolbar</a></li>
127 <a href="#">Toggle Toolbar</a></li>
127 </ul>
128 </ul>
128 </li>
129 </li>
129 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
130 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
130 <ul id="insert_menu" class="dropdown-menu">
131 <ul id="insert_menu" class="dropdown-menu">
131 <li id="insert_cell_above"
132 <li id="insert_cell_above"
132 title="Insert an empty Code cell above the currently active cell">
133 title="Insert an empty Code cell above the currently active cell">
133 <a href="#">Insert Cell Above</a></li>
134 <a href="#">Insert Cell Above</a></li>
134 <li id="insert_cell_below"
135 <li id="insert_cell_below"
135 title="Insert an empty Code cell below the currently active cell">
136 title="Insert an empty Code cell below the currently active cell">
136 <a href="#">Insert Cell Below</a></li>
137 <a href="#">Insert Cell Below</a></li>
137 </ul>
138 </ul>
138 </li>
139 </li>
139 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
140 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
140 <ul id="cell_menu" class="dropdown-menu">
141 <ul id="cell_menu" class="dropdown-menu">
141 <li id="run_cell" title="Run this cell, and move cursor to the next one">
142 <li id="run_cell" title="Run this cell, and move cursor to the next one">
142 <a href="#">Run</a></li>
143 <a href="#">Run</a></li>
143 <li id="run_cell_select_below" title="Run this cell, select below">
144 <li id="run_cell_select_below" title="Run this cell, select below">
144 <a href="#">Run and Select Below</a></li>
145 <a href="#">Run and Select Below</a></li>
145 <li id="run_cell_insert_below" title="Run this cell, insert below">
146 <li id="run_cell_insert_below" title="Run this cell, insert below">
146 <a href="#">Run and Insert Below</a></li>
147 <a href="#">Run and Insert Below</a></li>
147 <li id="run_all_cells" title="Run all cells in the notebook">
148 <li id="run_all_cells" title="Run all cells in the notebook">
148 <a href="#">Run All</a></li>
149 <a href="#">Run All</a></li>
149 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
150 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
150 <a href="#">Run All Above</a></li>
151 <a href="#">Run All Above</a></li>
151 <li id="run_all_cells_below" title="Run this cell and all cells below it">
152 <li id="run_all_cells_below" title="Run this cell and all cells below it">
152 <a href="#">Run All Below</a></li>
153 <a href="#">Run All Below</a></li>
153 <li class="divider"></li>
154 <li class="divider"></li>
154 <li id="change_cell_type" class="dropdown-submenu"
155 <li id="change_cell_type" class="dropdown-submenu"
155 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
156 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
156 <a href="#">Cell Type</a>
157 <a href="#">Cell Type</a>
157 <ul class="dropdown-menu">
158 <ul class="dropdown-menu">
158 <li id="to_code"
159 <li id="to_code"
159 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
160 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
160 <a href="#">Code</a></li>
161 <a href="#">Code</a></li>
161 <li id="to_markdown"
162 <li id="to_markdown"
162 title="Contents will be rendered as HTML and serve as explanatory text">
163 title="Contents will be rendered as HTML and serve as explanatory text">
163 <a href="#">Markdown</a></li>
164 <a href="#">Markdown</a></li>
164 <li id="to_raw"
165 <li id="to_raw"
165 title="Contents will pass through nbconvert unmodified">
166 title="Contents will pass through nbconvert unmodified">
166 <a href="#">Raw NBConvert</a></li>
167 <a href="#">Raw NBConvert</a></li>
167 <li id="to_heading1"><a href="#">Heading 1</a></li>
168 <li id="to_heading1"><a href="#">Heading 1</a></li>
168 <li id="to_heading2"><a href="#">Heading 2</a></li>
169 <li id="to_heading2"><a href="#">Heading 2</a></li>
169 <li id="to_heading3"><a href="#">Heading 3</a></li>
170 <li id="to_heading3"><a href="#">Heading 3</a></li>
170 <li id="to_heading4"><a href="#">Heading 4</a></li>
171 <li id="to_heading4"><a href="#">Heading 4</a></li>
171 <li id="to_heading5"><a href="#">Heading 5</a></li>
172 <li id="to_heading5"><a href="#">Heading 5</a></li>
172 <li id="to_heading6"><a href="#">Heading 6</a></li>
173 <li id="to_heading6"><a href="#">Heading 6</a></li>
173 </ul>
174 </ul>
174 </li>
175 </li>
175 <li class="divider"></li>
176 <li class="divider"></li>
176 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
177 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
177 <ul class="dropdown-menu">
178 <ul class="dropdown-menu">
178 <li id="toggle_current_output"
179 <li id="toggle_current_output"
179 title="Hide/Show the output of the current cell">
180 title="Hide/Show the output of the current cell">
180 <a href="#">Toggle</a>
181 <a href="#">Toggle</a>
181 </li>
182 </li>
182 <li id="toggle_current_output_scroll"
183 <li id="toggle_current_output_scroll"
183 title="Scroll the output of the current cell">
184 title="Scroll the output of the current cell">
184 <a href="#">Toggle Scrolling</a>
185 <a href="#">Toggle Scrolling</a>
185 </li>
186 </li>
186 <li id="clear_current_output"
187 <li id="clear_current_output"
187 title="Clear the output of the current cell">
188 title="Clear the output of the current cell">
188 <a href="#">Clear</a>
189 <a href="#">Clear</a>
189 </li>
190 </li>
190 </ul>
191 </ul>
191 </li>
192 </li>
192 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
193 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
193 <ul class="dropdown-menu">
194 <ul class="dropdown-menu">
194 <li id="toggle_all_output"
195 <li id="toggle_all_output"
195 title="Hide/Show the output of all cells">
196 title="Hide/Show the output of all cells">
196 <a href="#">Toggle</a>
197 <a href="#">Toggle</a>
197 </li>
198 </li>
198 <li id="toggle_all_output_scroll"
199 <li id="toggle_all_output_scroll"
199 title="Scroll the output of all cells">
200 title="Scroll the output of all cells">
200 <a href="#">Toggle Scrolling</a>
201 <a href="#">Toggle Scrolling</a>
201 </li>
202 </li>
202 <li id="clear_all_output"
203 <li id="clear_all_output"
203 title="Clear the output of all cells">
204 title="Clear the output of all cells">
204 <a href="#">Clear</a>
205 <a href="#">Clear</a>
205 </li>
206 </li>
206 </ul>
207 </ul>
207 </li>
208 </li>
208 </ul>
209 </ul>
209 </li>
210 </li>
210 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
211 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
211 <ul id="kernel_menu" class="dropdown-menu">
212 <ul id="kernel_menu" class="dropdown-menu">
212 <li id="int_kernel"
213 <li id="int_kernel"
213 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
214 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
214 <a href="#">Interrupt</a></li>
215 <a href="#">Interrupt</a></li>
215 <li id="restart_kernel"
216 <li id="restart_kernel"
216 title="Restart the Kernel">
217 title="Restart the Kernel">
217 <a href="#">Restart</a></li>
218 <a href="#">Restart</a></li>
218 </ul>
219 </ul>
219 </li>
220 </li>
220 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
221 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
221 <ul id="help_menu" class="dropdown-menu">
222 <ul id="help_menu" class="dropdown-menu">
222 <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
223 <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
223 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
224 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
224 <li class="divider"></li>
225 <li class="divider"></li>
225 {% set
226 {% set
226 sections = (
227 sections = (
227 (
228 (
228 ("http://ipython.org/documentation.html","IPython Help",True),
229 ("http://ipython.org/documentation.html","IPython Help",True),
229 ("http://nbviewer.ipython.org/github/ipython/ipython/tree/2.x/examples/Index.ipynb", "Notebook Help", True),
230 ("http://nbviewer.ipython.org/github/ipython/ipython/tree/2.x/examples/Index.ipynb", "Notebook Help", True),
230 ),(
231 ),(
231 ("http://docs.python.org","Python",True),
232 ("http://docs.python.org","Python",True),
232 ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
233 ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
233 ("http://docs.scipy.org/doc/numpy/reference/","NumPy",True),
234 ("http://docs.scipy.org/doc/numpy/reference/","NumPy",True),
234 ("http://docs.scipy.org/doc/scipy/reference/","SciPy",True),
235 ("http://docs.scipy.org/doc/scipy/reference/","SciPy",True),
235 ("http://matplotlib.org/contents.html","Matplotlib",True),
236 ("http://matplotlib.org/contents.html","Matplotlib",True),
236 ("http://docs.sympy.org/latest/index.html","SymPy",True),
237 ("http://docs.sympy.org/latest/index.html","SymPy",True),
237 ("http://pandas.pydata.org/pandas-docs/stable/","pandas", True)
238 ("http://pandas.pydata.org/pandas-docs/stable/","pandas", True)
238 )
239 )
239 )
240 )
240 %}
241 %}
241
242
242 {% for helplinks in sections %}
243 {% for helplinks in sections %}
243 {% for link in helplinks %}
244 {% for link in helplinks %}
244 <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
245 <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
245 {{'<i class="icon-external-link menu-icon pull-right"></i>' if link[2]}}
246 {{'<i class="icon-external-link menu-icon pull-right"></i>' if link[2]}}
246 {{link[1]}}
247 {{link[1]}}
247 </a></li>
248 </a></li>
248 {% endfor %}
249 {% endfor %}
249 {% if not loop.last %}
250 {% if not loop.last %}
250 <li class="divider"></li>
251 <li class="divider"></li>
251 {% endif %}
252 {% endif %}
252 {% endfor %}
253 {% endfor %}
253 </li>
254 </li>
254 </ul>
255 </ul>
255 </li>
256 </li>
256 </ul>
257 </ul>
257 <div id="kernel_indicator" class="indicator_area pull-right">
258 <div id="kernel_indicator" class="indicator_area pull-right">
258 <i id="kernel_indicator_icon"></i>
259 <i id="kernel_indicator_icon"></i>
259 </div>
260 </div>
260 <div id="modal_indicator" class="indicator_area pull-right">
261 <div id="modal_indicator" class="indicator_area pull-right">
261 <i id="modal_indicator_icon"></i>
262 <i id="modal_indicator_icon"></i>
262 </div>
263 </div>
263 <div id="notification_area"></div>
264 <div id="notification_area"></div>
264 </div>
265 </div>
265 </div>
266 </div>
266 </div>
267 </div>
267 </div>
268 </div>
268 <div id="maintoolbar" class="navbar">
269 <div id="maintoolbar" class="navbar">
269 <div class="toolbar-inner navbar-inner navbar-nobg">
270 <div class="toolbar-inner navbar-inner navbar-nobg">
270 <div id="maintoolbar-container" class="container"></div>
271 <div id="maintoolbar-container" class="container"></div>
271 </div>
272 </div>
272 </div>
273 </div>
273 </div>
274 </div>
274
275
275 <div id="ipython-main-app">
276 <div id="ipython-main-app">
276
277
277 <div id="notebook_panel">
278 <div id="notebook_panel">
278 <div id="notebook"></div>
279 <div id="notebook"></div>
279 <div id="pager_splitter"></div>
280 <div id="pager_splitter"></div>
280 <div id="pager">
281 <div id="pager">
281 <div id='pager_button_area'>
282 <div id='pager_button_area'>
282 </div>
283 </div>
283 <div id="pager-container" class="container"></div>
284 <div id="pager-container" class="container"></div>
284 </div>
285 </div>
285 </div>
286 </div>
286
287
287 </div>
288 </div>
288 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
289 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
289
290
290
291
291 {% endblock %}
292 {% endblock %}
292
293
293
294
294 {% block script %}
295 {% block script %}
295
296
296 {{super()}}
297 {{super()}}
297
298
298 <script src="{{ static_url("components/google-caja/html-css-sanitizer-minified.js") }}" charset="utf-8"></script>
299 <script src="{{ static_url("components/google-caja/html-css-sanitizer-minified.js") }}" charset="utf-8"></script>
299 <script src="{{ static_url("components/codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
300 <script src="{{ static_url("components/codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
300 <script type="text/javascript">
301 <script type="text/javascript">
301 CodeMirror.modeURL = "{{ static_url("components/codemirror/mode/%N/%N.js", include_version=False) }}";
302 CodeMirror.modeURL = "{{ static_url("components/codemirror/mode/%N/%N.js", include_version=False) }}";
302 </script>
303 </script>
303 <script src="{{ static_url("components/codemirror/addon/mode/loadmode.js") }}" charset="utf-8"></script>
304 <script src="{{ static_url("components/codemirror/addon/mode/loadmode.js") }}" charset="utf-8"></script>
304 <script src="{{ static_url("components/codemirror/addon/mode/multiplex.js") }}" charset="utf-8"></script>
305 <script src="{{ static_url("components/codemirror/addon/mode/multiplex.js") }}" charset="utf-8"></script>
305 <script src="{{ static_url("components/codemirror/addon/mode/overlay.js") }}" charset="utf-8"></script>
306 <script src="{{ static_url("components/codemirror/addon/mode/overlay.js") }}" charset="utf-8"></script>
306 <script src="{{ static_url("components/codemirror/addon/edit/matchbrackets.js") }}" charset="utf-8"></script>
307 <script src="{{ static_url("components/codemirror/addon/edit/matchbrackets.js") }}" charset="utf-8"></script>
307 <script src="{{ static_url("components/codemirror/addon/edit/closebrackets.js") }}" charset="utf-8"></script>
308 <script src="{{ static_url("components/codemirror/addon/edit/closebrackets.js") }}" charset="utf-8"></script>
308 <script src="{{ static_url("components/codemirror/addon/comment/comment.js") }}" charset="utf-8"></script>
309 <script src="{{ static_url("components/codemirror/addon/comment/comment.js") }}" charset="utf-8"></script>
309 <script src="{{ static_url("components/codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
310 <script src="{{ static_url("components/codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
310 <script src="{{ static_url("components/codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
311 <script src="{{ static_url("components/codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
311 <script src="{{ static_url("components/codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
312 <script src="{{ static_url("components/codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
312 <script src="{{ static_url("components/codemirror/mode/css/css.js") }}" charset="utf-8"></script>
313 <script src="{{ static_url("components/codemirror/mode/css/css.js") }}" charset="utf-8"></script>
313 <script src="{{ static_url("components/codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
314 <script src="{{ static_url("components/codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
314 <script src="{{ static_url("components/codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
315 <script src="{{ static_url("components/codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
315 <script src="{{ static_url("components/codemirror/mode/gfm/gfm.js") }}" charset="utf-8"></script>
316 <script src="{{ static_url("components/codemirror/mode/gfm/gfm.js") }}" charset="utf-8"></script>
316 <script src="{{ static_url("components/codemirror/mode/python/python.js") }}" charset="utf-8"></script>
317 <script src="{{ static_url("components/codemirror/mode/python/python.js") }}" charset="utf-8"></script>
317 <script src="{{ static_url("notebook/js/codemirror-ipython.js") }}" charset="utf-8"></script>
318 <script src="{{ static_url("notebook/js/codemirror-ipython.js") }}" charset="utf-8"></script>
318
319
319 <script src="{{ static_url("components/highlight.js/build/highlight.pack.js") }}" charset="utf-8"></script>
320 <script src="{{ static_url("components/highlight.js/build/highlight.pack.js") }}" charset="utf-8"></script>
320
321
321 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
322 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
322
323
323 <script src="{{ static_url("base/js/events.js") }}" type="text/javascript" charset="utf-8"></script>
324 <script src="{{ static_url("base/js/events.js") }}" type="text/javascript" charset="utf-8"></script>
324 <script src="{{ static_url("base/js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
325 <script src="{{ static_url("base/js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
325 <script src="{{ static_url("base/js/keyboard.js") }}" type="text/javascript" charset="utf-8"></script>
326 <script src="{{ static_url("base/js/keyboard.js") }}" type="text/javascript" charset="utf-8"></script>
326 <script src="{{ static_url("base/js/security.js") }}" type="text/javascript" charset="utf-8"></script>
327 <script src="{{ static_url("base/js/security.js") }}" type="text/javascript" charset="utf-8"></script>
327 <script src="{{ static_url("base/js/dialog.js") }}" type="text/javascript" charset="utf-8"></script>
328 <script src="{{ static_url("base/js/dialog.js") }}" type="text/javascript" charset="utf-8"></script>
328 <script src="{{ static_url("services/kernels/js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
329 <script src="{{ static_url("services/kernels/js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
329 <script src="{{ static_url("services/kernels/js/comm.js") }}" type="text/javascript" charset="utf-8"></script>
330 <script src="{{ static_url("services/kernels/js/comm.js") }}" type="text/javascript" charset="utf-8"></script>
330 <script src="{{ static_url("services/sessions/js/session.js") }}" type="text/javascript" charset="utf-8"></script>
331 <script src="{{ static_url("services/sessions/js/session.js") }}" type="text/javascript" charset="utf-8"></script>
331 <script src="{{ static_url("notebook/js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script>
332 <script src="{{ static_url("notebook/js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script>
332 <script src="{{ static_url("notebook/js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
333 <script src="{{ static_url("notebook/js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
333 <script src="{{ static_url("notebook/js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
334 <script src="{{ static_url("notebook/js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
334 <script src="{{ static_url("notebook/js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
335 <script src="{{ static_url("notebook/js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
335 <script src="{{ static_url("notebook/js/celltoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
336 <script src="{{ static_url("notebook/js/celltoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
336 <script src="{{ static_url("notebook/js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
337 <script src="{{ static_url("notebook/js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
337 <script src="{{ static_url("notebook/js/completer.js") }}" type="text/javascript" charset="utf-8"></script>
338 <script src="{{ static_url("notebook/js/completer.js") }}" type="text/javascript" charset="utf-8"></script>
338 <script src="{{ static_url("notebook/js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
339 <script src="{{ static_url("notebook/js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
339 <script src="{{ static_url("notebook/js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
340 <script src="{{ static_url("notebook/js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
340 <script src="{{ static_url("notebook/js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
341 <script src="{{ static_url("notebook/js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
341 <script src="{{ static_url("notebook/js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
342 <script src="{{ static_url("notebook/js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
342 <script src="{{ static_url("notebook/js/menubar.js") }}" type="text/javascript" charset="utf-8"></script>
343 <script src="{{ static_url("notebook/js/menubar.js") }}" type="text/javascript" charset="utf-8"></script>
343 <script src="{{ static_url("notebook/js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
344 <script src="{{ static_url("notebook/js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
344 <script src="{{ static_url("notebook/js/maintoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
345 <script src="{{ static_url("notebook/js/maintoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
345 <script src="{{ static_url("notebook/js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
346 <script src="{{ static_url("notebook/js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
346 <script src="{{ static_url("notebook/js/keyboardmanager.js") }}" type="text/javascript" charset="utf-8"></script>
347 <script src="{{ static_url("notebook/js/keyboardmanager.js") }}" type="text/javascript" charset="utf-8"></script>
347 <script src="{{ static_url("notebook/js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
348 <script src="{{ static_url("notebook/js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
348 <script src="{{ static_url("notebook/js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script>
349 <script src="{{ static_url("notebook/js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script>
349 <script src="{{ static_url("notebook/js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script>
350 <script src="{{ static_url("notebook/js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script>
350 <script src="{{ static_url("notebook/js/tour.js") }}" type="text/javascript" charset="utf-8"></script>
351 <script src="{{ static_url("notebook/js/tour.js") }}" type="text/javascript" charset="utf-8"></script>
351
352
352 <script src="{{ static_url("notebook/js/config.js") }}" type="text/javascript" charset="utf-8"></script>
353 <script src="{{ static_url("notebook/js/config.js") }}" type="text/javascript" charset="utf-8"></script>
353 <script src="{{ static_url("notebook/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
354 <script src="{{ static_url("notebook/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
354
355
355 <script src="{{ static_url("notebook/js/contexthint.js") }}" charset="utf-8"></script>
356 <script src="{{ static_url("notebook/js/contexthint.js") }}" charset="utf-8"></script>
356
357
357 <script src="{{ static_url("notebook/js/celltoolbarpresets/default.js") }}" type="text/javascript" charset="utf-8"></script>
358 <script src="{{ static_url("notebook/js/celltoolbarpresets/default.js") }}" type="text/javascript" charset="utf-8"></script>
358 <script src="{{ static_url("notebook/js/celltoolbarpresets/rawcell.js") }}" type="text/javascript" charset="utf-8"></script>
359 <script src="{{ static_url("notebook/js/celltoolbarpresets/rawcell.js") }}" type="text/javascript" charset="utf-8"></script>
359 <script src="{{ static_url("notebook/js/celltoolbarpresets/slideshow.js") }}" type="text/javascript" charset="utf-8"></script>
360 <script src="{{ static_url("notebook/js/celltoolbarpresets/slideshow.js") }}" type="text/javascript" charset="utf-8"></script>
360
361
361 {% endblock %}
362 {% endblock %}
@@ -1,9 +1,10 b''
1 from .export import *
1 from .export import *
2 from .html import HTMLExporter
2 from .html import HTMLExporter
3 from .slides import SlidesExporter
3 from .slides import SlidesExporter
4 from .templateexporter import TemplateExporter
4 from .templateexporter import TemplateExporter
5 from .latex import LatexExporter
5 from .latex import LatexExporter
6 from .markdown import MarkdownExporter
6 from .markdown import MarkdownExporter
7 from .pdf import PDFExporter
7 from .python import PythonExporter
8 from .python import PythonExporter
8 from .rst import RSTExporter
9 from .rst import RSTExporter
9 from .exporter import Exporter
10 from .exporter import Exporter
@@ -1,179 +1,172 b''
1 """
1 """Module containing single call export functions."""
2 Module containing single call export functions.
3 """
4 #-----------------------------------------------------------------------------
5 # Copyright (c) 2013, the IPython Development Team.
6 #
7 # Distributed under the terms of the Modified BSD License.
8 #
9 # The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
11
2
12 #-----------------------------------------------------------------------------
3 # Copyright (c) IPython Development Team.
13 # Imports
4 # Distributed under the terms of the Modified BSD License.
14 #-----------------------------------------------------------------------------
15
5
16 from functools import wraps
6 from functools import wraps
17
7
18 from IPython.nbformat.v3.nbbase import NotebookNode
8 from IPython.nbformat.v3.nbbase import NotebookNode
19 from IPython.utils.decorators import undoc
9 from IPython.utils.decorators import undoc
20 from IPython.utils.py3compat import string_types
10 from IPython.utils.py3compat import string_types
21
11
22 from .exporter import Exporter
12 from .exporter import Exporter
23 from .templateexporter import TemplateExporter
13 from .templateexporter import TemplateExporter
24 from .html import HTMLExporter
14 from .html import HTMLExporter
25 from .slides import SlidesExporter
15 from .slides import SlidesExporter
26 from .latex import LatexExporter
16 from .latex import LatexExporter
17 from .pdf import PDFExporter
27 from .markdown import MarkdownExporter
18 from .markdown import MarkdownExporter
28 from .python import PythonExporter
19 from .python import PythonExporter
29 from .rst import RSTExporter
20 from .rst import RSTExporter
30
21
31 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
32 # Classes
23 # Classes
33 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
34
25
35 @undoc
26 @undoc
36 def DocDecorator(f):
27 def DocDecorator(f):
37
28
38 #Set docstring of function
29 #Set docstring of function
39 f.__doc__ = f.__doc__ + """
30 f.__doc__ = f.__doc__ + """
40 nb : :class:`~{nbnode_mod}.NotebookNode`
31 nb : :class:`~{nbnode_mod}.NotebookNode`
41 The notebook to export.
32 The notebook to export.
42 config : config (optional, keyword arg)
33 config : config (optional, keyword arg)
43 User configuration instance.
34 User configuration instance.
44 resources : dict (optional, keyword arg)
35 resources : dict (optional, keyword arg)
45 Resources used in the conversion process.
36 Resources used in the conversion process.
46
37
47 Returns
38 Returns
48 -------
39 -------
49 tuple- output, resources, exporter_instance
40 tuple- output, resources, exporter_instance
50 output : str
41 output : str
51 Jinja 2 output. This is the resulting converted notebook.
42 Jinja 2 output. This is the resulting converted notebook.
52 resources : dictionary
43 resources : dictionary
53 Dictionary of resources used prior to and during the conversion
44 Dictionary of resources used prior to and during the conversion
54 process.
45 process.
55 exporter_instance : Exporter
46 exporter_instance : Exporter
56 Instance of the Exporter class used to export the document. Useful
47 Instance of the Exporter class used to export the document. Useful
57 to caller because it provides a 'file_extension' property which
48 to caller because it provides a 'file_extension' property which
58 specifies what extension the output should be saved as.
49 specifies what extension the output should be saved as.
59
50
60 Notes
51 Notes
61 -----
52 -----
62 WARNING: API WILL CHANGE IN FUTURE RELEASES OF NBCONVERT
53 WARNING: API WILL CHANGE IN FUTURE RELEASES OF NBCONVERT
63 """.format(nbnode_mod=NotebookNode.__module__)
54 """.format(nbnode_mod=NotebookNode.__module__)
64
55
65 @wraps(f)
56 @wraps(f)
66 def decorator(*args, **kwargs):
57 def decorator(*args, **kwargs):
67 return f(*args, **kwargs)
58 return f(*args, **kwargs)
68
59
69 return decorator
60 return decorator
70
61
71
62
72 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
73 # Functions
64 # Functions
74 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
75
66
76 __all__ = [
67 __all__ = [
77 'export',
68 'export',
78 'export_html',
69 'export_html',
79 'export_custom',
70 'export_custom',
80 'export_slides',
71 'export_slides',
81 'export_latex',
72 'export_latex',
73 'export_pdf',
82 'export_markdown',
74 'export_markdown',
83 'export_python',
75 'export_python',
84 'export_rst',
76 'export_rst',
85 'export_by_name',
77 'export_by_name',
86 'get_export_names',
78 'get_export_names',
87 'ExporterNameError'
79 'ExporterNameError'
88 ]
80 ]
89
81
90
82
91 class ExporterNameError(NameError):
83 class ExporterNameError(NameError):
92 pass
84 pass
93
85
94 @DocDecorator
86 @DocDecorator
95 def export(exporter, nb, **kw):
87 def export(exporter, nb, **kw):
96 """
88 """
97 Export a notebook object using specific exporter class.
89 Export a notebook object using specific exporter class.
98
90
99 Parameters
91 Parameters
100 ----------
92 ----------
101 exporter : class:`~IPython.nbconvert.exporters.exporter.Exporter` class or instance
93 exporter : class:`~IPython.nbconvert.exporters.exporter.Exporter` class or instance
102 Class type or instance of the exporter that should be used. If the
94 Class type or instance of the exporter that should be used. If the
103 method initializes it's own instance of the class, it is ASSUMED that
95 method initializes it's own instance of the class, it is ASSUMED that
104 the class type provided exposes a constructor (``__init__``) with the same
96 the class type provided exposes a constructor (``__init__``) with the same
105 signature as the base Exporter class.
97 signature as the base Exporter class.
106 """
98 """
107
99
108 #Check arguments
100 #Check arguments
109 if exporter is None:
101 if exporter is None:
110 raise TypeError("Exporter is None")
102 raise TypeError("Exporter is None")
111 elif not isinstance(exporter, Exporter) and not issubclass(exporter, Exporter):
103 elif not isinstance(exporter, Exporter) and not issubclass(exporter, Exporter):
112 raise TypeError("exporter does not inherit from Exporter (base)")
104 raise TypeError("exporter does not inherit from Exporter (base)")
113 if nb is None:
105 if nb is None:
114 raise TypeError("nb is None")
106 raise TypeError("nb is None")
115
107
116 #Create the exporter
108 #Create the exporter
117 resources = kw.pop('resources', None)
109 resources = kw.pop('resources', None)
118 if isinstance(exporter, Exporter):
110 if isinstance(exporter, Exporter):
119 exporter_instance = exporter
111 exporter_instance = exporter
120 else:
112 else:
121 exporter_instance = exporter(**kw)
113 exporter_instance = exporter(**kw)
122
114
123 #Try to convert the notebook using the appropriate conversion function.
115 #Try to convert the notebook using the appropriate conversion function.
124 if isinstance(nb, NotebookNode):
116 if isinstance(nb, NotebookNode):
125 output, resources = exporter_instance.from_notebook_node(nb, resources)
117 output, resources = exporter_instance.from_notebook_node(nb, resources)
126 elif isinstance(nb, string_types):
118 elif isinstance(nb, string_types):
127 output, resources = exporter_instance.from_filename(nb, resources)
119 output, resources = exporter_instance.from_filename(nb, resources)
128 else:
120 else:
129 output, resources = exporter_instance.from_file(nb, resources)
121 output, resources = exporter_instance.from_file(nb, resources)
130 return output, resources
122 return output, resources
131
123
132 exporter_map = dict(
124 exporter_map = dict(
133 custom=TemplateExporter,
125 custom=TemplateExporter,
134 html=HTMLExporter,
126 html=HTMLExporter,
135 slides=SlidesExporter,
127 slides=SlidesExporter,
136 latex=LatexExporter,
128 latex=LatexExporter,
129 pdf=PDFExporter,
137 markdown=MarkdownExporter,
130 markdown=MarkdownExporter,
138 python=PythonExporter,
131 python=PythonExporter,
139 rst=RSTExporter,
132 rst=RSTExporter,
140 )
133 )
141
134
142 def _make_exporter(name, E):
135 def _make_exporter(name, E):
143 """make an export_foo function from a short key and Exporter class E"""
136 """make an export_foo function from a short key and Exporter class E"""
144 def _export(nb, **kw):
137 def _export(nb, **kw):
145 return export(E, nb, **kw)
138 return export(E, nb, **kw)
146 _export.__doc__ = """Export a notebook object to {0} format""".format(name)
139 _export.__doc__ = """Export a notebook object to {0} format""".format(name)
147 return _export
140 return _export
148
141
149 g = globals()
142 g = globals()
150
143
151 for name, E in exporter_map.items():
144 for name, E in exporter_map.items():
152 g['export_%s' % name] = DocDecorator(_make_exporter(name, E))
145 g['export_%s' % name] = DocDecorator(_make_exporter(name, E))
153
146
154 @DocDecorator
147 @DocDecorator
155 def export_by_name(format_name, nb, **kw):
148 def export_by_name(format_name, nb, **kw):
156 """
149 """
157 Export a notebook object to a template type by its name. Reflection
150 Export a notebook object to a template type by its name. Reflection
158 (Inspect) is used to find the template's corresponding explicit export
151 (Inspect) is used to find the template's corresponding explicit export
159 method defined in this module. That method is then called directly.
152 method defined in this module. That method is then called directly.
160
153
161 Parameters
154 Parameters
162 ----------
155 ----------
163 format_name : str
156 format_name : str
164 Name of the template style to export to.
157 Name of the template style to export to.
165 """
158 """
166
159
167 function_name = "export_" + format_name.lower()
160 function_name = "export_" + format_name.lower()
168
161
169 if function_name in globals():
162 if function_name in globals():
170 return globals()[function_name](nb, **kw)
163 return globals()[function_name](nb, **kw)
171 else:
164 else:
172 raise ExporterNameError("template for `%s` not found" % function_name)
165 raise ExporterNameError("template for `%s` not found" % function_name)
173
166
174
167
175 def get_export_names():
168 def get_export_names():
176 """Return a list of the currently supported export targets
169 """Return a list of the currently supported export targets
177
170
178 WARNING: API WILL CHANGE IN FUTURE RELEASES OF NBCONVERT"""
171 WARNING: API WILL CHANGE IN FUTURE RELEASES OF NBCONVERT"""
179 return sorted(exporter_map.keys())
172 return sorted(exporter_map.keys())
@@ -1,156 +1,141 b''
1 """
1 """Export to PDF via latex"""
2 Contains writer for writing nbconvert output to PDF.
2
3 """
3 # Copyright (c) IPython Development Team.
4 #-----------------------------------------------------------------------------
4 # Distributed under the terms of the Modified BSD License.
5 #Copyright (c) 2013, the IPython Development Team.
6 #
7 #Distributed under the terms of the Modified BSD License.
8 #
9 #The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
11
12 #-----------------------------------------------------------------------------
13 # Imports
14 #-----------------------------------------------------------------------------
15
5
16 import subprocess
6 import subprocess
17 import os
7 import os
18 import sys
8 import sys
19
9
20 from IPython.utils.traitlets import Integer, List, Bool
10 from IPython.utils.traitlets import Integer, List, Bool, Instance
11 from IPython.utils.tempdir import TemporaryWorkingDirectory
12 from .latex import LatexExporter
21
13
22 from .base import PostProcessorBase
23
14
24 #-----------------------------------------------------------------------------
15 class PDFExporter(LatexExporter):
25 # Classes
26 #-----------------------------------------------------------------------------
27 class PDFPostProcessor(PostProcessorBase):
28 """Writer designed to write to PDF files"""
16 """Writer designed to write to PDF files"""
29
17
30 latex_count = Integer(3, config=True, help="""
18 latex_count = Integer(3, config=True,
31 How many times pdflatex will be called.
19 help="How many times latex will be called."
32 """)
20 )
33
21
34 latex_command = List([u"pdflatex", u"{filename}"], config=True, help="""
22 latex_command = List([u"pdflatex", u"{filename}"], config=True,
35 Shell command used to compile PDF.""")
23 help="Shell command used to compile latex."
24 )
36
25
37 bib_command = List([u"bibtex", u"{filename}"], config=True, help="""
26 bib_command = List([u"bibtex", u"{filename}"], config=True,
38 Shell command used to run bibtex.""")
27 help="Shell command used to run bibtex."
28 )
39
29
40 verbose = Bool(False, config=True, help="""
30 verbose = Bool(False, config=True,
41 Whether or not to display the output of the compile call.
31 help="Whether to display the output of latex commands."
42 """)
32 )
43
33
44 temp_file_exts = List(['.aux', '.bbl', '.blg', '.idx', '.log', '.out'],
34 temp_file_exts = List(['.aux', '.bbl', '.blg', '.idx', '.log', '.out'], config=True,
45 config=True, help="""
35 help="File extensions of temp files to remove after running."
46 Filename extensions of temp files to remove after running.
36 )
47 """)
37
48 pdf_open = Bool(False, config=True, help="""
38 writer = Instance("IPython.nbconvert.writers.FilesWriter", args=())
49 Whether or not to open the pdf after the compile call.
50 """)
51
39
52 def run_command(self, command_list, filename, count, log_function):
40 def run_command(self, command_list, filename, count, log_function):
53 """Run command_list count times.
41 """Run command_list count times.
54
42
55 Parameters
43 Parameters
56 ----------
44 ----------
57 command_list : list
45 command_list : list
58 A list of args to provide to Popen. Each element of this
46 A list of args to provide to Popen. Each element of this
59 list will be interpolated with the filename to convert.
47 list will be interpolated with the filename to convert.
60 filename : unicode
48 filename : unicode
61 The name of the file to convert.
49 The name of the file to convert.
62 count : int
50 count : int
63 How many times to run the command.
51 How many times to run the command.
64
52
65 Returns
53 Returns
66 -------
54 -------
67 continue : bool
55 success : bool
68 A boolean indicating if the command was successful (True)
56 A boolean indicating if the command was successful (True)
69 or failed (False).
57 or failed (False).
70 """
58 """
71 command = [c.format(filename=filename) for c in command_list]
59 command = [c.format(filename=filename) for c in command_list]
72 #In windows and python 2.x there is a bug in subprocess.Popen and
60 #In windows and python 2.x there is a bug in subprocess.Popen and
73 # unicode commands are not supported
61 # unicode commands are not supported
74 if sys.platform == 'win32' and sys.version_info < (3,0):
62 if sys.platform == 'win32' and sys.version_info < (3,0):
75 #We must use cp1252 encoding for calling subprocess.Popen
63 #We must use cp1252 encoding for calling subprocess.Popen
76 #Note that sys.stdin.encoding and encoding.DEFAULT_ENCODING
64 #Note that sys.stdin.encoding and encoding.DEFAULT_ENCODING
77 # could be different (cp437 in case of dos console)
65 # could be different (cp437 in case of dos console)
78 command = [c.encode('cp1252') for c in command]
66 command = [c.encode('cp1252') for c in command]
79 times = 'time' if count == 1 else 'times'
67 times = 'time' if count == 1 else 'times'
80 self.log.info("Running %s %i %s: %s", command_list[0], count, times, command)
68 self.log.info("Running %s %i %s: %s", command_list[0], count, times, command)
81 with open(os.devnull, 'rb') as null:
69 with open(os.devnull, 'rb') as null:
82 stdout = subprocess.PIPE if not self.verbose else None
70 stdout = subprocess.PIPE if not self.verbose else None
83 for index in range(count):
71 for index in range(count):
84 p = subprocess.Popen(command, stdout=stdout, stdin=null)
72 p = subprocess.Popen(command, stdout=stdout, stdin=null)
85 out, err = p.communicate()
73 out, err = p.communicate()
86 if p.returncode:
74 if p.returncode:
87 if self.verbose:
75 if self.verbose:
88 # verbose means I didn't capture stdout with PIPE,
76 # verbose means I didn't capture stdout with PIPE,
89 # so it's already been displayed and `out` is None.
77 # so it's already been displayed and `out` is None.
90 out = u''
78 out = u''
91 else:
79 else:
92 out = out.decode('utf-8', 'replace')
80 out = out.decode('utf-8', 'replace')
93 log_function(command, out)
81 log_function(command, out)
94 return False # failure
82 return False # failure
95 return True # success
83 return True # success
96
84
97 def run_latex(self, filename):
85 def run_latex(self, filename):
98 """Run pdflatex self.latex_count times."""
86 """Run pdflatex self.latex_count times."""
99
87
100 def log_error(command, out):
88 def log_error(command, out):
101 self.log.critical(u"%s failed: %s\n%s", command[0], command, out)
89 self.log.critical(u"%s failed: %s\n%s", command[0], command, out)
102
90
103 return self.run_command(self.latex_command, filename,
91 return self.run_command(self.latex_command, filename,
104 self.latex_count, log_error)
92 self.latex_count, log_error)
105
93
106 def run_bib(self, filename):
94 def run_bib(self, filename):
107 """Run bibtex self.latex_count times."""
95 """Run bibtex self.latex_count times."""
108 filename = os.path.splitext(filename)[0]
96 filename = os.path.splitext(filename)[0]
109
97
110 def log_error(command, out):
98 def log_error(command, out):
111 self.log.warn('%s had problems, most likely because there were no citations',
99 self.log.warn('%s had problems, most likely because there were no citations',
112 command[0])
100 command[0])
113 self.log.debug(u"%s output: %s\n%s", command[0], command, out)
101 self.log.debug(u"%s output: %s\n%s", command[0], command, out)
114
102
115 return self.run_command(self.bib_command, filename, 1, log_error)
103 return self.run_command(self.bib_command, filename, 1, log_error)
116
104
117 def clean_temp_files(self, filename):
105 def clean_temp_files(self, filename):
118 """Remove temporary files created by pdflatex/bibtex."""
106 """Remove temporary files created by pdflatex/bibtex."""
119 self.log.info("Removing temporary LaTeX files")
107 self.log.info("Removing temporary LaTeX files")
120 filename = os.path.splitext(filename)[0]
108 filename = os.path.splitext(filename)[0]
121 for ext in self.temp_file_exts:
109 for ext in self.temp_file_exts:
122 try:
110 try:
123 os.remove(filename+ext)
111 os.remove(filename+ext)
124 except OSError:
112 except OSError:
125 pass
113 pass
126
114
127 def open_pdf(self, filename):
115 def from_notebook_node(self, nb, resources=None, **kw):
128 """Open the pdf in the default viewer."""
116 latex, resources = super(PDFExporter, self).from_notebook_node(
129 if sys.platform.startswith('darwin'):
117 nb, resources=resources, **kw
130 subprocess.call(('open', filename))
118 )
131 elif os.name == 'nt':
119 with TemporaryWorkingDirectory() as td:
132 os.startfile(filename)
120 notebook_name = "notebook"
133 elif os.name == 'posix':
121 tex_file = self.writer.write(latex, resources, notebook_name=notebook_name)
134 subprocess.call(('xdg-open', filename))
122 self.log.info("Building PDF")
135 return
123 rc = self.run_latex(tex_file)
136
124 if not rc:
137 def postprocess(self, filename):
125 rc = self.run_bib(tex_file)
138 """Build a PDF by running pdflatex and bibtex"""
126 if not rc:
139 self.log.info("Building PDF")
127 rc = self.run_latex(tex_file)
140 cont = self.run_latex(filename)
128
141 if cont:
129 pdf_file = notebook_name + '.pdf'
142 cont = self.run_bib(filename)
130 if not os.path.isfile(pdf_file):
143 else:
131 raise RuntimeError("PDF creating failed")
144 self.clean_temp_files(filename)
145 return
146 if cont:
147 cont = self.run_latex(filename)
148 self.clean_temp_files(filename)
149 filename = os.path.splitext(filename)[0]
150 if os.path.isfile(filename+'.pdf'):
151 self.log.info('PDF successfully created')
132 self.log.info('PDF successfully created')
152 if self.pdf_open:
133 with open(pdf_file, 'rb') as f:
153 self.log.info('Viewer called')
134 pdf_data = f.read()
154 self.open_pdf(filename+'.pdf')
135
155 return
136 # convert output extension to pdf
156
137 # the writer above required it to be tex
138 resources['output_extension'] = 'pdf'
139
140 return pdf_data, resources
141
@@ -1,323 +1,312 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """NBConvert is a utility for conversion of .ipynb files.
2 """NbConvert is a utility for conversion of .ipynb files.
3
3
4 Command-line interface for the NbConvert conversion utility.
4 Command-line interface for the NbConvert conversion utility.
5 """
5 """
6 #-----------------------------------------------------------------------------
7 #Copyright (c) 2013, the IPython Development Team.
8 #
9 #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
6
14 #-----------------------------------------------------------------------------
7 # Copyright (c) IPython Development Team.
15 #Imports
8 # Distributed under the terms of the Modified BSD License.
16 #-----------------------------------------------------------------------------
17
9
18 # Stdlib imports
19 from __future__ import print_function
10 from __future__ import print_function
20
11
21 import logging
12 import logging
22 import sys
13 import sys
23 import os
14 import os
24 import glob
15 import glob
25
16
26 # From IPython
27 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
17 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
28 from IPython.core.profiledir import ProfileDir
18 from IPython.core.profiledir import ProfileDir
29 from IPython.config import catch_config_error, Configurable
19 from IPython.config import catch_config_error, Configurable
30 from IPython.utils.traitlets import (
20 from IPython.utils.traitlets import (
31 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
21 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
32 )
22 )
33 from IPython.utils.importstring import import_item
23 from IPython.utils.importstring import import_item
34 from IPython.utils.text import dedent
24 from IPython.utils.text import dedent
35
25
36 from .exporters.export import get_export_names, exporter_map
26 from .exporters.export import get_export_names, exporter_map
37 from IPython.nbconvert import exporters, preprocessors, writers, postprocessors
27 from IPython.nbconvert import exporters, preprocessors, writers, postprocessors
38 from .utils.base import NbConvertBase
28 from .utils.base import NbConvertBase
39 from .utils.exceptions import ConversionException
29 from .utils.exceptions import ConversionException
40
30
41 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
42 #Classes and functions
32 #Classes and functions
43 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
44
34
45 class DottedOrNone(DottedObjectName):
35 class DottedOrNone(DottedObjectName):
46 """
36 """
47 A string holding a valid dotted object name in Python, such as A.b3._c
37 A string holding a valid dotted object name in Python, such as A.b3._c
48 Also allows for None type."""
38 Also allows for None type."""
49
39
50 default_value = u''
40 default_value = u''
51
41
52 def validate(self, obj, value):
42 def validate(self, obj, value):
53 if value is not None and len(value) > 0:
43 if value is not None and len(value) > 0:
54 return super(DottedOrNone, self).validate(obj, value)
44 return super(DottedOrNone, self).validate(obj, value)
55 else:
45 else:
56 return value
46 return value
57
47
58 nbconvert_aliases = {}
48 nbconvert_aliases = {}
59 nbconvert_aliases.update(base_aliases)
49 nbconvert_aliases.update(base_aliases)
60 nbconvert_aliases.update({
50 nbconvert_aliases.update({
61 'to' : 'NbConvertApp.export_format',
51 'to' : 'NbConvertApp.export_format',
62 'template' : 'TemplateExporter.template_file',
52 'template' : 'TemplateExporter.template_file',
63 'writer' : 'NbConvertApp.writer_class',
53 'writer' : 'NbConvertApp.writer_class',
64 'post': 'NbConvertApp.postprocessor_class',
54 'post': 'NbConvertApp.postprocessor_class',
65 'output': 'NbConvertApp.output_base',
55 'output': 'NbConvertApp.output_base',
66 'reveal-prefix': 'RevealHelpPreprocessor.url_prefix',
56 'reveal-prefix': 'RevealHelpPreprocessor.url_prefix',
67 })
57 })
68
58
69 nbconvert_flags = {}
59 nbconvert_flags = {}
70 nbconvert_flags.update(base_flags)
60 nbconvert_flags.update(base_flags)
71 nbconvert_flags.update({
61 nbconvert_flags.update({
72 'stdout' : (
62 'stdout' : (
73 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
63 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
74 "Write notebook output to stdout instead of files."
64 "Write notebook output to stdout instead of files."
75 )
65 )
76 })
66 })
77
67
78
68
79 class NbConvertApp(BaseIPythonApplication):
69 class NbConvertApp(BaseIPythonApplication):
80 """Application used to convert from notebook file type (``*.ipynb``)"""
70 """Application used to convert from notebook file type (``*.ipynb``)"""
81
71
82 name = 'ipython-nbconvert'
72 name = 'ipython-nbconvert'
83 aliases = nbconvert_aliases
73 aliases = nbconvert_aliases
84 flags = nbconvert_flags
74 flags = nbconvert_flags
85
75
86 def _log_level_default(self):
76 def _log_level_default(self):
87 return logging.INFO
77 return logging.INFO
88
78
89 def _classes_default(self):
79 def _classes_default(self):
90 classes = [NbConvertBase, ProfileDir]
80 classes = [NbConvertBase, ProfileDir]
91 for pkg in (exporters, preprocessors, writers, postprocessors):
81 for pkg in (exporters, preprocessors, writers, postprocessors):
92 for name in dir(pkg):
82 for name in dir(pkg):
93 cls = getattr(pkg, name)
83 cls = getattr(pkg, name)
94 if isinstance(cls, type) and issubclass(cls, Configurable):
84 if isinstance(cls, type) and issubclass(cls, Configurable):
95 classes.append(cls)
85 classes.append(cls)
96
86
97 return classes
87 return classes
98
88
99 description = Unicode(
89 description = Unicode(
100 u"""This application is used to convert notebook files (*.ipynb)
90 u"""This application is used to convert notebook files (*.ipynb)
101 to various other formats.
91 to various other formats.
102
92
103 WARNING: THE COMMANDLINE INTERFACE MAY CHANGE IN FUTURE RELEASES.""")
93 WARNING: THE COMMANDLINE INTERFACE MAY CHANGE IN FUTURE RELEASES.""")
104
94
105 output_base = Unicode('', config=True, help='''overwrite base name use for output files.
95 output_base = Unicode('', config=True, help='''overwrite base name use for output files.
106 can only be use when converting one notebook at a time.
96 can only be use when converting one notebook at a time.
107 ''')
97 ''')
108
98
109 examples = Unicode(u"""
99 examples = Unicode(u"""
110 The simplest way to use nbconvert is
100 The simplest way to use nbconvert is
111
101
112 > ipython nbconvert mynotebook.ipynb
102 > ipython nbconvert mynotebook.ipynb
113
103
114 which will convert mynotebook.ipynb to the default format (probably HTML).
104 which will convert mynotebook.ipynb to the default format (probably HTML).
115
105
116 You can specify the export format with `--to`.
106 You can specify the export format with `--to`.
117 Options include {0}
107 Options include {0}
118
108
119 > ipython nbconvert --to latex mynotebook.ipnynb
109 > ipython nbconvert --to latex mynotebook.ipnynb
120
110
121 Both HTML and LaTeX support multiple output templates. LaTeX includes
111 Both HTML and LaTeX support multiple output templates. LaTeX includes
122 'basic', 'book', and 'article'. HTML includes 'basic' and 'full'. You
112 'basic', 'book', and 'article'. HTML includes 'basic' and 'full'. You
123 can specify the flavor of the format used.
113 can specify the flavor of the format used.
124
114
125 > ipython nbconvert --to html --template basic mynotebook.ipynb
115 > ipython nbconvert --to html --template basic mynotebook.ipynb
126
116
127 You can also pipe the output to stdout, rather than a file
117 You can also pipe the output to stdout, rather than a file
128
118
129 > ipython nbconvert mynotebook.ipynb --stdout
119 > ipython nbconvert mynotebook.ipynb --stdout
130
120
131 A post-processor can be used to compile a PDF
121 PDF is generated via latex
132
122
133 > ipython nbconvert mynotebook.ipynb --to latex --post PDF
123 > ipython nbconvert mynotebook.ipynb --to pdf
134
124
135 You can get (and serve) a Reveal.js-powered slideshow
125 You can get (and serve) a Reveal.js-powered slideshow
136
126
137 > ipython nbconvert myslides.ipynb --to slides --post serve
127 > ipython nbconvert myslides.ipynb --to slides --post serve
138
128
139 Multiple notebooks can be given at the command line in a couple of
129 Multiple notebooks can be given at the command line in a couple of
140 different ways:
130 different ways:
141
131
142 > ipython nbconvert notebook*.ipynb
132 > ipython nbconvert notebook*.ipynb
143 > ipython nbconvert notebook1.ipynb notebook2.ipynb
133 > ipython nbconvert notebook1.ipynb notebook2.ipynb
144
134
145 or you can specify the notebooks list in a config file, containing::
135 or you can specify the notebooks list in a config file, containing::
146
136
147 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
137 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
148
138
149 > ipython nbconvert --config mycfg.py
139 > ipython nbconvert --config mycfg.py
150 """.format(get_export_names()))
140 """.format(get_export_names()))
151
141
152 # Writer specific variables
142 # Writer specific variables
153 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
143 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
154 help="""Instance of the writer class used to write the
144 help="""Instance of the writer class used to write the
155 results of the conversion.""")
145 results of the conversion.""")
156 writer_class = DottedObjectName('FilesWriter', config=True,
146 writer_class = DottedObjectName('FilesWriter', config=True,
157 help="""Writer class used to write the
147 help="""Writer class used to write the
158 results of the conversion""")
148 results of the conversion""")
159 writer_aliases = {'fileswriter': 'IPython.nbconvert.writers.files.FilesWriter',
149 writer_aliases = {'fileswriter': 'IPython.nbconvert.writers.files.FilesWriter',
160 'debugwriter': 'IPython.nbconvert.writers.debug.DebugWriter',
150 'debugwriter': 'IPython.nbconvert.writers.debug.DebugWriter',
161 'stdoutwriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
151 'stdoutwriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
162 writer_factory = Type()
152 writer_factory = Type()
163
153
164 def _writer_class_changed(self, name, old, new):
154 def _writer_class_changed(self, name, old, new):
165 if new.lower() in self.writer_aliases:
155 if new.lower() in self.writer_aliases:
166 new = self.writer_aliases[new.lower()]
156 new = self.writer_aliases[new.lower()]
167 self.writer_factory = import_item(new)
157 self.writer_factory = import_item(new)
168
158
169 # Post-processor specific variables
159 # Post-processor specific variables
170 postprocessor = Instance('IPython.nbconvert.postprocessors.base.PostProcessorBase',
160 postprocessor = Instance('IPython.nbconvert.postprocessors.base.PostProcessorBase',
171 help="""Instance of the PostProcessor class used to write the
161 help="""Instance of the PostProcessor class used to write the
172 results of the conversion.""")
162 results of the conversion.""")
173
163
174 postprocessor_class = DottedOrNone(config=True,
164 postprocessor_class = DottedOrNone(config=True,
175 help="""PostProcessor class used to write the
165 help="""PostProcessor class used to write the
176 results of the conversion""")
166 results of the conversion""")
177 postprocessor_aliases = {'pdf': 'IPython.nbconvert.postprocessors.pdf.PDFPostProcessor',
167 postprocessor_aliases = {'serve': 'IPython.nbconvert.postprocessors.serve.ServePostProcessor'}
178 'serve': 'IPython.nbconvert.postprocessors.serve.ServePostProcessor'}
179 postprocessor_factory = Type()
168 postprocessor_factory = Type()
180
169
181 def _postprocessor_class_changed(self, name, old, new):
170 def _postprocessor_class_changed(self, name, old, new):
182 if new.lower() in self.postprocessor_aliases:
171 if new.lower() in self.postprocessor_aliases:
183 new = self.postprocessor_aliases[new.lower()]
172 new = self.postprocessor_aliases[new.lower()]
184 if new:
173 if new:
185 self.postprocessor_factory = import_item(new)
174 self.postprocessor_factory = import_item(new)
186
175
187
176
188 # Other configurable variables
177 # Other configurable variables
189 export_format = CaselessStrEnum(get_export_names(),
178 export_format = CaselessStrEnum(get_export_names(),
190 default_value="html",
179 default_value="html",
191 config=True,
180 config=True,
192 help="""The export format to be used."""
181 help="""The export format to be used."""
193 )
182 )
194
183
195 notebooks = List([], config=True, help="""List of notebooks to convert.
184 notebooks = List([], config=True, help="""List of notebooks to convert.
196 Wildcards are supported.
185 Wildcards are supported.
197 Filenames passed positionally will be added to the list.
186 Filenames passed positionally will be added to the list.
198 """)
187 """)
199
188
200 @catch_config_error
189 @catch_config_error
201 def initialize(self, argv=None):
190 def initialize(self, argv=None):
202 self.init_syspath()
191 self.init_syspath()
203 super(NbConvertApp, self).initialize(argv)
192 super(NbConvertApp, self).initialize(argv)
204 self.init_notebooks()
193 self.init_notebooks()
205 self.init_writer()
194 self.init_writer()
206 self.init_postprocessor()
195 self.init_postprocessor()
207
196
208
197
209
198
210 def init_syspath(self):
199 def init_syspath(self):
211 """
200 """
212 Add the cwd to the sys.path ($PYTHONPATH)
201 Add the cwd to the sys.path ($PYTHONPATH)
213 """
202 """
214 sys.path.insert(0, os.getcwd())
203 sys.path.insert(0, os.getcwd())
215
204
216
205
217 def init_notebooks(self):
206 def init_notebooks(self):
218 """Construct the list of notebooks.
207 """Construct the list of notebooks.
219 If notebooks are passed on the command-line,
208 If notebooks are passed on the command-line,
220 they override notebooks specified in config files.
209 they override notebooks specified in config files.
221 Glob each notebook to replace notebook patterns with filenames.
210 Glob each notebook to replace notebook patterns with filenames.
222 """
211 """
223
212
224 # Specifying notebooks on the command-line overrides (rather than adds)
213 # Specifying notebooks on the command-line overrides (rather than adds)
225 # the notebook list
214 # the notebook list
226 if self.extra_args:
215 if self.extra_args:
227 patterns = self.extra_args
216 patterns = self.extra_args
228 else:
217 else:
229 patterns = self.notebooks
218 patterns = self.notebooks
230
219
231 # Use glob to replace all the notebook patterns with filenames.
220 # Use glob to replace all the notebook patterns with filenames.
232 filenames = []
221 filenames = []
233 for pattern in patterns:
222 for pattern in patterns:
234
223
235 # Use glob to find matching filenames. Allow the user to convert
224 # Use glob to find matching filenames. Allow the user to convert
236 # notebooks without having to type the extension.
225 # notebooks without having to type the extension.
237 globbed_files = glob.glob(pattern)
226 globbed_files = glob.glob(pattern)
238 globbed_files.extend(glob.glob(pattern + '.ipynb'))
227 globbed_files.extend(glob.glob(pattern + '.ipynb'))
239 if not globbed_files:
228 if not globbed_files:
240 self.log.warn("pattern %r matched no files", pattern)
229 self.log.warn("pattern %r matched no files", pattern)
241
230
242 for filename in globbed_files:
231 for filename in globbed_files:
243 if not filename in filenames:
232 if not filename in filenames:
244 filenames.append(filename)
233 filenames.append(filename)
245 self.notebooks = filenames
234 self.notebooks = filenames
246
235
247 def init_writer(self):
236 def init_writer(self):
248 """
237 """
249 Initialize the writer (which is stateless)
238 Initialize the writer (which is stateless)
250 """
239 """
251 self._writer_class_changed(None, self.writer_class, self.writer_class)
240 self._writer_class_changed(None, self.writer_class, self.writer_class)
252 self.writer = self.writer_factory(parent=self)
241 self.writer = self.writer_factory(parent=self)
253
242
254 def init_postprocessor(self):
243 def init_postprocessor(self):
255 """
244 """
256 Initialize the postprocessor (which is stateless)
245 Initialize the postprocessor (which is stateless)
257 """
246 """
258 self._postprocessor_class_changed(None, self.postprocessor_class,
247 self._postprocessor_class_changed(None, self.postprocessor_class,
259 self.postprocessor_class)
248 self.postprocessor_class)
260 if self.postprocessor_factory:
249 if self.postprocessor_factory:
261 self.postprocessor = self.postprocessor_factory(parent=self)
250 self.postprocessor = self.postprocessor_factory(parent=self)
262
251
263 def start(self):
252 def start(self):
264 """
253 """
265 Ran after initialization completed
254 Ran after initialization completed
266 """
255 """
267 super(NbConvertApp, self).start()
256 super(NbConvertApp, self).start()
268 self.convert_notebooks()
257 self.convert_notebooks()
269
258
270 def convert_notebooks(self):
259 def convert_notebooks(self):
271 """
260 """
272 Convert the notebooks in the self.notebook traitlet
261 Convert the notebooks in the self.notebook traitlet
273 """
262 """
274 # Export each notebook
263 # Export each notebook
275 conversion_success = 0
264 conversion_success = 0
276
265
277 if self.output_base != '' and len(self.notebooks) > 1:
266 if self.output_base != '' and len(self.notebooks) > 1:
278 self.log.error(
267 self.log.error(
279 """UsageError: --output flag or `NbConvertApp.output_base` config option
268 """UsageError: --output flag or `NbConvertApp.output_base` config option
280 cannot be used when converting multiple notebooks.
269 cannot be used when converting multiple notebooks.
281 """)
270 """)
282 self.exit(1)
271 self.exit(1)
283
272
284 exporter = exporter_map[self.export_format](config=self.config)
273 exporter = exporter_map[self.export_format](config=self.config)
285
274
286 for notebook_filename in self.notebooks:
275 for notebook_filename in self.notebooks:
287 self.log.info("Converting notebook %s to %s", notebook_filename, self.export_format)
276 self.log.info("Converting notebook %s to %s", notebook_filename, self.export_format)
288
277
289 # Get a unique key for the notebook and set it in the resources object.
278 # Get a unique key for the notebook and set it in the resources object.
290 basename = os.path.basename(notebook_filename)
279 basename = os.path.basename(notebook_filename)
291 notebook_name = basename[:basename.rfind('.')]
280 notebook_name = basename[:basename.rfind('.')]
292 if self.output_base:
281 if self.output_base:
293 notebook_name = self.output_base
282 notebook_name = self.output_base
294 resources = {}
283 resources = {}
295 resources['unique_key'] = notebook_name
284 resources['unique_key'] = notebook_name
296 resources['output_files_dir'] = '%s_files' % notebook_name
285 resources['output_files_dir'] = '%s_files' % notebook_name
297 self.log.info("Support files will be in %s", os.path.join(resources['output_files_dir'], ''))
286 self.log.info("Support files will be in %s", os.path.join(resources['output_files_dir'], ''))
298
287
299 # Try to export
288 # Try to export
300 try:
289 try:
301 output, resources = exporter.from_filename(notebook_filename, resources=resources)
290 output, resources = exporter.from_filename(notebook_filename, resources=resources)
302 except ConversionException as e:
291 except ConversionException as e:
303 self.log.error("Error while converting '%s'", notebook_filename,
292 self.log.error("Error while converting '%s'", notebook_filename,
304 exc_info=True)
293 exc_info=True)
305 self.exit(1)
294 self.exit(1)
306 else:
295 else:
307 write_resultes = self.writer.write(output, resources, notebook_name=notebook_name)
296 write_resultes = self.writer.write(output, resources, notebook_name=notebook_name)
308
297
309 #Post-process if post processor has been defined.
298 #Post-process if post processor has been defined.
310 if hasattr(self, 'postprocessor') and self.postprocessor:
299 if hasattr(self, 'postprocessor') and self.postprocessor:
311 self.postprocessor(write_resultes)
300 self.postprocessor(write_resultes)
312 conversion_success += 1
301 conversion_success += 1
313
302
314 # If nothing was converted successfully, help the user.
303 # If nothing was converted successfully, help the user.
315 if conversion_success == 0:
304 if conversion_success == 0:
316 self.print_help()
305 self.print_help()
317 sys.exit(-1)
306 sys.exit(-1)
318
307
319 #-----------------------------------------------------------------------------
308 #-----------------------------------------------------------------------------
320 # Main entry point
309 # Main entry point
321 #-----------------------------------------------------------------------------
310 #-----------------------------------------------------------------------------
322
311
323 launch_new_instance = NbConvertApp.launch_instance
312 launch_new_instance = NbConvertApp.launch_instance
@@ -1,8 +1,7 b''
1 from .base import PostProcessorBase
1 from .base import PostProcessorBase
2 from .pdf import PDFPostProcessor
3
2
4 # protect against unavailable tornado
3 # protect against unavailable tornado
5 try:
4 try:
6 from .serve import ServePostProcessor
5 from .serve import ServePostProcessor
7 except ImportError:
6 except ImportError:
8 pass
7 pass
@@ -1,211 +1,202 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Test NbConvertApp"""
2 """Test NbConvertApp"""
3
3
4 #-----------------------------------------------------------------------------
4 # Copyright (c) IPython Development Team.
5 # Copyright (C) 2013 The IPython Development Team
5 # Distributed under the terms of the Modified BSD License.
6 #
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
10
11 #-----------------------------------------------------------------------------
12 # Imports
13 #-----------------------------------------------------------------------------
14
6
15 import os
7 import os
16 import glob
8 import glob
17 import sys
9 import sys
18
10
19 from .base import TestsBase
11 from .base import TestsBase
12 from ..postprocessors import PostProcessorBase
20
13
21 import IPython.testing.tools as tt
14 import IPython.testing.tools as tt
22 from IPython.testing import decorators as dec
15 from IPython.testing import decorators as dec
23
16
24 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
25 # Classes and functions
18 # Classes and functions
26 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
27
20
21 class DummyPost(PostProcessorBase):
22 def postprocess(self, filename):
23 print("Dummy:%s" % filename)
24
28 class TestNbConvertApp(TestsBase):
25 class TestNbConvertApp(TestsBase):
29 """Collection of NbConvertApp tests"""
26 """Collection of NbConvertApp tests"""
30
27
31
28
32 def test_notebook_help(self):
29 def test_notebook_help(self):
33 """Will help show if no notebooks are specified?"""
30 """Will help show if no notebooks are specified?"""
34 with self.create_temp_cwd():
31 with self.create_temp_cwd():
35 out, err = self.call('nbconvert --log-level 0', ignore_return_code=True)
32 out, err = self.call('nbconvert --log-level 0', ignore_return_code=True)
36 self.assertIn("see '--help-all'", out)
33 self.assertIn("see '--help-all'", out)
37
34
38 def test_help_output(self):
35 def test_help_output(self):
39 """ipython nbconvert --help-all works"""
36 """ipython nbconvert --help-all works"""
40 tt.help_all_output_test('nbconvert')
37 tt.help_all_output_test('nbconvert')
41
38
42 def test_glob(self):
39 def test_glob(self):
43 """
40 """
44 Do search patterns work for notebook names?
41 Do search patterns work for notebook names?
45 """
42 """
46 with self.create_temp_cwd(['notebook*.ipynb']):
43 with self.create_temp_cwd(['notebook*.ipynb']):
47 self.call('nbconvert --to python *.ipynb --log-level 0')
44 self.call('nbconvert --to python *.ipynb --log-level 0')
48 assert os.path.isfile('notebook1.py')
45 assert os.path.isfile('notebook1.py')
49 assert os.path.isfile('notebook2.py')
46 assert os.path.isfile('notebook2.py')
50
47
51
48
52 def test_glob_subdir(self):
49 def test_glob_subdir(self):
53 """
50 """
54 Do search patterns work for subdirectory notebook names?
51 Do search patterns work for subdirectory notebook names?
55 """
52 """
56 with self.create_temp_cwd():
53 with self.create_temp_cwd():
57 self.copy_files_to(['notebook*.ipynb'], 'subdir/')
54 self.copy_files_to(['notebook*.ipynb'], 'subdir/')
58 self.call('nbconvert --to python --log-level 0 ' +
55 self.call('nbconvert --to python --log-level 0 ' +
59 os.path.join('subdir', '*.ipynb'))
56 os.path.join('subdir', '*.ipynb'))
60 assert os.path.isfile('notebook1.py')
57 assert os.path.isfile('notebook1.py')
61 assert os.path.isfile('notebook2.py')
58 assert os.path.isfile('notebook2.py')
62
59
63
60
64 def test_explicit(self):
61 def test_explicit(self):
65 """
62 """
66 Do explicit notebook names work?
63 Do explicit notebook names work?
67 """
64 """
68 with self.create_temp_cwd(['notebook*.ipynb']):
65 with self.create_temp_cwd(['notebook*.ipynb']):
69 self.call('nbconvert --log-level 0 --to python notebook2')
66 self.call('nbconvert --log-level 0 --to python notebook2')
70 assert not os.path.isfile('notebook1.py')
67 assert not os.path.isfile('notebook1.py')
71 assert os.path.isfile('notebook2.py')
68 assert os.path.isfile('notebook2.py')
72
69
73
70
74 @dec.onlyif_cmds_exist('pdflatex')
71 @dec.onlyif_cmds_exist('pdflatex')
75 @dec.onlyif_cmds_exist('pandoc')
72 @dec.onlyif_cmds_exist('pandoc')
76 def test_filename_spaces(self):
73 def test_filename_spaces(self):
77 """
74 """
78 Generate PDFs with graphics if notebooks have spaces in the name?
75 Generate PDFs with graphics if notebooks have spaces in the name?
79 """
76 """
80 with self.create_temp_cwd(['notebook2.ipynb']):
77 with self.create_temp_cwd(['notebook2.ipynb']):
81 os.rename('notebook2.ipynb', 'notebook with spaces.ipynb')
78 os.rename('notebook2.ipynb', 'notebook with spaces.ipynb')
82 self.call('nbconvert --log-level 0 --to latex '
79 self.call('nbconvert --log-level 0 --to pdf'
83 '"notebook with spaces" --post PDF '
80 ' "notebook with spaces"'
84 '--PDFPostProcessor.verbose=True')
81 ' --PDFExporter.latex_count=1'
85 assert os.path.isfile('notebook with spaces.tex')
82 ' --PDFExporter.verbose=True'
86 assert os.path.isdir('notebook with spaces_files')
83 )
87 assert os.path.isfile('notebook with spaces.pdf')
84 assert os.path.isfile('notebook with spaces.pdf')
88
85
89 @dec.onlyif_cmds_exist('pdflatex')
90 @dec.onlyif_cmds_exist('pandoc')
91 def test_post_processor(self):
86 def test_post_processor(self):
92 """
87 """Do post processors work?"""
93 Do post processors work?
94 """
95 with self.create_temp_cwd(['notebook1.ipynb']):
88 with self.create_temp_cwd(['notebook1.ipynb']):
96 self.call('nbconvert --log-level 0 --to latex notebook1 '
89 out, err = self.call('nbconvert --log-level 0 --to python notebook1 '
97 '--post PDF --PDFPostProcessor.verbose=True')
90 '--post IPython.nbconvert.tests.test_nbconvertapp.DummyPost')
98 assert os.path.isfile('notebook1.tex')
91 self.assertIn('Dummy:notebook1.py', out)
99 assert os.path.isfile('notebook1.pdf')
100
92
101 @dec.onlyif_cmds_exist('pandoc')
93 @dec.onlyif_cmds_exist('pandoc')
102 def test_spurious_cr(self):
94 def test_spurious_cr(self):
103 """Check for extra CR characters"""
95 """Check for extra CR characters"""
104 with self.create_temp_cwd(['notebook2.ipynb']):
96 with self.create_temp_cwd(['notebook2.ipynb']):
105 self.call('nbconvert --log-level 0 --to latex notebook2')
97 self.call('nbconvert --log-level 0 --to latex notebook2')
106 assert os.path.isfile('notebook2.tex')
98 assert os.path.isfile('notebook2.tex')
107 with open('notebook2.tex') as f:
99 with open('notebook2.tex') as f:
108 tex = f.read()
100 tex = f.read()
109 self.call('nbconvert --log-level 0 --to html notebook2')
101 self.call('nbconvert --log-level 0 --to html notebook2')
110 assert os.path.isfile('notebook2.html')
102 assert os.path.isfile('notebook2.html')
111 with open('notebook2.html') as f:
103 with open('notebook2.html') as f:
112 html = f.read()
104 html = f.read()
113 self.assertEqual(tex.count('\r'), tex.count('\r\n'))
105 self.assertEqual(tex.count('\r'), tex.count('\r\n'))
114 self.assertEqual(html.count('\r'), html.count('\r\n'))
106 self.assertEqual(html.count('\r'), html.count('\r\n'))
115
107
116 @dec.onlyif_cmds_exist('pandoc')
108 @dec.onlyif_cmds_exist('pandoc')
117 def test_png_base64_html_ok(self):
109 def test_png_base64_html_ok(self):
118 """Is embedded png data well formed in HTML?"""
110 """Is embedded png data well formed in HTML?"""
119 with self.create_temp_cwd(['notebook2.ipynb']):
111 with self.create_temp_cwd(['notebook2.ipynb']):
120 self.call('nbconvert --log-level 0 --to HTML '
112 self.call('nbconvert --log-level 0 --to HTML '
121 'notebook2.ipynb --template full')
113 'notebook2.ipynb --template full')
122 assert os.path.isfile('notebook2.html')
114 assert os.path.isfile('notebook2.html')
123 with open('notebook2.html') as f:
115 with open('notebook2.html') as f:
124 assert "data:image/png;base64,b'" not in f.read()
116 assert "data:image/png;base64,b'" not in f.read()
125
117
126 @dec.onlyif_cmds_exist('pandoc')
118 @dec.onlyif_cmds_exist('pandoc')
127 def test_template(self):
119 def test_template(self):
128 """
120 """
129 Do export templates work?
121 Do export templates work?
130 """
122 """
131 with self.create_temp_cwd(['notebook2.ipynb']):
123 with self.create_temp_cwd(['notebook2.ipynb']):
132 self.call('nbconvert --log-level 0 --to slides '
124 self.call('nbconvert --log-level 0 --to slides '
133 'notebook2.ipynb')
125 'notebook2.ipynb')
134 assert os.path.isfile('notebook2.slides.html')
126 assert os.path.isfile('notebook2.slides.html')
135 with open('notebook2.slides.html') as f:
127 with open('notebook2.slides.html') as f:
136 assert '/reveal.css' in f.read()
128 assert '/reveal.css' in f.read()
137
129
138
130
139 def test_glob_explicit(self):
131 def test_glob_explicit(self):
140 """
132 """
141 Can a search pattern be used along with matching explicit notebook names?
133 Can a search pattern be used along with matching explicit notebook names?
142 """
134 """
143 with self.create_temp_cwd(['notebook*.ipynb']):
135 with self.create_temp_cwd(['notebook*.ipynb']):
144 self.call('nbconvert --log-level 0 --to python '
136 self.call('nbconvert --log-level 0 --to python '
145 '*.ipynb notebook1.ipynb notebook2.ipynb')
137 '*.ipynb notebook1.ipynb notebook2.ipynb')
146 assert os.path.isfile('notebook1.py')
138 assert os.path.isfile('notebook1.py')
147 assert os.path.isfile('notebook2.py')
139 assert os.path.isfile('notebook2.py')
148
140
149
141
150 def test_explicit_glob(self):
142 def test_explicit_glob(self):
151 """
143 """
152 Can explicit notebook names be used and then a matching search pattern?
144 Can explicit notebook names be used and then a matching search pattern?
153 """
145 """
154 with self.create_temp_cwd(['notebook*.ipynb']):
146 with self.create_temp_cwd(['notebook*.ipynb']):
155 self.call('nbconvert --log-level 0 --to=python '
147 self.call('nbconvert --log-level 0 --to=python '
156 'notebook1.ipynb notebook2.ipynb *.ipynb')
148 'notebook1.ipynb notebook2.ipynb *.ipynb')
157 assert os.path.isfile('notebook1.py')
149 assert os.path.isfile('notebook1.py')
158 assert os.path.isfile('notebook2.py')
150 assert os.path.isfile('notebook2.py')
159
151
160
152
161 def test_default_config(self):
153 def test_default_config(self):
162 """
154 """
163 Does the default config work?
155 Does the default config work?
164 """
156 """
165 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py']):
157 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py']):
166 self.call('nbconvert --log-level 0')
158 self.call('nbconvert --log-level 0')
167 assert os.path.isfile('notebook1.py')
159 assert os.path.isfile('notebook1.py')
168 assert not os.path.isfile('notebook2.py')
160 assert not os.path.isfile('notebook2.py')
169
161
170
162
171 def test_override_config(self):
163 def test_override_config(self):
172 """
164 """
173 Can the default config be overriden?
165 Can the default config be overriden?
174 """
166 """
175 with self.create_temp_cwd(['notebook*.ipynb',
167 with self.create_temp_cwd(['notebook*.ipynb',
176 'ipython_nbconvert_config.py',
168 'ipython_nbconvert_config.py',
177 'override.py']):
169 'override.py']):
178 self.call('nbconvert --log-level 0 --config="override.py"')
170 self.call('nbconvert --log-level 0 --config="override.py"')
179 assert not os.path.isfile('notebook1.py')
171 assert not os.path.isfile('notebook1.py')
180 assert os.path.isfile('notebook2.py')
172 assert os.path.isfile('notebook2.py')
181
173
182 def test_accents_in_filename(self):
174 def test_accents_in_filename(self):
183 """
175 """
184 Can notebook names include accents?
176 Can notebook names include accents?
185 """
177 """
186 with self.create_temp_cwd():
178 with self.create_temp_cwd():
187 self.create_empty_notebook(u'nb1_análisis.ipynb')
179 self.create_empty_notebook(u'nb1_análisis.ipynb')
188 self.call('nbconvert --log-level 0 --to python nb1_*')
180 self.call('nbconvert --log-level 0 --to python nb1_*')
189 assert os.path.isfile(u'nb1_análisis.py')
181 assert os.path.isfile(u'nb1_análisis.py')
190
182
191 @dec.onlyif_cmds_exist('pdflatex', 'pandoc')
183 @dec.onlyif_cmds_exist('pdflatex', 'pandoc')
192 def test_filename_accent_pdf(self):
184 def test_filename_accent_pdf(self):
193 """
185 """
194 Generate PDFs if notebooks have an accent in their name?
186 Generate PDFs if notebooks have an accent in their name?
195 """
187 """
196 with self.create_temp_cwd():
188 with self.create_temp_cwd():
197 self.create_empty_notebook(u'nb1_análisis.ipynb')
189 self.create_empty_notebook(u'nb1_análisis.ipynb')
198 self.call('nbconvert --log-level 0 --to latex '
190 self.call('nbconvert --log-level 0 --to pdf "nb1_*"'
199 '"nb1_*" --post PDF '
191 ' --PDFExporter.latex_count=1'
200 '--PDFPostProcessor.verbose=True')
192 ' --PDFExporter.verbose=True')
201 assert os.path.isfile(u'nb1_análisis.tex')
202 assert os.path.isfile(u'nb1_análisis.pdf')
193 assert os.path.isfile(u'nb1_análisis.pdf')
203
194
204 def test_cwd_plugin(self):
195 def test_cwd_plugin(self):
205 """
196 """
206 Verify that an extension in the cwd can be imported.
197 Verify that an extension in the cwd can be imported.
207 """
198 """
208 with self.create_temp_cwd(['hello.py']):
199 with self.create_temp_cwd(['hello.py']):
209 self.create_empty_notebook(u'empty.ipynb')
200 self.create_empty_notebook(u'empty.ipynb')
210 self.call('nbconvert empty --to html --NbConvertApp.writer_class=\'hello.HelloWriter\'')
201 self.call('nbconvert empty --to html --NbConvertApp.writer_class=\'hello.HelloWriter\'')
211 assert os.path.isfile(u'hello.txt')
202 assert os.path.isfile(u'hello.txt')
@@ -1,115 +1,111 b''
1 """
1 """Contains writer for writing nbconvert output to filesystem."""
2 Contains writer for writing nbconvert output to filesystem.
3 """
4 #-----------------------------------------------------------------------------
5 #Copyright (c) 2013, the IPython Development Team.
6 #
7 #Distributed under the terms of the Modified BSD License.
8 #
9 #The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
11
2
12 #-----------------------------------------------------------------------------
3 # Copyright (c) IPython Development Team.
13 # Imports
4 # Distributed under the terms of the Modified BSD License.
14 #-----------------------------------------------------------------------------
15
5
16 import io
6 import io
17 import os
7 import os
18 import glob
8 import glob
19
9
20 from IPython.utils.traitlets import Unicode
10 from IPython.utils.traitlets import Unicode
21 from IPython.utils.path import link_or_copy
11 from IPython.utils.path import link_or_copy
12 from IPython.utils.py3compat import unicode_type
22
13
23 from .base import WriterBase
14 from .base import WriterBase
24
15
25 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
26 # Classes
17 # Classes
27 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
28
19
29 class FilesWriter(WriterBase):
20 class FilesWriter(WriterBase):
30 """Consumes nbconvert output and produces files."""
21 """Consumes nbconvert output and produces files."""
31
22
32
23
33 build_directory = Unicode("", config=True,
24 build_directory = Unicode("", config=True,
34 help="""Directory to write output to. Leave blank
25 help="""Directory to write output to. Leave blank
35 to output to the current directory""")
26 to output to the current directory""")
36
27
37
28
38 # Make sure that the output directory exists.
29 # Make sure that the output directory exists.
39 def _build_directory_changed(self, name, old, new):
30 def _build_directory_changed(self, name, old, new):
40 if new and not os.path.isdir(new):
31 if new and not os.path.isdir(new):
41 os.makedirs(new)
32 os.makedirs(new)
42
33
43
34
44 def __init__(self, **kw):
35 def __init__(self, **kw):
45 super(FilesWriter, self).__init__(**kw)
36 super(FilesWriter, self).__init__(**kw)
46 self._build_directory_changed('build_directory', self.build_directory,
37 self._build_directory_changed('build_directory', self.build_directory,
47 self.build_directory)
38 self.build_directory)
48
39
49 def _makedir(self, path):
40 def _makedir(self, path):
50 """Make a directory if it doesn't already exist"""
41 """Make a directory if it doesn't already exist"""
51 if path and not os.path.isdir(path):
42 if path and not os.path.isdir(path):
52 self.log.info("Making directory %s", path)
43 self.log.info("Making directory %s", path)
53 os.makedirs(path)
44 os.makedirs(path)
54
45
55 def write(self, output, resources, notebook_name=None, **kw):
46 def write(self, output, resources, notebook_name=None, **kw):
56 """
47 """
57 Consume and write Jinja output to the file system. Output directory
48 Consume and write Jinja output to the file system. Output directory
58 is set via the 'build_directory' variable of this instance (a
49 is set via the 'build_directory' variable of this instance (a
59 configurable).
50 configurable).
60
51
61 See base for more...
52 See base for more...
62 """
53 """
63
54
64 # Verify that a notebook name is provided.
55 # Verify that a notebook name is provided.
65 if notebook_name is None:
56 if notebook_name is None:
66 raise TypeError('notebook_name')
57 raise TypeError('notebook_name')
67
58
68 # Pull the extension and subdir from the resources dict.
59 # Pull the extension and subdir from the resources dict.
69 output_extension = resources.get('output_extension', None)
60 output_extension = resources.get('output_extension', None)
70
61
71 # Write all of the extracted resources to the destination directory.
62 # Write all of the extracted resources to the destination directory.
72 # NOTE: WE WRITE EVERYTHING AS-IF IT'S BINARY. THE EXTRACT FIG
63 # NOTE: WE WRITE EVERYTHING AS-IF IT'S BINARY. THE EXTRACT FIG
73 # PREPROCESSOR SHOULD HANDLE UNIX/WINDOWS LINE ENDINGS...
64 # PREPROCESSOR SHOULD HANDLE UNIX/WINDOWS LINE ENDINGS...
74 for filename, data in resources.get('outputs', {}).items():
65 for filename, data in resources.get('outputs', {}).items():
75
66
76 # Determine where to write the file to
67 # Determine where to write the file to
77 dest = os.path.join(self.build_directory, filename)
68 dest = os.path.join(self.build_directory, filename)
78 path = os.path.dirname(dest)
69 path = os.path.dirname(dest)
79 self._makedir(path)
70 self._makedir(path)
80
71
81 # Write file
72 # Write file
82 self.log.debug("Writing %i bytes to support file %s", len(data), dest)
73 self.log.debug("Writing %i bytes to support file %s", len(data), dest)
83 with io.open(dest, 'wb') as f:
74 with io.open(dest, 'wb') as f:
84 f.write(data)
75 f.write(data)
85
76
86 # Copy referenced files to output directory
77 # Copy referenced files to output directory
87 if self.build_directory:
78 if self.build_directory:
88 for filename in self.files:
79 for filename in self.files:
89
80
90 # Copy files that match search pattern
81 # Copy files that match search pattern
91 for matching_filename in glob.glob(filename):
82 for matching_filename in glob.glob(filename):
92
83
93 # Make sure folder exists.
84 # Make sure folder exists.
94 dest = os.path.join(self.build_directory, filename)
85 dest = os.path.join(self.build_directory, filename)
95 path = os.path.dirname(dest)
86 path = os.path.dirname(dest)
96 self._makedir(path)
87 self._makedir(path)
97
88
98 # Copy if destination is different.
89 # Copy if destination is different.
99 if not os.path.normpath(dest) == os.path.normpath(matching_filename):
90 if not os.path.normpath(dest) == os.path.normpath(matching_filename):
100 self.log.info("Linking %s -> %s", matching_filename, dest)
91 self.log.info("Linking %s -> %s", matching_filename, dest)
101 link_or_copy(matching_filename, dest)
92 link_or_copy(matching_filename, dest)
102
93
103 # Determine where to write conversion results.
94 # Determine where to write conversion results.
104 if output_extension is not None:
95 if output_extension is not None:
105 dest = notebook_name + '.' + output_extension
96 dest = notebook_name + '.' + output_extension
106 else:
97 else:
107 dest = notebook_name
98 dest = notebook_name
108 if self.build_directory:
99 if self.build_directory:
109 dest = os.path.join(self.build_directory, dest)
100 dest = os.path.join(self.build_directory, dest)
110
101
111 # Write conversion results.
102 # Write conversion results.
112 self.log.info("Writing %i bytes to %s", len(output), dest)
103 self.log.info("Writing %i bytes to %s", len(output), dest)
113 with io.open(dest, 'w', encoding='utf-8') as f:
104 if isinstance(output, unicode_type):
114 f.write(output)
105 with io.open(dest, 'w', encoding='utf-8') as f:
106 f.write(output)
107 else:
108 with io.open(dest, 'wb') as f:
109 f.write(output)
110
115 return dest
111 return dest
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now