##// END OF EJS Templates
move Python-specific help links to kernel_info...
Min RK -
Show More
@@ -1,386 +1,425 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'jquery',
5 'jquery',
6 'base/js/namespace',
6 'base/js/namespace',
7 'base/js/dialog',
7 'base/js/dialog',
8 'base/js/utils',
8 'base/js/utils',
9 'notebook/js/tour',
9 'notebook/js/tour',
10 'bootstrap',
10 'bootstrap',
11 'moment',
11 'moment',
12 ], function($, IPython, dialog, utils, tour, bootstrap, moment) {
12 ], function($, IPython, dialog, utils, tour, bootstrap, moment) {
13 "use strict";
13 "use strict";
14
14
15 var MenuBar = function (selector, options) {
15 var MenuBar = function (selector, options) {
16 /**
16 /**
17 * Constructor
17 * Constructor
18 *
18 *
19 * A MenuBar Class to generate the menubar of IPython notebook
19 * A MenuBar Class to generate the menubar of IPython notebook
20 *
20 *
21 * Parameters:
21 * Parameters:
22 * selector: string
22 * selector: string
23 * options: dictionary
23 * options: dictionary
24 * Dictionary of keyword arguments.
24 * Dictionary of keyword arguments.
25 * notebook: Notebook instance
25 * notebook: Notebook instance
26 * contents: ContentManager instance
26 * contents: ContentManager instance
27 * events: $(Events) instance
27 * events: $(Events) instance
28 * save_widget: SaveWidget instance
28 * save_widget: SaveWidget instance
29 * quick_help: QuickHelp instance
29 * quick_help: QuickHelp instance
30 * base_url : string
30 * base_url : string
31 * notebook_path : string
31 * notebook_path : string
32 * notebook_name : string
32 * notebook_name : string
33 */
33 */
34 options = options || {};
34 options = options || {};
35 this.base_url = options.base_url || utils.get_body_data("baseUrl");
35 this.base_url = options.base_url || utils.get_body_data("baseUrl");
36 this.selector = selector;
36 this.selector = selector;
37 this.notebook = options.notebook;
37 this.notebook = options.notebook;
38 this.contents = options.contents;
38 this.contents = options.contents;
39 this.events = options.events;
39 this.events = options.events;
40 this.save_widget = options.save_widget;
40 this.save_widget = options.save_widget;
41 this.quick_help = options.quick_help;
41 this.quick_help = options.quick_help;
42
42
43 try {
43 try {
44 this.tour = new tour.Tour(this.notebook, this.events);
44 this.tour = new tour.Tour(this.notebook, this.events);
45 } catch (e) {
45 } catch (e) {
46 this.tour = undefined;
46 this.tour = undefined;
47 console.log("Failed to instantiate Notebook Tour", e);
47 console.log("Failed to instantiate Notebook Tour", e);
48 }
48 }
49
49
50 if (this.selector !== undefined) {
50 if (this.selector !== undefined) {
51 this.element = $(selector);
51 this.element = $(selector);
52 this.style();
52 this.style();
53 this.bind_events();
53 this.bind_events();
54 }
54 }
55 };
55 };
56
56
57 // TODO: This has definitively nothing to do with style ...
57 // TODO: This has definitively nothing to do with style ...
58 MenuBar.prototype.style = function () {
58 MenuBar.prototype.style = function () {
59 var that = this;
59 var that = this;
60 this.element.find("li").click(function (event, ui) {
60 this.element.find("li").click(function (event, ui) {
61 // The selected cell loses focus when the menu is entered, so we
61 // The selected cell loses focus when the menu is entered, so we
62 // re-select it upon selection.
62 // re-select it upon selection.
63 var i = that.notebook.get_selected_index();
63 var i = that.notebook.get_selected_index();
64 that.notebook.select(i);
64 that.notebook.select(i);
65 }
65 }
66 );
66 );
67 };
67 };
68
68
69 MenuBar.prototype._nbconvert = function (format, download) {
69 MenuBar.prototype._nbconvert = function (format, download) {
70 download = download || false;
70 download = download || false;
71 var notebook_path = this.notebook.notebook_path;
71 var notebook_path = this.notebook.notebook_path;
72 var url = utils.url_join_encode(
72 var url = utils.url_join_encode(
73 this.base_url,
73 this.base_url,
74 'nbconvert',
74 'nbconvert',
75 format,
75 format,
76 notebook_path
76 notebook_path
77 ) + "?download=" + download.toString();
77 ) + "?download=" + download.toString();
78
78
79 var w = window.open()
79 var w = window.open();
80 if (this.notebook.dirty) {
80 if (this.notebook.dirty) {
81 this.notebook.save_notebook().then(function() {
81 this.notebook.save_notebook().then(function() {
82 w.location = url;
82 w.location = url;
83 });
83 });
84 } else {
84 } else {
85 w.location = url;
85 w.location = url;
86 }
86 }
87 };
87 };
88
88
89 MenuBar.prototype._size_header = function() {
89 MenuBar.prototype._size_header = function() {
90 /**
90 /**
91 * Update header spacer size.
91 * Update header spacer size.
92 */
92 */
93 this.events.trigger('resize-header.Page');
93 this.events.trigger('resize-header.Page');
94 };
94 };
95
95
96 MenuBar.prototype.bind_events = function () {
96 MenuBar.prototype.bind_events = function () {
97 /**
97 /**
98 * File
98 * File
99 */
99 */
100 var that = this;
100 var that = this;
101 this.element.find('#new_notebook').click(function () {
101 this.element.find('#new_notebook').click(function () {
102 var w = window.open();
102 var w = window.open();
103 // Create a new notebook in the same path as the current
103 // Create a new notebook in the same path as the current
104 // notebook's path.
104 // notebook's path.
105 var parent = utils.url_path_split(that.notebook.notebook_path)[0];
105 var parent = utils.url_path_split(that.notebook.notebook_path)[0];
106 that.contents.new_untitled(parent, {type: "notebook"}).then(
106 that.contents.new_untitled(parent, {type: "notebook"}).then(
107 function (data) {
107 function (data) {
108 w.location = utils.url_join_encode(
108 w.location = utils.url_join_encode(
109 that.base_url, 'notebooks', data.path
109 that.base_url, 'notebooks', data.path
110 );
110 );
111 },
111 },
112 function(error) {
112 function(error) {
113 w.close();
113 w.close();
114 dialog.modal({
114 dialog.modal({
115 title : 'Creating Notebook Failed',
115 title : 'Creating Notebook Failed',
116 body : "The error was: " + error.message,
116 body : "The error was: " + error.message,
117 buttons : {'OK' : {'class' : 'btn-primary'}}
117 buttons : {'OK' : {'class' : 'btn-primary'}}
118 });
118 });
119 }
119 }
120 );
120 );
121 });
121 });
122 this.element.find('#open_notebook').click(function () {
122 this.element.find('#open_notebook').click(function () {
123 var parent = utils.url_path_split(that.notebook.notebook_path)[0];
123 var parent = utils.url_path_split(that.notebook.notebook_path)[0];
124 window.open(utils.url_join_encode(that.base_url, 'tree', parent));
124 window.open(utils.url_join_encode(that.base_url, 'tree', parent));
125 });
125 });
126 this.element.find('#copy_notebook').click(function () {
126 this.element.find('#copy_notebook').click(function () {
127 that.notebook.copy_notebook();
127 that.notebook.copy_notebook();
128 return false;
128 return false;
129 });
129 });
130 this.element.find('#download_ipynb').click(function () {
130 this.element.find('#download_ipynb').click(function () {
131 var base_url = that.notebook.base_url;
131 var base_url = that.notebook.base_url;
132 var notebook_path = that.notebook.notebook_path;
132 var notebook_path = that.notebook.notebook_path;
133 if (that.notebook.dirty) {
133 if (that.notebook.dirty) {
134 that.notebook.save_notebook({async : false});
134 that.notebook.save_notebook({async : false});
135 }
135 }
136
136
137 var url = utils.url_join_encode(base_url, 'files', notebook_path);
137 var url = utils.url_join_encode(base_url, 'files', notebook_path);
138 window.open(url + '?download=1');
138 window.open(url + '?download=1');
139 });
139 });
140
140
141 this.element.find('#print_preview').click(function () {
141 this.element.find('#print_preview').click(function () {
142 that._nbconvert('html', false);
142 that._nbconvert('html', false);
143 });
143 });
144
144
145 this.element.find('#download_html').click(function () {
145 this.element.find('#download_html').click(function () {
146 that._nbconvert('html', true);
146 that._nbconvert('html', true);
147 });
147 });
148
148
149 this.element.find('#download_rst').click(function () {
149 this.element.find('#download_rst').click(function () {
150 that._nbconvert('rst', true);
150 that._nbconvert('rst', true);
151 });
151 });
152
152
153 this.element.find('#download_pdf').click(function () {
153 this.element.find('#download_pdf').click(function () {
154 that._nbconvert('pdf', true);
154 that._nbconvert('pdf', true);
155 });
155 });
156
156
157 this.element.find('#download_script').click(function () {
157 this.element.find('#download_script').click(function () {
158 that._nbconvert('script', true);
158 that._nbconvert('script', true);
159 });
159 });
160
160
161 this.element.find('#rename_notebook').click(function () {
161 this.element.find('#rename_notebook').click(function () {
162 that.save_widget.rename_notebook({notebook: that.notebook});
162 that.save_widget.rename_notebook({notebook: that.notebook});
163 });
163 });
164 this.element.find('#save_checkpoint').click(function () {
164 this.element.find('#save_checkpoint').click(function () {
165 that.notebook.save_checkpoint();
165 that.notebook.save_checkpoint();
166 });
166 });
167 this.element.find('#restore_checkpoint').click(function () {
167 this.element.find('#restore_checkpoint').click(function () {
168 });
168 });
169 this.element.find('#trust_notebook').click(function () {
169 this.element.find('#trust_notebook').click(function () {
170 that.notebook.trust_notebook();
170 that.notebook.trust_notebook();
171 });
171 });
172 this.events.on('trust_changed.Notebook', function (event, trusted) {
172 this.events.on('trust_changed.Notebook', function (event, trusted) {
173 if (trusted) {
173 if (trusted) {
174 that.element.find('#trust_notebook')
174 that.element.find('#trust_notebook')
175 .addClass("disabled")
175 .addClass("disabled")
176 .find("a").text("Trusted Notebook");
176 .find("a").text("Trusted Notebook");
177 } else {
177 } else {
178 that.element.find('#trust_notebook')
178 that.element.find('#trust_notebook')
179 .removeClass("disabled")
179 .removeClass("disabled")
180 .find("a").text("Trust Notebook");
180 .find("a").text("Trust Notebook");
181 }
181 }
182 });
182 });
183 this.element.find('#kill_and_exit').click(function () {
183 this.element.find('#kill_and_exit').click(function () {
184 var close_window = function () {
184 var close_window = function () {
185 /**
185 /**
186 * allow closing of new tabs in Chromium, impossible in FF
186 * allow closing of new tabs in Chromium, impossible in FF
187 */
187 */
188 window.open('', '_self', '');
188 window.open('', '_self', '');
189 window.close();
189 window.close();
190 };
190 };
191 // finish with close on success or failure
191 // finish with close on success or failure
192 that.notebook.session.delete(close_window, close_window);
192 that.notebook.session.delete(close_window, close_window);
193 });
193 });
194 // Edit
194 // Edit
195 this.element.find('#cut_cell').click(function () {
195 this.element.find('#cut_cell').click(function () {
196 that.notebook.cut_cell();
196 that.notebook.cut_cell();
197 });
197 });
198 this.element.find('#copy_cell').click(function () {
198 this.element.find('#copy_cell').click(function () {
199 that.notebook.copy_cell();
199 that.notebook.copy_cell();
200 });
200 });
201 this.element.find('#delete_cell').click(function () {
201 this.element.find('#delete_cell').click(function () {
202 that.notebook.delete_cell();
202 that.notebook.delete_cell();
203 });
203 });
204 this.element.find('#undelete_cell').click(function () {
204 this.element.find('#undelete_cell').click(function () {
205 that.notebook.undelete_cell();
205 that.notebook.undelete_cell();
206 });
206 });
207 this.element.find('#split_cell').click(function () {
207 this.element.find('#split_cell').click(function () {
208 that.notebook.split_cell();
208 that.notebook.split_cell();
209 });
209 });
210 this.element.find('#merge_cell_above').click(function () {
210 this.element.find('#merge_cell_above').click(function () {
211 that.notebook.merge_cell_above();
211 that.notebook.merge_cell_above();
212 });
212 });
213 this.element.find('#merge_cell_below').click(function () {
213 this.element.find('#merge_cell_below').click(function () {
214 that.notebook.merge_cell_below();
214 that.notebook.merge_cell_below();
215 });
215 });
216 this.element.find('#move_cell_up').click(function () {
216 this.element.find('#move_cell_up').click(function () {
217 that.notebook.move_cell_up();
217 that.notebook.move_cell_up();
218 });
218 });
219 this.element.find('#move_cell_down').click(function () {
219 this.element.find('#move_cell_down').click(function () {
220 that.notebook.move_cell_down();
220 that.notebook.move_cell_down();
221 });
221 });
222 this.element.find('#edit_nb_metadata').click(function () {
222 this.element.find('#edit_nb_metadata').click(function () {
223 that.notebook.edit_metadata({
223 that.notebook.edit_metadata({
224 notebook: that.notebook,
224 notebook: that.notebook,
225 keyboard_manager: that.notebook.keyboard_manager});
225 keyboard_manager: that.notebook.keyboard_manager});
226 });
226 });
227
227
228 // View
228 // View
229 this.element.find('#toggle_header').click(function () {
229 this.element.find('#toggle_header').click(function () {
230 $('div#header-container').toggle();
230 $('div#header-container').toggle();
231 that._size_header();
231 that._size_header();
232 });
232 });
233 this.element.find('#toggle_toolbar').click(function () {
233 this.element.find('#toggle_toolbar').click(function () {
234 $('div#maintoolbar').toggle();
234 $('div#maintoolbar').toggle();
235 that._size_header();
235 that._size_header();
236 });
236 });
237 // Insert
237 // Insert
238 this.element.find('#insert_cell_above').click(function () {
238 this.element.find('#insert_cell_above').click(function () {
239 that.notebook.insert_cell_above('code');
239 that.notebook.insert_cell_above('code');
240 that.notebook.select_prev();
240 that.notebook.select_prev();
241 });
241 });
242 this.element.find('#insert_cell_below').click(function () {
242 this.element.find('#insert_cell_below').click(function () {
243 that.notebook.insert_cell_below('code');
243 that.notebook.insert_cell_below('code');
244 that.notebook.select_next();
244 that.notebook.select_next();
245 });
245 });
246 // Cell
246 // Cell
247 this.element.find('#run_cell').click(function () {
247 this.element.find('#run_cell').click(function () {
248 that.notebook.execute_cell();
248 that.notebook.execute_cell();
249 });
249 });
250 this.element.find('#run_cell_select_below').click(function () {
250 this.element.find('#run_cell_select_below').click(function () {
251 that.notebook.execute_cell_and_select_below();
251 that.notebook.execute_cell_and_select_below();
252 });
252 });
253 this.element.find('#run_cell_insert_below').click(function () {
253 this.element.find('#run_cell_insert_below').click(function () {
254 that.notebook.execute_cell_and_insert_below();
254 that.notebook.execute_cell_and_insert_below();
255 });
255 });
256 this.element.find('#run_all_cells').click(function () {
256 this.element.find('#run_all_cells').click(function () {
257 that.notebook.execute_all_cells();
257 that.notebook.execute_all_cells();
258 });
258 });
259 this.element.find('#run_all_cells_above').click(function () {
259 this.element.find('#run_all_cells_above').click(function () {
260 that.notebook.execute_cells_above();
260 that.notebook.execute_cells_above();
261 });
261 });
262 this.element.find('#run_all_cells_below').click(function () {
262 this.element.find('#run_all_cells_below').click(function () {
263 that.notebook.execute_cells_below();
263 that.notebook.execute_cells_below();
264 });
264 });
265 this.element.find('#to_code').click(function () {
265 this.element.find('#to_code').click(function () {
266 that.notebook.to_code();
266 that.notebook.to_code();
267 });
267 });
268 this.element.find('#to_markdown').click(function () {
268 this.element.find('#to_markdown').click(function () {
269 that.notebook.to_markdown();
269 that.notebook.to_markdown();
270 });
270 });
271 this.element.find('#to_raw').click(function () {
271 this.element.find('#to_raw').click(function () {
272 that.notebook.to_raw();
272 that.notebook.to_raw();
273 });
273 });
274
274
275 this.element.find('#toggle_current_output').click(function () {
275 this.element.find('#toggle_current_output').click(function () {
276 that.notebook.toggle_output();
276 that.notebook.toggle_output();
277 });
277 });
278 this.element.find('#toggle_current_output_scroll').click(function () {
278 this.element.find('#toggle_current_output_scroll').click(function () {
279 that.notebook.toggle_output_scroll();
279 that.notebook.toggle_output_scroll();
280 });
280 });
281 this.element.find('#clear_current_output').click(function () {
281 this.element.find('#clear_current_output').click(function () {
282 that.notebook.clear_output();
282 that.notebook.clear_output();
283 });
283 });
284
284
285 this.element.find('#toggle_all_output').click(function () {
285 this.element.find('#toggle_all_output').click(function () {
286 that.notebook.toggle_all_output();
286 that.notebook.toggle_all_output();
287 });
287 });
288 this.element.find('#toggle_all_output_scroll').click(function () {
288 this.element.find('#toggle_all_output_scroll').click(function () {
289 that.notebook.toggle_all_output_scroll();
289 that.notebook.toggle_all_output_scroll();
290 });
290 });
291 this.element.find('#clear_all_output').click(function () {
291 this.element.find('#clear_all_output').click(function () {
292 that.notebook.clear_all_output();
292 that.notebook.clear_all_output();
293 });
293 });
294
294
295 // Kernel
295 // Kernel
296 this.element.find('#int_kernel').click(function () {
296 this.element.find('#int_kernel').click(function () {
297 that.notebook.kernel.interrupt();
297 that.notebook.kernel.interrupt();
298 });
298 });
299 this.element.find('#restart_kernel').click(function () {
299 this.element.find('#restart_kernel').click(function () {
300 that.notebook.restart_kernel();
300 that.notebook.restart_kernel();
301 });
301 });
302 this.element.find('#reconnect_kernel').click(function () {
302 this.element.find('#reconnect_kernel').click(function () {
303 that.notebook.kernel.reconnect();
303 that.notebook.kernel.reconnect();
304 });
304 });
305 // Help
305 // Help
306 if (this.tour) {
306 if (this.tour) {
307 this.element.find('#notebook_tour').click(function () {
307 this.element.find('#notebook_tour').click(function () {
308 that.tour.start();
308 that.tour.start();
309 });
309 });
310 } else {
310 } else {
311 this.element.find('#notebook_tour').addClass("disabled");
311 this.element.find('#notebook_tour').addClass("disabled");
312 }
312 }
313 this.element.find('#keyboard_shortcuts').click(function () {
313 this.element.find('#keyboard_shortcuts').click(function () {
314 that.quick_help.show_keyboard_shortcuts();
314 that.quick_help.show_keyboard_shortcuts();
315 });
315 });
316
316
317 this.update_restore_checkpoint(null);
317 this.update_restore_checkpoint(null);
318
318
319 this.events.on('checkpoints_listed.Notebook', function (event, data) {
319 this.events.on('checkpoints_listed.Notebook', function (event, data) {
320 that.update_restore_checkpoint(that.notebook.checkpoints);
320 that.update_restore_checkpoint(that.notebook.checkpoints);
321 });
321 });
322
322
323 this.events.on('checkpoint_created.Notebook', function (event, data) {
323 this.events.on('checkpoint_created.Notebook', function (event, data) {
324 that.update_restore_checkpoint(that.notebook.checkpoints);
324 that.update_restore_checkpoint(that.notebook.checkpoints);
325 });
325 });
326
326
327 this.events.on('notebook_loaded.Notebook', function() {
327 this.events.on('notebook_loaded.Notebook', function() {
328 var langinfo = that.notebook.metadata.language_info || {};
328 var langinfo = that.notebook.metadata.language_info || {};
329 that.update_nbconvert_script(langinfo);
329 that.update_nbconvert_script(langinfo);
330 });
330 });
331
331
332 this.events.on('kernel_ready.Kernel', function(event, data) {
332 this.events.on('kernel_ready.Kernel', function(event, data) {
333 var langinfo = data.kernel.info_reply.language_info || {};
333 var langinfo = data.kernel.info_reply.language_info || {};
334 that.update_nbconvert_script(langinfo);
334 that.update_nbconvert_script(langinfo);
335 that.add_kernel_help_links(data.kernel.info_reply.help_links || []);
335 });
336 });
336 };
337 };
337
338
338 MenuBar.prototype.update_restore_checkpoint = function(checkpoints) {
339 MenuBar.prototype.update_restore_checkpoint = function(checkpoints) {
339 var ul = this.element.find("#restore_checkpoint").find("ul");
340 var ul = this.element.find("#restore_checkpoint").find("ul");
340 ul.empty();
341 ul.empty();
341 if (!checkpoints || checkpoints.length === 0) {
342 if (!checkpoints || checkpoints.length === 0) {
342 ul.append(
343 ul.append(
343 $("<li/>")
344 $("<li/>")
344 .addClass("disabled")
345 .addClass("disabled")
345 .append(
346 .append(
346 $("<a/>")
347 $("<a/>")
347 .text("No checkpoints")
348 .text("No checkpoints")
348 )
349 )
349 );
350 );
350 return;
351 return;
351 }
352 }
352
353
353 var that = this;
354 var that = this;
354 checkpoints.map(function (checkpoint) {
355 checkpoints.map(function (checkpoint) {
355 var d = new Date(checkpoint.last_modified);
356 var d = new Date(checkpoint.last_modified);
356 ul.append(
357 ul.append(
357 $("<li/>").append(
358 $("<li/>").append(
358 $("<a/>")
359 $("<a/>")
359 .attr("href", "#")
360 .attr("href", "#")
360 .text(moment(d).format("LLLL"))
361 .text(moment(d).format("LLLL"))
361 .click(function () {
362 .click(function () {
362 that.notebook.restore_checkpoint_dialog(checkpoint);
363 that.notebook.restore_checkpoint_dialog(checkpoint);
363 })
364 })
364 )
365 )
365 );
366 );
366 });
367 });
367 };
368 };
368
369
369 MenuBar.prototype.update_nbconvert_script = function(langinfo) {
370 MenuBar.prototype.update_nbconvert_script = function(langinfo) {
370 /**
371 /**
371 * Set the 'Download as foo' menu option for the relevant language.
372 * Set the 'Download as foo' menu option for the relevant language.
372 */
373 */
373 var el = this.element.find('#download_script');
374 var el = this.element.find('#download_script');
374 var that = this;
375 var that = this;
375
376
376 // Set menu entry text to e.g. "Python (.py)"
377 // Set menu entry text to e.g. "Python (.py)"
377 var langname = (langinfo.name || 'Script')
378 var langname = (langinfo.name || 'Script');
378 langname = langname.charAt(0).toUpperCase()+langname.substr(1) // Capitalise
379 langname = langname.charAt(0).toUpperCase()+langname.substr(1); // Capitalise
379 el.find('a').text(langname + ' ('+(langinfo.file_extension || 'txt')+')');
380 el.find('a').text(langname + ' ('+(langinfo.file_extension || 'txt')+')');
380 };
381 };
381
382
383 MenuBar.prototype.add_kernel_help_links = function(help_links) {
384 /** add links from kernel_info to the help menu */
385 var divider = $("#kernel-help-links");
386 if (divider.length === 0) {
387 // insert kernel help section above about link
388 var about = $("#notebook_about").parent();
389 divider = $("<li>")
390 .attr('id', "kernel-help-links")
391 .addClass('divider');
392 about.prev().before(divider);
393 }
394 // remove previous entries
395 while (!divider.next().hasClass('divider')) {
396 divider.next().remove();
397 }
398 if (help_links.length === 0) {
399 // no help links, remove the divider
400 divider.remove();
401 return;
402 }
403 var cursor = divider;
404 help_links.map(function (link) {
405 cursor.after($("<li>")
406 .append($("<a>")
407 .attr('target', '_blank')
408 .attr('title', 'Opens in a new window')
409 .attr('href', link.url)
410 .text(link.text)
411 .append($("<i>")
412 .addClass("fa fa-external-link menu-icon pull-right")
413 )
414 )
415 );
416 cursor = cursor.next();
417 });
418
419 };
420
382 // Backwards compatability.
421 // Backwards compatability.
383 IPython.MenuBar = MenuBar;
422 IPython.MenuBar = MenuBar;
384
423
385 return {'MenuBar': MenuBar};
424 return {'MenuBar': MenuBar};
386 });
425 });
@@ -1,326 +1,318 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 <link rel="stylesheet" href="" id='kernel-css' type="text/css" />
20 <link rel="stylesheet" href="" id='kernel-css' type="text/css" />
21
21
22 {% endblock %}
22 {% endblock %}
23
23
24 {% block params %}
24 {% block params %}
25
25
26 data-project="{{project}}"
26 data-project="{{project}}"
27 data-base-url="{{base_url}}"
27 data-base-url="{{base_url}}"
28 data-ws-url="{{ws_url}}"
28 data-ws-url="{{ws_url}}"
29 data-notebook-name="{{notebook_name}}"
29 data-notebook-name="{{notebook_name}}"
30 data-notebook-path="{{notebook_path}}"
30 data-notebook-path="{{notebook_path}}"
31 class="notebook_app"
31 class="notebook_app"
32
32
33 {% endblock %}
33 {% endblock %}
34
34
35
35
36 {% block headercontainer %}
36 {% block headercontainer %}
37
37
38
38
39 <span id="save_widget" class="pull-left save_widget">
39 <span id="save_widget" class="pull-left save_widget">
40 <span class="filename"></span>
40 <span class="filename"></span>
41 <span class="checkpoint_status"></span>
41 <span class="checkpoint_status"></span>
42 <span class="autosave_status"></span>
42 <span class="autosave_status"></span>
43 </span>
43 </span>
44
44
45 <span id="kernel_logo_widget">
45 <span id="kernel_logo_widget">
46 <img class="current_kernel_logo" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"/>
46 <img class="current_kernel_logo" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"/>
47 </span>
47 </span>
48
48
49 {% endblock headercontainer %}
49 {% endblock headercontainer %}
50
50
51 {% block header %}
51 {% block header %}
52 <div id="menubar-container" class="container">
52 <div id="menubar-container" class="container">
53 <div id="menubar">
53 <div id="menubar">
54 <div id="menus" class="navbar navbar-default" role="navigation">
54 <div id="menus" class="navbar navbar-default" role="navigation">
55 <div class="container-fluid">
55 <div class="container-fluid">
56 <button type="button" class="btn btn-default navbar-btn navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
56 <button type="button" class="btn btn-default navbar-btn navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
57 <i class="fa fa-bars"></i>
57 <i class="fa fa-bars"></i>
58 <span class="navbar-text">Menu</span>
58 <span class="navbar-text">Menu</span>
59 </button>
59 </button>
60 <p id="kernel_indicator" class="navbar-text">
60 <p id="kernel_indicator" class="navbar-text">
61 <span class="kernel_indicator_name">Kernel</span>
61 <span class="kernel_indicator_name">Kernel</span>
62 <i id="kernel_indicator_icon"></i>
62 <i id="kernel_indicator_icon"></i>
63 </p>
63 </p>
64 <i id="modal_indicator" class="navbar-text"></i>
64 <i id="modal_indicator" class="navbar-text"></i>
65 <span id="notification_area"></span>
65 <span id="notification_area"></span>
66 <div class="navbar-collapse collapse">
66 <div class="navbar-collapse collapse">
67 <ul class="nav navbar-nav">
67 <ul class="nav navbar-nav">
68 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
68 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
69 <ul id="file_menu" class="dropdown-menu">
69 <ul id="file_menu" class="dropdown-menu">
70 <li id="new_notebook"
70 <li id="new_notebook"
71 title="Make a new notebook (Opens a new window)">
71 title="Make a new notebook (Opens a new window)">
72 <a href="#">New</a></li>
72 <a href="#">New</a></li>
73 <li id="open_notebook"
73 <li id="open_notebook"
74 title="Opens a new window with the Dashboard view">
74 title="Opens a new window with the Dashboard view">
75 <a href="#">Open...</a></li>
75 <a href="#">Open...</a></li>
76 <!-- <hr/> -->
76 <!-- <hr/> -->
77 <li class="divider"></li>
77 <li class="divider"></li>
78 <li id="copy_notebook"
78 <li id="copy_notebook"
79 title="Open a copy of this notebook's contents and start a new kernel">
79 title="Open a copy of this notebook's contents and start a new kernel">
80 <a href="#">Make a Copy...</a></li>
80 <a href="#">Make a Copy...</a></li>
81 <li id="rename_notebook"><a href="#">Rename...</a></li>
81 <li id="rename_notebook"><a href="#">Rename...</a></li>
82 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
82 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
83 <!-- <hr/> -->
83 <!-- <hr/> -->
84 <li class="divider"></li>
84 <li class="divider"></li>
85 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
85 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
86 <ul class="dropdown-menu">
86 <ul class="dropdown-menu">
87 <li><a href="#"></a></li>
87 <li><a href="#"></a></li>
88 <li><a href="#"></a></li>
88 <li><a href="#"></a></li>
89 <li><a href="#"></a></li>
89 <li><a href="#"></a></li>
90 <li><a href="#"></a></li>
90 <li><a href="#"></a></li>
91 <li><a href="#"></a></li>
91 <li><a href="#"></a></li>
92 </ul>
92 </ul>
93 </li>
93 </li>
94 <li class="divider"></li>
94 <li class="divider"></li>
95 <li id="print_preview"><a href="#">Print Preview</a></li>
95 <li id="print_preview"><a href="#">Print Preview</a></li>
96 <li class="dropdown-submenu"><a href="#">Download as</a>
96 <li class="dropdown-submenu"><a href="#">Download as</a>
97 <ul class="dropdown-menu">
97 <ul class="dropdown-menu">
98 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
98 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
99 <li id="download_script"><a href="#">Script</a></li>
99 <li id="download_script"><a href="#">Script</a></li>
100 <li id="download_html"><a href="#">HTML (.html)</a></li>
100 <li id="download_html"><a href="#">HTML (.html)</a></li>
101 <li id="download_rst"><a href="#">reST (.rst)</a></li>
101 <li id="download_rst"><a href="#">reST (.rst)</a></li>
102 <li id="download_pdf"><a href="#">PDF (.pdf)</a></li>
102 <li id="download_pdf"><a href="#">PDF (.pdf)</a></li>
103 </ul>
103 </ul>
104 </li>
104 </li>
105 <li class="divider"></li>
105 <li class="divider"></li>
106 <li id="trust_notebook"
106 <li id="trust_notebook"
107 title="Trust the output of this notebook">
107 title="Trust the output of this notebook">
108 <a href="#" >Trust Notebook</a></li>
108 <a href="#" >Trust Notebook</a></li>
109 <li class="divider"></li>
109 <li class="divider"></li>
110 <li id="kill_and_exit"
110 <li id="kill_and_exit"
111 title="Shutdown this notebook's kernel, and close this window">
111 title="Shutdown this notebook's kernel, and close this window">
112 <a href="#" >Close and halt</a></li>
112 <a href="#" >Close and halt</a></li>
113 </ul>
113 </ul>
114 </li>
114 </li>
115 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
115 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
116 <ul id="edit_menu" class="dropdown-menu">
116 <ul id="edit_menu" class="dropdown-menu">
117 <li id="cut_cell"><a href="#">Cut Cell</a></li>
117 <li id="cut_cell"><a href="#">Cut Cell</a></li>
118 <li id="copy_cell"><a href="#">Copy Cell</a></li>
118 <li id="copy_cell"><a href="#">Copy Cell</a></li>
119 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
119 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
120 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
120 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
121 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
121 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
122 <li id="delete_cell"><a href="#">Delete Cell</a></li>
122 <li id="delete_cell"><a href="#">Delete Cell</a></li>
123 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
123 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
124 <li class="divider"></li>
124 <li class="divider"></li>
125 <li id="split_cell"><a href="#">Split Cell</a></li>
125 <li id="split_cell"><a href="#">Split Cell</a></li>
126 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
126 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
127 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
127 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
128 <li class="divider"></li>
128 <li class="divider"></li>
129 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
129 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
130 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
130 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
131 <li class="divider"></li>
131 <li class="divider"></li>
132 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
132 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
133 </ul>
133 </ul>
134 </li>
134 </li>
135 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
135 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
136 <ul id="view_menu" class="dropdown-menu">
136 <ul id="view_menu" class="dropdown-menu">
137 <li id="toggle_header"
137 <li id="toggle_header"
138 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
138 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
139 <a href="#">Toggle Header</a></li>
139 <a href="#">Toggle Header</a></li>
140 <li id="toggle_toolbar"
140 <li id="toggle_toolbar"
141 title="Show/Hide the action icons (below menu bar)">
141 title="Show/Hide the action icons (below menu bar)">
142 <a href="#">Toggle Toolbar</a></li>
142 <a href="#">Toggle Toolbar</a></li>
143 </ul>
143 </ul>
144 </li>
144 </li>
145 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
145 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
146 <ul id="insert_menu" class="dropdown-menu">
146 <ul id="insert_menu" class="dropdown-menu">
147 <li id="insert_cell_above"
147 <li id="insert_cell_above"
148 title="Insert an empty Code cell above the currently active cell">
148 title="Insert an empty Code cell above the currently active cell">
149 <a href="#">Insert Cell Above</a></li>
149 <a href="#">Insert Cell Above</a></li>
150 <li id="insert_cell_below"
150 <li id="insert_cell_below"
151 title="Insert an empty Code cell below the currently active cell">
151 title="Insert an empty Code cell below the currently active cell">
152 <a href="#">Insert Cell Below</a></li>
152 <a href="#">Insert Cell Below</a></li>
153 </ul>
153 </ul>
154 </li>
154 </li>
155 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
155 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
156 <ul id="cell_menu" class="dropdown-menu">
156 <ul id="cell_menu" class="dropdown-menu">
157 <li id="run_cell" title="Run this cell, and move cursor to the next one">
157 <li id="run_cell" title="Run this cell, and move cursor to the next one">
158 <a href="#">Run</a></li>
158 <a href="#">Run</a></li>
159 <li id="run_cell_select_below" title="Run this cell, select below">
159 <li id="run_cell_select_below" title="Run this cell, select below">
160 <a href="#">Run and Select Below</a></li>
160 <a href="#">Run and Select Below</a></li>
161 <li id="run_cell_insert_below" title="Run this cell, insert below">
161 <li id="run_cell_insert_below" title="Run this cell, insert below">
162 <a href="#">Run and Insert Below</a></li>
162 <a href="#">Run and Insert Below</a></li>
163 <li id="run_all_cells" title="Run all cells in the notebook">
163 <li id="run_all_cells" title="Run all cells in the notebook">
164 <a href="#">Run All</a></li>
164 <a href="#">Run All</a></li>
165 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
165 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
166 <a href="#">Run All Above</a></li>
166 <a href="#">Run All Above</a></li>
167 <li id="run_all_cells_below" title="Run this cell and all cells below it">
167 <li id="run_all_cells_below" title="Run this cell and all cells below it">
168 <a href="#">Run All Below</a></li>
168 <a href="#">Run All Below</a></li>
169 <li class="divider"></li>
169 <li class="divider"></li>
170 <li id="change_cell_type" class="dropdown-submenu"
170 <li id="change_cell_type" class="dropdown-submenu"
171 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
171 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
172 <a href="#">Cell Type</a>
172 <a href="#">Cell Type</a>
173 <ul class="dropdown-menu">
173 <ul class="dropdown-menu">
174 <li id="to_code"
174 <li id="to_code"
175 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
175 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
176 <a href="#">Code</a></li>
176 <a href="#">Code</a></li>
177 <li id="to_markdown"
177 <li id="to_markdown"
178 title="Contents will be rendered as HTML and serve as explanatory text">
178 title="Contents will be rendered as HTML and serve as explanatory text">
179 <a href="#">Markdown</a></li>
179 <a href="#">Markdown</a></li>
180 <li id="to_raw"
180 <li id="to_raw"
181 title="Contents will pass through nbconvert unmodified">
181 title="Contents will pass through nbconvert unmodified">
182 <a href="#">Raw NBConvert</a></li>
182 <a href="#">Raw NBConvert</a></li>
183 </ul>
183 </ul>
184 </li>
184 </li>
185 <li class="divider"></li>
185 <li class="divider"></li>
186 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
186 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
187 <ul class="dropdown-menu">
187 <ul class="dropdown-menu">
188 <li id="toggle_current_output"
188 <li id="toggle_current_output"
189 title="Hide/Show the output of the current cell">
189 title="Hide/Show the output of the current cell">
190 <a href="#">Toggle</a>
190 <a href="#">Toggle</a>
191 </li>
191 </li>
192 <li id="toggle_current_output_scroll"
192 <li id="toggle_current_output_scroll"
193 title="Scroll the output of the current cell">
193 title="Scroll the output of the current cell">
194 <a href="#">Toggle Scrolling</a>
194 <a href="#">Toggle Scrolling</a>
195 </li>
195 </li>
196 <li id="clear_current_output"
196 <li id="clear_current_output"
197 title="Clear the output of the current cell">
197 title="Clear the output of the current cell">
198 <a href="#">Clear</a>
198 <a href="#">Clear</a>
199 </li>
199 </li>
200 </ul>
200 </ul>
201 </li>
201 </li>
202 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
202 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
203 <ul class="dropdown-menu">
203 <ul class="dropdown-menu">
204 <li id="toggle_all_output"
204 <li id="toggle_all_output"
205 title="Hide/Show the output of all cells">
205 title="Hide/Show the output of all cells">
206 <a href="#">Toggle</a>
206 <a href="#">Toggle</a>
207 </li>
207 </li>
208 <li id="toggle_all_output_scroll"
208 <li id="toggle_all_output_scroll"
209 title="Scroll the output of all cells">
209 title="Scroll the output of all cells">
210 <a href="#">Toggle Scrolling</a>
210 <a href="#">Toggle Scrolling</a>
211 </li>
211 </li>
212 <li id="clear_all_output"
212 <li id="clear_all_output"
213 title="Clear the output of all cells">
213 title="Clear the output of all cells">
214 <a href="#">Clear</a>
214 <a href="#">Clear</a>
215 </li>
215 </li>
216 </ul>
216 </ul>
217 </li>
217 </li>
218 </ul>
218 </ul>
219 </li>
219 </li>
220 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
220 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
221 <ul id="kernel_menu" class="dropdown-menu">
221 <ul id="kernel_menu" class="dropdown-menu">
222 <li id="int_kernel"
222 <li id="int_kernel"
223 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
223 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
224 <a href="#">Interrupt</a>
224 <a href="#">Interrupt</a>
225 </li>
225 </li>
226 <li id="restart_kernel"
226 <li id="restart_kernel"
227 title="Restart the Kernel">
227 title="Restart the Kernel">
228 <a href="#">Restart</a>
228 <a href="#">Restart</a>
229 </li>
229 </li>
230 <li id="reconnect_kernel"
230 <li id="reconnect_kernel"
231 title="Reconnect to the Kernel">
231 title="Reconnect to the Kernel">
232 <a href="#">Reconnect</a>
232 <a href="#">Reconnect</a>
233 </li>
233 </li>
234 <li class="divider"></li>
234 <li class="divider"></li>
235 <li id="menu-change-kernel" class="dropdown-submenu">
235 <li id="menu-change-kernel" class="dropdown-submenu">
236 <a href="#">Change kernel</a>
236 <a href="#">Change kernel</a>
237 <ul class="dropdown-menu" id="menu-change-kernel-submenu"></ul>
237 <ul class="dropdown-menu" id="menu-change-kernel-submenu"></ul>
238 </li>
238 </li>
239 </ul>
239 </ul>
240 </li>
240 </li>
241 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
241 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
242 <ul id="help_menu" class="dropdown-menu">
242 <ul id="help_menu" class="dropdown-menu">
243 <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
243 <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
244 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
244 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
245 <li class="divider"></li>
245 <li class="divider"></li>
246 {% set
246 {% set
247 sections = (
247 sections = (
248 (
248 (
249 ("http://ipython.org/documentation.html","IPython Help",True),
249 ("http://nbviewer.ipython.org/github/ipython/ipython/blob/2.x/examples/Index.ipynb", "Notebook Help", True),
250 ("http://nbviewer.ipython.org/github/ipython/ipython/tree/2.x/examples/Index.ipynb", "Notebook Help", True),
251 ),(
252 ("http://docs.python.org","Python",True),
253 ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
250 ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
254 ("http://docs.scipy.org/doc/numpy/reference/","NumPy",True),
251 ),
255 ("http://docs.scipy.org/doc/scipy/reference/","SciPy",True),
256 ("http://matplotlib.org/contents.html","Matplotlib",True),
257 ("http://docs.sympy.org/latest/index.html","SymPy",True),
258 ("http://pandas.pydata.org/pandas-docs/stable/","pandas", True)
259 )
260 )
252 )
261 %}
253 %}
262
254
263 {% for helplinks in sections %}
255 {% for helplinks in sections %}
264 {% for link in helplinks %}
256 {% for link in helplinks %}
265 <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
257 <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
266 {{'<i class="fa fa-external-link menu-icon pull-right"></i>' if link[2]}}
258 {{'<i class="fa fa-external-link menu-icon pull-right"></i>' if link[2]}}
267 {{link[1]}}
259 {{link[1]}}
268 </a></li>
260 </a></li>
269 {% endfor %}
261 {% endfor %}
270 {% if not loop.last %}
262 {% if not loop.last %}
271 <li class="divider"></li>
263 <li class="divider"></li>
272 {% endif %}
264 {% endif %}
273 {% endfor %}
265 {% endfor %}
274 <li class="divider"></li>
266 <li class="divider"></li>
275 <li title="About IPython Notebook"><a id="notebook_about" href="#">About</a></li>
267 <li title="About IPython Notebook"><a id="notebook_about" href="#">About</a></li>
276 </ul>
268 </ul>
277 </li>
269 </li>
278 </ul>
270 </ul>
279 </div>
271 </div>
280 </div>
272 </div>
281 </div>
273 </div>
282 </div>
274 </div>
283
275
284 <div id="maintoolbar" class="navbar">
276 <div id="maintoolbar" class="navbar">
285 <div class="toolbar-inner navbar-inner navbar-nobg">
277 <div class="toolbar-inner navbar-inner navbar-nobg">
286 <div id="maintoolbar-container" class="container"></div>
278 <div id="maintoolbar-container" class="container"></div>
287 </div>
279 </div>
288 </div>
280 </div>
289 </div>
281 </div>
290
282
291 <div class="lower-header-bar"></div>
283 <div class="lower-header-bar"></div>
292 {% endblock header %}
284 {% endblock header %}
293
285
294 {% block site %}
286 {% block site %}
295
287
296
288
297 <div id="ipython-main-app">
289 <div id="ipython-main-app">
298 <div id="notebook_panel">
290 <div id="notebook_panel">
299 <div id="notebook"></div>
291 <div id="notebook"></div>
300 </div>
292 </div>
301 </div>
293 </div>
302
294
303 <div id="pager">
295 <div id="pager">
304 <div id="pager-contents">
296 <div id="pager-contents">
305 <div id="pager-container" class="container"></div>
297 <div id="pager-container" class="container"></div>
306 </div>
298 </div>
307 <div id='pager-button-area'></div>
299 <div id='pager-button-area'></div>
308 </div>
300 </div>
309
301
310 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
302 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
311
303
312
304
313 {% endblock %}
305 {% endblock %}
314
306
315
307
316 {% block script %}
308 {% block script %}
317 {{super()}}
309 {{super()}}
318 <script type="text/javascript">
310 <script type="text/javascript">
319 sys_info = {{sys_info}};
311 sys_info = {{sys_info}};
320 </script>
312 </script>
321
313
322 <script src="{{ static_url("components/text-encoding/lib/encoding.js") }}" charset="utf-8"></script>
314 <script src="{{ static_url("components/text-encoding/lib/encoding.js") }}" charset="utf-8"></script>
323
315
324 <script src="{{ static_url("notebook/js/main.js") }}" charset="utf-8"></script>
316 <script src="{{ static_url("notebook/js/main.js") }}" charset="utf-8"></script>
325
317
326 {% endblock %}
318 {% endblock %}
@@ -1,336 +1,367 b''
1 """The IPython kernel implementation"""
1 """The IPython kernel implementation"""
2
2
3 import getpass
3 import getpass
4 import sys
4 import sys
5 import traceback
5 import traceback
6
6
7 from IPython.core import release
7 from IPython.core import release
8 from IPython.utils.py3compat import builtin_mod, PY3
8 from IPython.utils.py3compat import builtin_mod, PY3
9 from IPython.utils.tokenutil import token_at_cursor, line_at_cursor
9 from IPython.utils.tokenutil import token_at_cursor, line_at_cursor
10 from IPython.utils.traitlets import Instance, Type, Any
10 from IPython.utils.traitlets import Instance, Type, Any, List
11 from IPython.utils.decorators import undoc
11 from IPython.utils.decorators import undoc
12
12
13 from ..comm import CommManager
13 from ..comm import CommManager
14 from .kernelbase import Kernel as KernelBase
14 from .kernelbase import Kernel as KernelBase
15 from .serialize import serialize_object, unpack_apply_message
15 from .serialize import serialize_object, unpack_apply_message
16 from .zmqshell import ZMQInteractiveShell
16 from .zmqshell import ZMQInteractiveShell
17
17
18
18
19 def lazy_import_handle_comm_opened(*args, **kwargs):
19 def lazy_import_handle_comm_opened(*args, **kwargs):
20 from IPython.html.widgets import Widget
20 from IPython.html.widgets import Widget
21 Widget.handle_comm_opened(*args, **kwargs)
21 Widget.handle_comm_opened(*args, **kwargs)
22
22
23
23
24 class IPythonKernel(KernelBase):
24 class IPythonKernel(KernelBase):
25 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
25 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
26 shell_class = Type(ZMQInteractiveShell)
26 shell_class = Type(ZMQInteractiveShell)
27
27
28 user_module = Any()
28 user_module = Any()
29 def _user_module_changed(self, name, old, new):
29 def _user_module_changed(self, name, old, new):
30 if self.shell is not None:
30 if self.shell is not None:
31 self.shell.user_module = new
31 self.shell.user_module = new
32
32
33 user_ns = Instance(dict, args=None, allow_none=True)
33 user_ns = Instance(dict, args=None, allow_none=True)
34 def _user_ns_changed(self, name, old, new):
34 def _user_ns_changed(self, name, old, new):
35 if self.shell is not None:
35 if self.shell is not None:
36 self.shell.user_ns = new
36 self.shell.user_ns = new
37 self.shell.init_user_ns()
37 self.shell.init_user_ns()
38
38
39 # A reference to the Python builtin 'raw_input' function.
39 # A reference to the Python builtin 'raw_input' function.
40 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
40 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
41 _sys_raw_input = Any()
41 _sys_raw_input = Any()
42 _sys_eval_input = Any()
42 _sys_eval_input = Any()
43
43
44 def __init__(self, **kwargs):
44 def __init__(self, **kwargs):
45 super(IPythonKernel, self).__init__(**kwargs)
45 super(IPythonKernel, self).__init__(**kwargs)
46
46
47 # Initialize the InteractiveShell subclass
47 # Initialize the InteractiveShell subclass
48 self.shell = self.shell_class.instance(parent=self,
48 self.shell = self.shell_class.instance(parent=self,
49 profile_dir = self.profile_dir,
49 profile_dir = self.profile_dir,
50 user_module = self.user_module,
50 user_module = self.user_module,
51 user_ns = self.user_ns,
51 user_ns = self.user_ns,
52 kernel = self,
52 kernel = self,
53 )
53 )
54 self.shell.displayhook.session = self.session
54 self.shell.displayhook.session = self.session
55 self.shell.displayhook.pub_socket = self.iopub_socket
55 self.shell.displayhook.pub_socket = self.iopub_socket
56 self.shell.displayhook.topic = self._topic('execute_result')
56 self.shell.displayhook.topic = self._topic('execute_result')
57 self.shell.display_pub.session = self.session
57 self.shell.display_pub.session = self.session
58 self.shell.display_pub.pub_socket = self.iopub_socket
58 self.shell.display_pub.pub_socket = self.iopub_socket
59 self.shell.data_pub.session = self.session
59 self.shell.data_pub.session = self.session
60 self.shell.data_pub.pub_socket = self.iopub_socket
60 self.shell.data_pub.pub_socket = self.iopub_socket
61
61
62 # TMP - hack while developing
62 # TMP - hack while developing
63 self.shell._reply_content = None
63 self.shell._reply_content = None
64
64
65 self.comm_manager = CommManager(shell=self.shell, parent=self,
65 self.comm_manager = CommManager(shell=self.shell, parent=self,
66 kernel=self)
66 kernel=self)
67 self.comm_manager.register_target('ipython.widget', lazy_import_handle_comm_opened)
67 self.comm_manager.register_target('ipython.widget', lazy_import_handle_comm_opened)
68
68
69 self.shell.configurables.append(self.comm_manager)
69 self.shell.configurables.append(self.comm_manager)
70 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
70 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
71 for msg_type in comm_msg_types:
71 for msg_type in comm_msg_types:
72 self.shell_handlers[msg_type] = getattr(self.comm_manager, msg_type)
72 self.shell_handlers[msg_type] = getattr(self.comm_manager, msg_type)
73
74 help_links = List([
75 {
76 'text': "Python",
77 'url': "http://docs.python.org/%i.%i" % sys.version_info[:2],
78 },
79 {
80 'text': "IPython",
81 'url': "http://ipython.org/documentation.html",
82 },
83 {
84 'text': "NumPy",
85 'url': "http://docs.scipy.org/doc/numpy/reference/",
86 },
87 {
88 'text': "SciPy",
89 'url': "http://docs.scipy.org/doc/scipy/reference/",
90 },
91 {
92 'text': "Matplotlib",
93 'url': "http://matplotlib.org/contents.html",
94 },
95 {
96 'text': "SymPy",
97 'url': "http://docs.sympy.org/latest/index.html",
98 },
99 {
100 'text': "pandas",
101 'url': "http://pandas.pydata.org/pandas-docs/stable/",
102 },
103 ])
73
104
74 # Kernel info fields
105 # Kernel info fields
75 implementation = 'ipython'
106 implementation = 'ipython'
76 implementation_version = release.version
107 implementation_version = release.version
77 language_info = {
108 language_info = {
78 'name': 'python',
109 'name': 'python',
79 'version': sys.version.split()[0],
110 'version': sys.version.split()[0],
80 'mimetype': 'text/x-python',
111 'mimetype': 'text/x-python',
81 'codemirror_mode': {'name': 'ipython',
112 'codemirror_mode': {'name': 'ipython',
82 'version': sys.version_info[0]},
113 'version': sys.version_info[0]},
83 'pygments_lexer': 'ipython%d' % (3 if PY3 else 2),
114 'pygments_lexer': 'ipython%d' % (3 if PY3 else 2),
84 'nbconvert_exporter': 'python',
115 'nbconvert_exporter': 'python',
85 'file_extension': '.py'
116 'file_extension': '.py'
86 }
117 }
87 @property
118 @property
88 def banner(self):
119 def banner(self):
89 return self.shell.banner
120 return self.shell.banner
90
121
91 def start(self):
122 def start(self):
92 self.shell.exit_now = False
123 self.shell.exit_now = False
93 super(IPythonKernel, self).start()
124 super(IPythonKernel, self).start()
94
125
95 def set_parent(self, ident, parent):
126 def set_parent(self, ident, parent):
96 """Overridden from parent to tell the display hook and output streams
127 """Overridden from parent to tell the display hook and output streams
97 about the parent message.
128 about the parent message.
98 """
129 """
99 super(IPythonKernel, self).set_parent(ident, parent)
130 super(IPythonKernel, self).set_parent(ident, parent)
100 self.shell.set_parent(parent)
131 self.shell.set_parent(parent)
101
132
102 def _forward_input(self, allow_stdin=False):
133 def _forward_input(self, allow_stdin=False):
103 """Forward raw_input and getpass to the current frontend.
134 """Forward raw_input and getpass to the current frontend.
104
135
105 via input_request
136 via input_request
106 """
137 """
107 self._allow_stdin = allow_stdin
138 self._allow_stdin = allow_stdin
108
139
109 if PY3:
140 if PY3:
110 self._sys_raw_input = builtin_mod.input
141 self._sys_raw_input = builtin_mod.input
111 builtin_mod.input = self.raw_input
142 builtin_mod.input = self.raw_input
112 else:
143 else:
113 self._sys_raw_input = builtin_mod.raw_input
144 self._sys_raw_input = builtin_mod.raw_input
114 self._sys_eval_input = builtin_mod.input
145 self._sys_eval_input = builtin_mod.input
115 builtin_mod.raw_input = self.raw_input
146 builtin_mod.raw_input = self.raw_input
116 builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt))
147 builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt))
117 self._save_getpass = getpass.getpass
148 self._save_getpass = getpass.getpass
118 getpass.getpass = self.getpass
149 getpass.getpass = self.getpass
119
150
120 def _restore_input(self):
151 def _restore_input(self):
121 """Restore raw_input, getpass"""
152 """Restore raw_input, getpass"""
122 if PY3:
153 if PY3:
123 builtin_mod.input = self._sys_raw_input
154 builtin_mod.input = self._sys_raw_input
124 else:
155 else:
125 builtin_mod.raw_input = self._sys_raw_input
156 builtin_mod.raw_input = self._sys_raw_input
126 builtin_mod.input = self._sys_eval_input
157 builtin_mod.input = self._sys_eval_input
127
158
128 getpass.getpass = self._save_getpass
159 getpass.getpass = self._save_getpass
129
160
130 @property
161 @property
131 def execution_count(self):
162 def execution_count(self):
132 return self.shell.execution_count
163 return self.shell.execution_count
133
164
134 @execution_count.setter
165 @execution_count.setter
135 def execution_count(self, value):
166 def execution_count(self, value):
136 # Ignore the incrememnting done by KernelBase, in favour of our shell's
167 # Ignore the incrememnting done by KernelBase, in favour of our shell's
137 # execution counter.
168 # execution counter.
138 pass
169 pass
139
170
140 def do_execute(self, code, silent, store_history=True,
171 def do_execute(self, code, silent, store_history=True,
141 user_expressions=None, allow_stdin=False):
172 user_expressions=None, allow_stdin=False):
142 shell = self.shell # we'll need this a lot here
173 shell = self.shell # we'll need this a lot here
143
174
144 self._forward_input(allow_stdin)
175 self._forward_input(allow_stdin)
145
176
146 reply_content = {}
177 reply_content = {}
147 # FIXME: the shell calls the exception handler itself.
178 # FIXME: the shell calls the exception handler itself.
148 shell._reply_content = None
179 shell._reply_content = None
149 try:
180 try:
150 shell.run_cell(code, store_history=store_history, silent=silent)
181 shell.run_cell(code, store_history=store_history, silent=silent)
151 except:
182 except:
152 status = u'error'
183 status = u'error'
153 # FIXME: this code right now isn't being used yet by default,
184 # FIXME: this code right now isn't being used yet by default,
154 # because the run_cell() call above directly fires off exception
185 # because the run_cell() call above directly fires off exception
155 # reporting. This code, therefore, is only active in the scenario
186 # reporting. This code, therefore, is only active in the scenario
156 # where runlines itself has an unhandled exception. We need to
187 # where runlines itself has an unhandled exception. We need to
157 # uniformize this, for all exception construction to come from a
188 # uniformize this, for all exception construction to come from a
158 # single location in the codbase.
189 # single location in the codbase.
159 etype, evalue, tb = sys.exc_info()
190 etype, evalue, tb = sys.exc_info()
160 tb_list = traceback.format_exception(etype, evalue, tb)
191 tb_list = traceback.format_exception(etype, evalue, tb)
161 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
192 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
162 else:
193 else:
163 status = u'ok'
194 status = u'ok'
164 finally:
195 finally:
165 self._restore_input()
196 self._restore_input()
166
197
167 reply_content[u'status'] = status
198 reply_content[u'status'] = status
168
199
169 # Return the execution counter so clients can display prompts
200 # Return the execution counter so clients can display prompts
170 reply_content['execution_count'] = shell.execution_count - 1
201 reply_content['execution_count'] = shell.execution_count - 1
171
202
172 # FIXME - fish exception info out of shell, possibly left there by
203 # FIXME - fish exception info out of shell, possibly left there by
173 # runlines. We'll need to clean up this logic later.
204 # runlines. We'll need to clean up this logic later.
174 if shell._reply_content is not None:
205 if shell._reply_content is not None:
175 reply_content.update(shell._reply_content)
206 reply_content.update(shell._reply_content)
176 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
207 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
177 reply_content['engine_info'] = e_info
208 reply_content['engine_info'] = e_info
178 # reset after use
209 # reset after use
179 shell._reply_content = None
210 shell._reply_content = None
180
211
181 if 'traceback' in reply_content:
212 if 'traceback' in reply_content:
182 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
213 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
183
214
184
215
185 # At this point, we can tell whether the main code execution succeeded
216 # At this point, we can tell whether the main code execution succeeded
186 # or not. If it did, we proceed to evaluate user_expressions
217 # or not. If it did, we proceed to evaluate user_expressions
187 if reply_content['status'] == 'ok':
218 if reply_content['status'] == 'ok':
188 reply_content[u'user_expressions'] = \
219 reply_content[u'user_expressions'] = \
189 shell.user_expressions(user_expressions or {})
220 shell.user_expressions(user_expressions or {})
190 else:
221 else:
191 # If there was an error, don't even try to compute expressions
222 # If there was an error, don't even try to compute expressions
192 reply_content[u'user_expressions'] = {}
223 reply_content[u'user_expressions'] = {}
193
224
194 # Payloads should be retrieved regardless of outcome, so we can both
225 # Payloads should be retrieved regardless of outcome, so we can both
195 # recover partial output (that could have been generated early in a
226 # recover partial output (that could have been generated early in a
196 # block, before an error) and clear the payload system always.
227 # block, before an error) and clear the payload system always.
197 reply_content[u'payload'] = shell.payload_manager.read_payload()
228 reply_content[u'payload'] = shell.payload_manager.read_payload()
198 # Be agressive about clearing the payload because we don't want
229 # Be agressive about clearing the payload because we don't want
199 # it to sit in memory until the next execute_request comes in.
230 # it to sit in memory until the next execute_request comes in.
200 shell.payload_manager.clear_payload()
231 shell.payload_manager.clear_payload()
201
232
202 return reply_content
233 return reply_content
203
234
204 def do_complete(self, code, cursor_pos):
235 def do_complete(self, code, cursor_pos):
205 # FIXME: IPython completers currently assume single line,
236 # FIXME: IPython completers currently assume single line,
206 # but completion messages give multi-line context
237 # but completion messages give multi-line context
207 # For now, extract line from cell, based on cursor_pos:
238 # For now, extract line from cell, based on cursor_pos:
208 if cursor_pos is None:
239 if cursor_pos is None:
209 cursor_pos = len(code)
240 cursor_pos = len(code)
210 line, offset = line_at_cursor(code, cursor_pos)
241 line, offset = line_at_cursor(code, cursor_pos)
211 line_cursor = cursor_pos - offset
242 line_cursor = cursor_pos - offset
212
243
213 txt, matches = self.shell.complete('', line, line_cursor)
244 txt, matches = self.shell.complete('', line, line_cursor)
214 return {'matches' : matches,
245 return {'matches' : matches,
215 'cursor_end' : cursor_pos,
246 'cursor_end' : cursor_pos,
216 'cursor_start' : cursor_pos - len(txt),
247 'cursor_start' : cursor_pos - len(txt),
217 'metadata' : {},
248 'metadata' : {},
218 'status' : 'ok'}
249 'status' : 'ok'}
219
250
220 def do_inspect(self, code, cursor_pos, detail_level=0):
251 def do_inspect(self, code, cursor_pos, detail_level=0):
221 name = token_at_cursor(code, cursor_pos)
252 name = token_at_cursor(code, cursor_pos)
222 info = self.shell.object_inspect(name)
253 info = self.shell.object_inspect(name)
223
254
224 reply_content = {'status' : 'ok'}
255 reply_content = {'status' : 'ok'}
225 reply_content['data'] = data = {}
256 reply_content['data'] = data = {}
226 reply_content['metadata'] = {}
257 reply_content['metadata'] = {}
227 reply_content['found'] = info['found']
258 reply_content['found'] = info['found']
228 if info['found']:
259 if info['found']:
229 info_text = self.shell.object_inspect_text(
260 info_text = self.shell.object_inspect_text(
230 name,
261 name,
231 detail_level=detail_level,
262 detail_level=detail_level,
232 )
263 )
233 data['text/plain'] = info_text
264 data['text/plain'] = info_text
234
265
235 return reply_content
266 return reply_content
236
267
237 def do_history(self, hist_access_type, output, raw, session=None, start=None,
268 def do_history(self, hist_access_type, output, raw, session=None, start=None,
238 stop=None, n=None, pattern=None, unique=False):
269 stop=None, n=None, pattern=None, unique=False):
239 if hist_access_type == 'tail':
270 if hist_access_type == 'tail':
240 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
271 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
241 include_latest=True)
272 include_latest=True)
242
273
243 elif hist_access_type == 'range':
274 elif hist_access_type == 'range':
244 hist = self.shell.history_manager.get_range(session, start, stop,
275 hist = self.shell.history_manager.get_range(session, start, stop,
245 raw=raw, output=output)
276 raw=raw, output=output)
246
277
247 elif hist_access_type == 'search':
278 elif hist_access_type == 'search':
248 hist = self.shell.history_manager.search(
279 hist = self.shell.history_manager.search(
249 pattern, raw=raw, output=output, n=n, unique=unique)
280 pattern, raw=raw, output=output, n=n, unique=unique)
250 else:
281 else:
251 hist = []
282 hist = []
252
283
253 return {'history' : list(hist)}
284 return {'history' : list(hist)}
254
285
255 def do_shutdown(self, restart):
286 def do_shutdown(self, restart):
256 self.shell.exit_now = True
287 self.shell.exit_now = True
257 return dict(status='ok', restart=restart)
288 return dict(status='ok', restart=restart)
258
289
259 def do_is_complete(self, code):
290 def do_is_complete(self, code):
260 status, indent_spaces = self.shell.input_transformer_manager.check_complete(code)
291 status, indent_spaces = self.shell.input_transformer_manager.check_complete(code)
261 r = {'status': status}
292 r = {'status': status}
262 if status == 'incomplete':
293 if status == 'incomplete':
263 r['indent'] = ' ' * indent_spaces
294 r['indent'] = ' ' * indent_spaces
264 return r
295 return r
265
296
266 def do_apply(self, content, bufs, msg_id, reply_metadata):
297 def do_apply(self, content, bufs, msg_id, reply_metadata):
267 shell = self.shell
298 shell = self.shell
268 try:
299 try:
269 working = shell.user_ns
300 working = shell.user_ns
270
301
271 prefix = "_"+str(msg_id).replace("-","")+"_"
302 prefix = "_"+str(msg_id).replace("-","")+"_"
272
303
273 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
304 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
274
305
275 fname = getattr(f, '__name__', 'f')
306 fname = getattr(f, '__name__', 'f')
276
307
277 fname = prefix+"f"
308 fname = prefix+"f"
278 argname = prefix+"args"
309 argname = prefix+"args"
279 kwargname = prefix+"kwargs"
310 kwargname = prefix+"kwargs"
280 resultname = prefix+"result"
311 resultname = prefix+"result"
281
312
282 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
313 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
283 # print ns
314 # print ns
284 working.update(ns)
315 working.update(ns)
285 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
316 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
286 try:
317 try:
287 exec(code, shell.user_global_ns, shell.user_ns)
318 exec(code, shell.user_global_ns, shell.user_ns)
288 result = working.get(resultname)
319 result = working.get(resultname)
289 finally:
320 finally:
290 for key in ns:
321 for key in ns:
291 working.pop(key)
322 working.pop(key)
292
323
293 result_buf = serialize_object(result,
324 result_buf = serialize_object(result,
294 buffer_threshold=self.session.buffer_threshold,
325 buffer_threshold=self.session.buffer_threshold,
295 item_threshold=self.session.item_threshold,
326 item_threshold=self.session.item_threshold,
296 )
327 )
297
328
298 except:
329 except:
299 # invoke IPython traceback formatting
330 # invoke IPython traceback formatting
300 shell.showtraceback()
331 shell.showtraceback()
301 # FIXME - fish exception info out of shell, possibly left there by
332 # FIXME - fish exception info out of shell, possibly left there by
302 # run_code. We'll need to clean up this logic later.
333 # run_code. We'll need to clean up this logic later.
303 reply_content = {}
334 reply_content = {}
304 if shell._reply_content is not None:
335 if shell._reply_content is not None:
305 reply_content.update(shell._reply_content)
336 reply_content.update(shell._reply_content)
306 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
337 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
307 reply_content['engine_info'] = e_info
338 reply_content['engine_info'] = e_info
308 # reset after use
339 # reset after use
309 shell._reply_content = None
340 shell._reply_content = None
310
341
311 self.send_response(self.iopub_socket, u'error', reply_content,
342 self.send_response(self.iopub_socket, u'error', reply_content,
312 ident=self._topic('error'))
343 ident=self._topic('error'))
313 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
344 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
314 result_buf = []
345 result_buf = []
315
346
316 if reply_content['ename'] == 'UnmetDependency':
347 if reply_content['ename'] == 'UnmetDependency':
317 reply_metadata['dependencies_met'] = False
348 reply_metadata['dependencies_met'] = False
318 else:
349 else:
319 reply_content = {'status' : 'ok'}
350 reply_content = {'status' : 'ok'}
320
351
321 return reply_content, result_buf
352 return reply_content, result_buf
322
353
323 def do_clear(self):
354 def do_clear(self):
324 self.shell.reset(False)
355 self.shell.reset(False)
325 return dict(status='ok')
356 return dict(status='ok')
326
357
327
358
328 # This exists only for backwards compatibility - use IPythonKernel instead
359 # This exists only for backwards compatibility - use IPythonKernel instead
329
360
330 @undoc
361 @undoc
331 class Kernel(IPythonKernel):
362 class Kernel(IPythonKernel):
332 def __init__(self, *args, **kwargs):
363 def __init__(self, *args, **kwargs):
333 import warnings
364 import warnings
334 warnings.warn('Kernel is a deprecated alias of IPython.kernel.zmq.ipkernel.IPythonKernel',
365 warnings.warn('Kernel is a deprecated alias of IPython.kernel.zmq.ipkernel.IPythonKernel',
335 DeprecationWarning)
366 DeprecationWarning)
336 super(Kernel, self).__init__(*args, **kwargs) No newline at end of file
367 super(Kernel, self).__init__(*args, **kwargs)
@@ -1,695 +1,699 b''
1 """Base class for a kernel that talks to frontends over 0MQ."""
1 """Base class for a kernel that talks to frontends over 0MQ."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 from __future__ import print_function
6 from __future__ import print_function
7
7
8 import sys
8 import sys
9 import time
9 import time
10 import logging
10 import logging
11 import uuid
11 import uuid
12
12
13 from datetime import datetime
13 from datetime import datetime
14 from signal import (
14 from signal import (
15 signal, default_int_handler, SIGINT
15 signal, default_int_handler, SIGINT
16 )
16 )
17
17
18 import zmq
18 import zmq
19 from zmq.eventloop import ioloop
19 from zmq.eventloop import ioloop
20 from zmq.eventloop.zmqstream import ZMQStream
20 from zmq.eventloop.zmqstream import ZMQStream
21
21
22 from IPython.config.configurable import SingletonConfigurable
22 from IPython.config.configurable import SingletonConfigurable
23 from IPython.core.error import StdinNotImplementedError
23 from IPython.core.error import StdinNotImplementedError
24 from IPython.core import release
24 from IPython.core import release
25 from IPython.utils import py3compat
25 from IPython.utils import py3compat
26 from IPython.utils.py3compat import unicode_type, string_types
26 from IPython.utils.py3compat import unicode_type, string_types
27 from IPython.utils.jsonutil import json_clean
27 from IPython.utils.jsonutil import json_clean
28 from IPython.utils.traitlets import (
28 from IPython.utils.traitlets import (
29 Any, Instance, Float, Dict, List, Set, Integer, Unicode, Bool,
29 Any, Instance, Float, Dict, List, Set, Integer, Unicode, Bool,
30 )
30 )
31
31
32 from .session import Session
32 from .session import Session
33
33
34
34
35 class Kernel(SingletonConfigurable):
35 class Kernel(SingletonConfigurable):
36
36
37 #---------------------------------------------------------------------------
37 #---------------------------------------------------------------------------
38 # Kernel interface
38 # Kernel interface
39 #---------------------------------------------------------------------------
39 #---------------------------------------------------------------------------
40
40
41 # attribute to override with a GUI
41 # attribute to override with a GUI
42 eventloop = Any(None)
42 eventloop = Any(None)
43 def _eventloop_changed(self, name, old, new):
43 def _eventloop_changed(self, name, old, new):
44 """schedule call to eventloop from IOLoop"""
44 """schedule call to eventloop from IOLoop"""
45 loop = ioloop.IOLoop.instance()
45 loop = ioloop.IOLoop.instance()
46 loop.add_callback(self.enter_eventloop)
46 loop.add_callback(self.enter_eventloop)
47
47
48 session = Instance(Session)
48 session = Instance(Session)
49 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
49 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
50 shell_streams = List()
50 shell_streams = List()
51 control_stream = Instance(ZMQStream)
51 control_stream = Instance(ZMQStream)
52 iopub_socket = Instance(zmq.Socket)
52 iopub_socket = Instance(zmq.Socket)
53 stdin_socket = Instance(zmq.Socket)
53 stdin_socket = Instance(zmq.Socket)
54 log = Instance(logging.Logger)
54 log = Instance(logging.Logger)
55
55
56 # identities:
56 # identities:
57 int_id = Integer(-1)
57 int_id = Integer(-1)
58 ident = Unicode()
58 ident = Unicode()
59
59
60 def _ident_default(self):
60 def _ident_default(self):
61 return unicode_type(uuid.uuid4())
61 return unicode_type(uuid.uuid4())
62
62
63 # This should be overridden by wrapper kernels that implement any real
63 # This should be overridden by wrapper kernels that implement any real
64 # language.
64 # language.
65 language_info = {}
65 language_info = {}
66
67 # any links that should go in the help menu
68 help_links = List()
66
69
67 # Private interface
70 # Private interface
68
71
69 _darwin_app_nap = Bool(True, config=True,
72 _darwin_app_nap = Bool(True, config=True,
70 help="""Whether to use appnope for compatiblity with OS X App Nap.
73 help="""Whether to use appnope for compatiblity with OS X App Nap.
71
74
72 Only affects OS X >= 10.9.
75 Only affects OS X >= 10.9.
73 """
76 """
74 )
77 )
75
78
76 # track associations with current request
79 # track associations with current request
77 _allow_stdin = Bool(False)
80 _allow_stdin = Bool(False)
78 _parent_header = Dict()
81 _parent_header = Dict()
79 _parent_ident = Any(b'')
82 _parent_ident = Any(b'')
80 # Time to sleep after flushing the stdout/err buffers in each execute
83 # Time to sleep after flushing the stdout/err buffers in each execute
81 # cycle. While this introduces a hard limit on the minimal latency of the
84 # cycle. While this introduces a hard limit on the minimal latency of the
82 # execute cycle, it helps prevent output synchronization problems for
85 # execute cycle, it helps prevent output synchronization problems for
83 # clients.
86 # clients.
84 # Units are in seconds. The minimum zmq latency on local host is probably
87 # Units are in seconds. The minimum zmq latency on local host is probably
85 # ~150 microseconds, set this to 500us for now. We may need to increase it
88 # ~150 microseconds, set this to 500us for now. We may need to increase it
86 # a little if it's not enough after more interactive testing.
89 # a little if it's not enough after more interactive testing.
87 _execute_sleep = Float(0.0005, config=True)
90 _execute_sleep = Float(0.0005, config=True)
88
91
89 # Frequency of the kernel's event loop.
92 # Frequency of the kernel's event loop.
90 # Units are in seconds, kernel subclasses for GUI toolkits may need to
93 # Units are in seconds, kernel subclasses for GUI toolkits may need to
91 # adapt to milliseconds.
94 # adapt to milliseconds.
92 _poll_interval = Float(0.05, config=True)
95 _poll_interval = Float(0.05, config=True)
93
96
94 # If the shutdown was requested over the network, we leave here the
97 # If the shutdown was requested over the network, we leave here the
95 # necessary reply message so it can be sent by our registered atexit
98 # necessary reply message so it can be sent by our registered atexit
96 # handler. This ensures that the reply is only sent to clients truly at
99 # handler. This ensures that the reply is only sent to clients truly at
97 # the end of our shutdown process (which happens after the underlying
100 # the end of our shutdown process (which happens after the underlying
98 # IPython shell's own shutdown).
101 # IPython shell's own shutdown).
99 _shutdown_message = None
102 _shutdown_message = None
100
103
101 # This is a dict of port number that the kernel is listening on. It is set
104 # This is a dict of port number that the kernel is listening on. It is set
102 # by record_ports and used by connect_request.
105 # by record_ports and used by connect_request.
103 _recorded_ports = Dict()
106 _recorded_ports = Dict()
104
107
105 # set of aborted msg_ids
108 # set of aborted msg_ids
106 aborted = Set()
109 aborted = Set()
107
110
108 # Track execution count here. For IPython, we override this to use the
111 # Track execution count here. For IPython, we override this to use the
109 # execution count we store in the shell.
112 # execution count we store in the shell.
110 execution_count = 0
113 execution_count = 0
111
114
112
115
113 def __init__(self, **kwargs):
116 def __init__(self, **kwargs):
114 super(Kernel, self).__init__(**kwargs)
117 super(Kernel, self).__init__(**kwargs)
115
118
116 # Build dict of handlers for message types
119 # Build dict of handlers for message types
117 msg_types = [ 'execute_request', 'complete_request',
120 msg_types = [ 'execute_request', 'complete_request',
118 'inspect_request', 'history_request',
121 'inspect_request', 'history_request',
119 'kernel_info_request',
122 'kernel_info_request',
120 'connect_request', 'shutdown_request',
123 'connect_request', 'shutdown_request',
121 'apply_request', 'is_complete_request',
124 'apply_request', 'is_complete_request',
122 ]
125 ]
123 self.shell_handlers = {}
126 self.shell_handlers = {}
124 for msg_type in msg_types:
127 for msg_type in msg_types:
125 self.shell_handlers[msg_type] = getattr(self, msg_type)
128 self.shell_handlers[msg_type] = getattr(self, msg_type)
126
129
127 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
130 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
128 self.control_handlers = {}
131 self.control_handlers = {}
129 for msg_type in control_msg_types:
132 for msg_type in control_msg_types:
130 self.control_handlers[msg_type] = getattr(self, msg_type)
133 self.control_handlers[msg_type] = getattr(self, msg_type)
131
134
132
135
133 def dispatch_control(self, msg):
136 def dispatch_control(self, msg):
134 """dispatch control requests"""
137 """dispatch control requests"""
135 idents,msg = self.session.feed_identities(msg, copy=False)
138 idents,msg = self.session.feed_identities(msg, copy=False)
136 try:
139 try:
137 msg = self.session.deserialize(msg, content=True, copy=False)
140 msg = self.session.deserialize(msg, content=True, copy=False)
138 except:
141 except:
139 self.log.error("Invalid Control Message", exc_info=True)
142 self.log.error("Invalid Control Message", exc_info=True)
140 return
143 return
141
144
142 self.log.debug("Control received: %s", msg)
145 self.log.debug("Control received: %s", msg)
143
146
144 # Set the parent message for side effects.
147 # Set the parent message for side effects.
145 self.set_parent(idents, msg)
148 self.set_parent(idents, msg)
146 self._publish_status(u'busy')
149 self._publish_status(u'busy')
147
150
148 header = msg['header']
151 header = msg['header']
149 msg_type = header['msg_type']
152 msg_type = header['msg_type']
150
153
151 handler = self.control_handlers.get(msg_type, None)
154 handler = self.control_handlers.get(msg_type, None)
152 if handler is None:
155 if handler is None:
153 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
156 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
154 else:
157 else:
155 try:
158 try:
156 handler(self.control_stream, idents, msg)
159 handler(self.control_stream, idents, msg)
157 except Exception:
160 except Exception:
158 self.log.error("Exception in control handler:", exc_info=True)
161 self.log.error("Exception in control handler:", exc_info=True)
159
162
160 sys.stdout.flush()
163 sys.stdout.flush()
161 sys.stderr.flush()
164 sys.stderr.flush()
162 self._publish_status(u'idle')
165 self._publish_status(u'idle')
163
166
164 def dispatch_shell(self, stream, msg):
167 def dispatch_shell(self, stream, msg):
165 """dispatch shell requests"""
168 """dispatch shell requests"""
166 # flush control requests first
169 # flush control requests first
167 if self.control_stream:
170 if self.control_stream:
168 self.control_stream.flush()
171 self.control_stream.flush()
169
172
170 idents,msg = self.session.feed_identities(msg, copy=False)
173 idents,msg = self.session.feed_identities(msg, copy=False)
171 try:
174 try:
172 msg = self.session.deserialize(msg, content=True, copy=False)
175 msg = self.session.deserialize(msg, content=True, copy=False)
173 except:
176 except:
174 self.log.error("Invalid Message", exc_info=True)
177 self.log.error("Invalid Message", exc_info=True)
175 return
178 return
176
179
177 # Set the parent message for side effects.
180 # Set the parent message for side effects.
178 self.set_parent(idents, msg)
181 self.set_parent(idents, msg)
179 self._publish_status(u'busy')
182 self._publish_status(u'busy')
180
183
181 header = msg['header']
184 header = msg['header']
182 msg_id = header['msg_id']
185 msg_id = header['msg_id']
183 msg_type = msg['header']['msg_type']
186 msg_type = msg['header']['msg_type']
184
187
185 # Print some info about this message and leave a '--->' marker, so it's
188 # Print some info about this message and leave a '--->' marker, so it's
186 # easier to trace visually the message chain when debugging. Each
189 # easier to trace visually the message chain when debugging. Each
187 # handler prints its message at the end.
190 # handler prints its message at the end.
188 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
191 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
189 self.log.debug(' Content: %s\n --->\n ', msg['content'])
192 self.log.debug(' Content: %s\n --->\n ', msg['content'])
190
193
191 if msg_id in self.aborted:
194 if msg_id in self.aborted:
192 self.aborted.remove(msg_id)
195 self.aborted.remove(msg_id)
193 # is it safe to assume a msg_id will not be resubmitted?
196 # is it safe to assume a msg_id will not be resubmitted?
194 reply_type = msg_type.split('_')[0] + '_reply'
197 reply_type = msg_type.split('_')[0] + '_reply'
195 status = {'status' : 'aborted'}
198 status = {'status' : 'aborted'}
196 md = {'engine' : self.ident}
199 md = {'engine' : self.ident}
197 md.update(status)
200 md.update(status)
198 self.session.send(stream, reply_type, metadata=md,
201 self.session.send(stream, reply_type, metadata=md,
199 content=status, parent=msg, ident=idents)
202 content=status, parent=msg, ident=idents)
200 return
203 return
201
204
202 handler = self.shell_handlers.get(msg_type, None)
205 handler = self.shell_handlers.get(msg_type, None)
203 if handler is None:
206 if handler is None:
204 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
207 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
205 else:
208 else:
206 # ensure default_int_handler during handler call
209 # ensure default_int_handler during handler call
207 sig = signal(SIGINT, default_int_handler)
210 sig = signal(SIGINT, default_int_handler)
208 self.log.debug("%s: %s", msg_type, msg)
211 self.log.debug("%s: %s", msg_type, msg)
209 try:
212 try:
210 handler(stream, idents, msg)
213 handler(stream, idents, msg)
211 except Exception:
214 except Exception:
212 self.log.error("Exception in message handler:", exc_info=True)
215 self.log.error("Exception in message handler:", exc_info=True)
213 finally:
216 finally:
214 signal(SIGINT, sig)
217 signal(SIGINT, sig)
215
218
216 sys.stdout.flush()
219 sys.stdout.flush()
217 sys.stderr.flush()
220 sys.stderr.flush()
218 self._publish_status(u'idle')
221 self._publish_status(u'idle')
219
222
220 def enter_eventloop(self):
223 def enter_eventloop(self):
221 """enter eventloop"""
224 """enter eventloop"""
222 self.log.info("entering eventloop %s", self.eventloop)
225 self.log.info("entering eventloop %s", self.eventloop)
223 for stream in self.shell_streams:
226 for stream in self.shell_streams:
224 # flush any pending replies,
227 # flush any pending replies,
225 # which may be skipped by entering the eventloop
228 # which may be skipped by entering the eventloop
226 stream.flush(zmq.POLLOUT)
229 stream.flush(zmq.POLLOUT)
227 # restore default_int_handler
230 # restore default_int_handler
228 signal(SIGINT, default_int_handler)
231 signal(SIGINT, default_int_handler)
229 while self.eventloop is not None:
232 while self.eventloop is not None:
230 try:
233 try:
231 self.eventloop(self)
234 self.eventloop(self)
232 except KeyboardInterrupt:
235 except KeyboardInterrupt:
233 # Ctrl-C shouldn't crash the kernel
236 # Ctrl-C shouldn't crash the kernel
234 self.log.error("KeyboardInterrupt caught in kernel")
237 self.log.error("KeyboardInterrupt caught in kernel")
235 continue
238 continue
236 else:
239 else:
237 # eventloop exited cleanly, this means we should stop (right?)
240 # eventloop exited cleanly, this means we should stop (right?)
238 self.eventloop = None
241 self.eventloop = None
239 break
242 break
240 self.log.info("exiting eventloop")
243 self.log.info("exiting eventloop")
241
244
242 def start(self):
245 def start(self):
243 """register dispatchers for streams"""
246 """register dispatchers for streams"""
244 if self.control_stream:
247 if self.control_stream:
245 self.control_stream.on_recv(self.dispatch_control, copy=False)
248 self.control_stream.on_recv(self.dispatch_control, copy=False)
246
249
247 def make_dispatcher(stream):
250 def make_dispatcher(stream):
248 def dispatcher(msg):
251 def dispatcher(msg):
249 return self.dispatch_shell(stream, msg)
252 return self.dispatch_shell(stream, msg)
250 return dispatcher
253 return dispatcher
251
254
252 for s in self.shell_streams:
255 for s in self.shell_streams:
253 s.on_recv(make_dispatcher(s), copy=False)
256 s.on_recv(make_dispatcher(s), copy=False)
254
257
255 # publish idle status
258 # publish idle status
256 self._publish_status('starting')
259 self._publish_status('starting')
257
260
258 def do_one_iteration(self):
261 def do_one_iteration(self):
259 """step eventloop just once"""
262 """step eventloop just once"""
260 if self.control_stream:
263 if self.control_stream:
261 self.control_stream.flush()
264 self.control_stream.flush()
262 for stream in self.shell_streams:
265 for stream in self.shell_streams:
263 # handle at most one request per iteration
266 # handle at most one request per iteration
264 stream.flush(zmq.POLLIN, 1)
267 stream.flush(zmq.POLLIN, 1)
265 stream.flush(zmq.POLLOUT)
268 stream.flush(zmq.POLLOUT)
266
269
267
270
268 def record_ports(self, ports):
271 def record_ports(self, ports):
269 """Record the ports that this kernel is using.
272 """Record the ports that this kernel is using.
270
273
271 The creator of the Kernel instance must call this methods if they
274 The creator of the Kernel instance must call this methods if they
272 want the :meth:`connect_request` method to return the port numbers.
275 want the :meth:`connect_request` method to return the port numbers.
273 """
276 """
274 self._recorded_ports = ports
277 self._recorded_ports = ports
275
278
276 #---------------------------------------------------------------------------
279 #---------------------------------------------------------------------------
277 # Kernel request handlers
280 # Kernel request handlers
278 #---------------------------------------------------------------------------
281 #---------------------------------------------------------------------------
279
282
280 def _make_metadata(self, other=None):
283 def _make_metadata(self, other=None):
281 """init metadata dict, for execute/apply_reply"""
284 """init metadata dict, for execute/apply_reply"""
282 new_md = {
285 new_md = {
283 'dependencies_met' : True,
286 'dependencies_met' : True,
284 'engine' : self.ident,
287 'engine' : self.ident,
285 'started': datetime.now(),
288 'started': datetime.now(),
286 }
289 }
287 if other:
290 if other:
288 new_md.update(other)
291 new_md.update(other)
289 return new_md
292 return new_md
290
293
291 def _publish_execute_input(self, code, parent, execution_count):
294 def _publish_execute_input(self, code, parent, execution_count):
292 """Publish the code request on the iopub stream."""
295 """Publish the code request on the iopub stream."""
293
296
294 self.session.send(self.iopub_socket, u'execute_input',
297 self.session.send(self.iopub_socket, u'execute_input',
295 {u'code':code, u'execution_count': execution_count},
298 {u'code':code, u'execution_count': execution_count},
296 parent=parent, ident=self._topic('execute_input')
299 parent=parent, ident=self._topic('execute_input')
297 )
300 )
298
301
299 def _publish_status(self, status, parent=None):
302 def _publish_status(self, status, parent=None):
300 """send status (busy/idle) on IOPub"""
303 """send status (busy/idle) on IOPub"""
301 self.session.send(self.iopub_socket,
304 self.session.send(self.iopub_socket,
302 u'status',
305 u'status',
303 {u'execution_state': status},
306 {u'execution_state': status},
304 parent=parent or self._parent_header,
307 parent=parent or self._parent_header,
305 ident=self._topic('status'),
308 ident=self._topic('status'),
306 )
309 )
307
310
308 def set_parent(self, ident, parent):
311 def set_parent(self, ident, parent):
309 """Set the current parent_header
312 """Set the current parent_header
310
313
311 Side effects (IOPub messages) and replies are associated with
314 Side effects (IOPub messages) and replies are associated with
312 the request that caused them via the parent_header.
315 the request that caused them via the parent_header.
313
316
314 The parent identity is used to route input_request messages
317 The parent identity is used to route input_request messages
315 on the stdin channel.
318 on the stdin channel.
316 """
319 """
317 self._parent_ident = ident
320 self._parent_ident = ident
318 self._parent_header = parent
321 self._parent_header = parent
319
322
320 def send_response(self, stream, msg_or_type, content=None, ident=None,
323 def send_response(self, stream, msg_or_type, content=None, ident=None,
321 buffers=None, track=False, header=None, metadata=None):
324 buffers=None, track=False, header=None, metadata=None):
322 """Send a response to the message we're currently processing.
325 """Send a response to the message we're currently processing.
323
326
324 This accepts all the parameters of :meth:`IPython.kernel.zmq.session.Session.send`
327 This accepts all the parameters of :meth:`IPython.kernel.zmq.session.Session.send`
325 except ``parent``.
328 except ``parent``.
326
329
327 This relies on :meth:`set_parent` having been called for the current
330 This relies on :meth:`set_parent` having been called for the current
328 message.
331 message.
329 """
332 """
330 return self.session.send(stream, msg_or_type, content, self._parent_header,
333 return self.session.send(stream, msg_or_type, content, self._parent_header,
331 ident, buffers, track, header, metadata)
334 ident, buffers, track, header, metadata)
332
335
333 def execute_request(self, stream, ident, parent):
336 def execute_request(self, stream, ident, parent):
334 """handle an execute_request"""
337 """handle an execute_request"""
335
338
336 try:
339 try:
337 content = parent[u'content']
340 content = parent[u'content']
338 code = py3compat.cast_unicode_py2(content[u'code'])
341 code = py3compat.cast_unicode_py2(content[u'code'])
339 silent = content[u'silent']
342 silent = content[u'silent']
340 store_history = content.get(u'store_history', not silent)
343 store_history = content.get(u'store_history', not silent)
341 user_expressions = content.get('user_expressions', {})
344 user_expressions = content.get('user_expressions', {})
342 allow_stdin = content.get('allow_stdin', False)
345 allow_stdin = content.get('allow_stdin', False)
343 except:
346 except:
344 self.log.error("Got bad msg: ")
347 self.log.error("Got bad msg: ")
345 self.log.error("%s", parent)
348 self.log.error("%s", parent)
346 return
349 return
347
350
348 md = self._make_metadata(parent['metadata'])
351 md = self._make_metadata(parent['metadata'])
349
352
350 # Re-broadcast our input for the benefit of listening clients, and
353 # Re-broadcast our input for the benefit of listening clients, and
351 # start computing output
354 # start computing output
352 if not silent:
355 if not silent:
353 self.execution_count += 1
356 self.execution_count += 1
354 self._publish_execute_input(code, parent, self.execution_count)
357 self._publish_execute_input(code, parent, self.execution_count)
355
358
356 reply_content = self.do_execute(code, silent, store_history,
359 reply_content = self.do_execute(code, silent, store_history,
357 user_expressions, allow_stdin)
360 user_expressions, allow_stdin)
358
361
359 # Flush output before sending the reply.
362 # Flush output before sending the reply.
360 sys.stdout.flush()
363 sys.stdout.flush()
361 sys.stderr.flush()
364 sys.stderr.flush()
362 # FIXME: on rare occasions, the flush doesn't seem to make it to the
365 # FIXME: on rare occasions, the flush doesn't seem to make it to the
363 # clients... This seems to mitigate the problem, but we definitely need
366 # clients... This seems to mitigate the problem, but we definitely need
364 # to better understand what's going on.
367 # to better understand what's going on.
365 if self._execute_sleep:
368 if self._execute_sleep:
366 time.sleep(self._execute_sleep)
369 time.sleep(self._execute_sleep)
367
370
368 # Send the reply.
371 # Send the reply.
369 reply_content = json_clean(reply_content)
372 reply_content = json_clean(reply_content)
370
373
371 md['status'] = reply_content['status']
374 md['status'] = reply_content['status']
372 if reply_content['status'] == 'error' and \
375 if reply_content['status'] == 'error' and \
373 reply_content['ename'] == 'UnmetDependency':
376 reply_content['ename'] == 'UnmetDependency':
374 md['dependencies_met'] = False
377 md['dependencies_met'] = False
375
378
376 reply_msg = self.session.send(stream, u'execute_reply',
379 reply_msg = self.session.send(stream, u'execute_reply',
377 reply_content, parent, metadata=md,
380 reply_content, parent, metadata=md,
378 ident=ident)
381 ident=ident)
379
382
380 self.log.debug("%s", reply_msg)
383 self.log.debug("%s", reply_msg)
381
384
382 if not silent and reply_msg['content']['status'] == u'error':
385 if not silent and reply_msg['content']['status'] == u'error':
383 self._abort_queues()
386 self._abort_queues()
384
387
385 def do_execute(self, code, silent, store_history=True,
388 def do_execute(self, code, silent, store_history=True,
386 user_experssions=None, allow_stdin=False):
389 user_experssions=None, allow_stdin=False):
387 """Execute user code. Must be overridden by subclasses.
390 """Execute user code. Must be overridden by subclasses.
388 """
391 """
389 raise NotImplementedError
392 raise NotImplementedError
390
393
391 def complete_request(self, stream, ident, parent):
394 def complete_request(self, stream, ident, parent):
392 content = parent['content']
395 content = parent['content']
393 code = content['code']
396 code = content['code']
394 cursor_pos = content['cursor_pos']
397 cursor_pos = content['cursor_pos']
395
398
396 matches = self.do_complete(code, cursor_pos)
399 matches = self.do_complete(code, cursor_pos)
397 matches = json_clean(matches)
400 matches = json_clean(matches)
398 completion_msg = self.session.send(stream, 'complete_reply',
401 completion_msg = self.session.send(stream, 'complete_reply',
399 matches, parent, ident)
402 matches, parent, ident)
400 self.log.debug("%s", completion_msg)
403 self.log.debug("%s", completion_msg)
401
404
402 def do_complete(self, code, cursor_pos):
405 def do_complete(self, code, cursor_pos):
403 """Override in subclasses to find completions.
406 """Override in subclasses to find completions.
404 """
407 """
405 return {'matches' : [],
408 return {'matches' : [],
406 'cursor_end' : cursor_pos,
409 'cursor_end' : cursor_pos,
407 'cursor_start' : cursor_pos,
410 'cursor_start' : cursor_pos,
408 'metadata' : {},
411 'metadata' : {},
409 'status' : 'ok'}
412 'status' : 'ok'}
410
413
411 def inspect_request(self, stream, ident, parent):
414 def inspect_request(self, stream, ident, parent):
412 content = parent['content']
415 content = parent['content']
413
416
414 reply_content = self.do_inspect(content['code'], content['cursor_pos'],
417 reply_content = self.do_inspect(content['code'], content['cursor_pos'],
415 content.get('detail_level', 0))
418 content.get('detail_level', 0))
416 # Before we send this object over, we scrub it for JSON usage
419 # Before we send this object over, we scrub it for JSON usage
417 reply_content = json_clean(reply_content)
420 reply_content = json_clean(reply_content)
418 msg = self.session.send(stream, 'inspect_reply',
421 msg = self.session.send(stream, 'inspect_reply',
419 reply_content, parent, ident)
422 reply_content, parent, ident)
420 self.log.debug("%s", msg)
423 self.log.debug("%s", msg)
421
424
422 def do_inspect(self, code, cursor_pos, detail_level=0):
425 def do_inspect(self, code, cursor_pos, detail_level=0):
423 """Override in subclasses to allow introspection.
426 """Override in subclasses to allow introspection.
424 """
427 """
425 return {'status': 'ok', 'data':{}, 'metadata':{}, 'found':False}
428 return {'status': 'ok', 'data':{}, 'metadata':{}, 'found':False}
426
429
427 def history_request(self, stream, ident, parent):
430 def history_request(self, stream, ident, parent):
428 content = parent['content']
431 content = parent['content']
429
432
430 reply_content = self.do_history(**content)
433 reply_content = self.do_history(**content)
431
434
432 reply_content = json_clean(reply_content)
435 reply_content = json_clean(reply_content)
433 msg = self.session.send(stream, 'history_reply',
436 msg = self.session.send(stream, 'history_reply',
434 reply_content, parent, ident)
437 reply_content, parent, ident)
435 self.log.debug("%s", msg)
438 self.log.debug("%s", msg)
436
439
437 def do_history(self, hist_access_type, output, raw, session=None, start=None,
440 def do_history(self, hist_access_type, output, raw, session=None, start=None,
438 stop=None, n=None, pattern=None, unique=False):
441 stop=None, n=None, pattern=None, unique=False):
439 """Override in subclasses to access history.
442 """Override in subclasses to access history.
440 """
443 """
441 return {'history': []}
444 return {'history': []}
442
445
443 def connect_request(self, stream, ident, parent):
446 def connect_request(self, stream, ident, parent):
444 if self._recorded_ports is not None:
447 if self._recorded_ports is not None:
445 content = self._recorded_ports.copy()
448 content = self._recorded_ports.copy()
446 else:
449 else:
447 content = {}
450 content = {}
448 msg = self.session.send(stream, 'connect_reply',
451 msg = self.session.send(stream, 'connect_reply',
449 content, parent, ident)
452 content, parent, ident)
450 self.log.debug("%s", msg)
453 self.log.debug("%s", msg)
451
454
452 @property
455 @property
453 def kernel_info(self):
456 def kernel_info(self):
454 return {
457 return {
455 'protocol_version': release.kernel_protocol_version,
458 'protocol_version': release.kernel_protocol_version,
456 'implementation': self.implementation,
459 'implementation': self.implementation,
457 'implementation_version': self.implementation_version,
460 'implementation_version': self.implementation_version,
458 'language_info': self.language_info,
461 'language_info': self.language_info,
459 'banner': self.banner,
462 'banner': self.banner,
463 'help_links': self.help_links,
460 }
464 }
461
465
462 def kernel_info_request(self, stream, ident, parent):
466 def kernel_info_request(self, stream, ident, parent):
463 msg = self.session.send(stream, 'kernel_info_reply',
467 msg = self.session.send(stream, 'kernel_info_reply',
464 self.kernel_info, parent, ident)
468 self.kernel_info, parent, ident)
465 self.log.debug("%s", msg)
469 self.log.debug("%s", msg)
466
470
467 def shutdown_request(self, stream, ident, parent):
471 def shutdown_request(self, stream, ident, parent):
468 content = self.do_shutdown(parent['content']['restart'])
472 content = self.do_shutdown(parent['content']['restart'])
469 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
473 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
470 # same content, but different msg_id for broadcasting on IOPub
474 # same content, but different msg_id for broadcasting on IOPub
471 self._shutdown_message = self.session.msg(u'shutdown_reply',
475 self._shutdown_message = self.session.msg(u'shutdown_reply',
472 content, parent
476 content, parent
473 )
477 )
474
478
475 self._at_shutdown()
479 self._at_shutdown()
476 # call sys.exit after a short delay
480 # call sys.exit after a short delay
477 loop = ioloop.IOLoop.instance()
481 loop = ioloop.IOLoop.instance()
478 loop.add_timeout(time.time()+0.1, loop.stop)
482 loop.add_timeout(time.time()+0.1, loop.stop)
479
483
480 def do_shutdown(self, restart):
484 def do_shutdown(self, restart):
481 """Override in subclasses to do things when the frontend shuts down the
485 """Override in subclasses to do things when the frontend shuts down the
482 kernel.
486 kernel.
483 """
487 """
484 return {'status': 'ok', 'restart': restart}
488 return {'status': 'ok', 'restart': restart}
485
489
486 def is_complete_request(self, stream, ident, parent):
490 def is_complete_request(self, stream, ident, parent):
487 content = parent['content']
491 content = parent['content']
488 code = content['code']
492 code = content['code']
489
493
490 reply_content = self.do_is_complete(code)
494 reply_content = self.do_is_complete(code)
491 reply_content = json_clean(reply_content)
495 reply_content = json_clean(reply_content)
492 reply_msg = self.session.send(stream, 'is_complete_reply',
496 reply_msg = self.session.send(stream, 'is_complete_reply',
493 reply_content, parent, ident)
497 reply_content, parent, ident)
494 self.log.debug("%s", reply_msg)
498 self.log.debug("%s", reply_msg)
495
499
496 def do_is_complete(self, code):
500 def do_is_complete(self, code):
497 """Override in subclasses to find completions.
501 """Override in subclasses to find completions.
498 """
502 """
499 return {'status' : 'unknown',
503 return {'status' : 'unknown',
500 }
504 }
501
505
502 #---------------------------------------------------------------------------
506 #---------------------------------------------------------------------------
503 # Engine methods
507 # Engine methods
504 #---------------------------------------------------------------------------
508 #---------------------------------------------------------------------------
505
509
506 def apply_request(self, stream, ident, parent):
510 def apply_request(self, stream, ident, parent):
507 try:
511 try:
508 content = parent[u'content']
512 content = parent[u'content']
509 bufs = parent[u'buffers']
513 bufs = parent[u'buffers']
510 msg_id = parent['header']['msg_id']
514 msg_id = parent['header']['msg_id']
511 except:
515 except:
512 self.log.error("Got bad msg: %s", parent, exc_info=True)
516 self.log.error("Got bad msg: %s", parent, exc_info=True)
513 return
517 return
514
518
515 md = self._make_metadata(parent['metadata'])
519 md = self._make_metadata(parent['metadata'])
516
520
517 reply_content, result_buf = self.do_apply(content, bufs, msg_id, md)
521 reply_content, result_buf = self.do_apply(content, bufs, msg_id, md)
518
522
519 # put 'ok'/'error' status in header, for scheduler introspection:
523 # put 'ok'/'error' status in header, for scheduler introspection:
520 md['status'] = reply_content['status']
524 md['status'] = reply_content['status']
521
525
522 # flush i/o
526 # flush i/o
523 sys.stdout.flush()
527 sys.stdout.flush()
524 sys.stderr.flush()
528 sys.stderr.flush()
525
529
526 self.session.send(stream, u'apply_reply', reply_content,
530 self.session.send(stream, u'apply_reply', reply_content,
527 parent=parent, ident=ident,buffers=result_buf, metadata=md)
531 parent=parent, ident=ident,buffers=result_buf, metadata=md)
528
532
529 def do_apply(self, content, bufs, msg_id, reply_metadata):
533 def do_apply(self, content, bufs, msg_id, reply_metadata):
530 """Override in subclasses to support the IPython parallel framework.
534 """Override in subclasses to support the IPython parallel framework.
531 """
535 """
532 raise NotImplementedError
536 raise NotImplementedError
533
537
534 #---------------------------------------------------------------------------
538 #---------------------------------------------------------------------------
535 # Control messages
539 # Control messages
536 #---------------------------------------------------------------------------
540 #---------------------------------------------------------------------------
537
541
538 def abort_request(self, stream, ident, parent):
542 def abort_request(self, stream, ident, parent):
539 """abort a specific msg by id"""
543 """abort a specific msg by id"""
540 msg_ids = parent['content'].get('msg_ids', None)
544 msg_ids = parent['content'].get('msg_ids', None)
541 if isinstance(msg_ids, string_types):
545 if isinstance(msg_ids, string_types):
542 msg_ids = [msg_ids]
546 msg_ids = [msg_ids]
543 if not msg_ids:
547 if not msg_ids:
544 self._abort_queues()
548 self._abort_queues()
545 for mid in msg_ids:
549 for mid in msg_ids:
546 self.aborted.add(str(mid))
550 self.aborted.add(str(mid))
547
551
548 content = dict(status='ok')
552 content = dict(status='ok')
549 reply_msg = self.session.send(stream, 'abort_reply', content=content,
553 reply_msg = self.session.send(stream, 'abort_reply', content=content,
550 parent=parent, ident=ident)
554 parent=parent, ident=ident)
551 self.log.debug("%s", reply_msg)
555 self.log.debug("%s", reply_msg)
552
556
553 def clear_request(self, stream, idents, parent):
557 def clear_request(self, stream, idents, parent):
554 """Clear our namespace."""
558 """Clear our namespace."""
555 content = self.do_clear()
559 content = self.do_clear()
556 self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
560 self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
557 content = content)
561 content = content)
558
562
559 def do_clear(self):
563 def do_clear(self):
560 """Override in subclasses to clear the namespace
564 """Override in subclasses to clear the namespace
561
565
562 This is only required for IPython.parallel.
566 This is only required for IPython.parallel.
563 """
567 """
564 raise NotImplementedError
568 raise NotImplementedError
565
569
566 #---------------------------------------------------------------------------
570 #---------------------------------------------------------------------------
567 # Protected interface
571 # Protected interface
568 #---------------------------------------------------------------------------
572 #---------------------------------------------------------------------------
569
573
570 def _topic(self, topic):
574 def _topic(self, topic):
571 """prefixed topic for IOPub messages"""
575 """prefixed topic for IOPub messages"""
572 if self.int_id >= 0:
576 if self.int_id >= 0:
573 base = "engine.%i" % self.int_id
577 base = "engine.%i" % self.int_id
574 else:
578 else:
575 base = "kernel.%s" % self.ident
579 base = "kernel.%s" % self.ident
576
580
577 return py3compat.cast_bytes("%s.%s" % (base, topic))
581 return py3compat.cast_bytes("%s.%s" % (base, topic))
578
582
579 def _abort_queues(self):
583 def _abort_queues(self):
580 for stream in self.shell_streams:
584 for stream in self.shell_streams:
581 if stream:
585 if stream:
582 self._abort_queue(stream)
586 self._abort_queue(stream)
583
587
584 def _abort_queue(self, stream):
588 def _abort_queue(self, stream):
585 poller = zmq.Poller()
589 poller = zmq.Poller()
586 poller.register(stream.socket, zmq.POLLIN)
590 poller.register(stream.socket, zmq.POLLIN)
587 while True:
591 while True:
588 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
592 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
589 if msg is None:
593 if msg is None:
590 return
594 return
591
595
592 self.log.info("Aborting:")
596 self.log.info("Aborting:")
593 self.log.info("%s", msg)
597 self.log.info("%s", msg)
594 msg_type = msg['header']['msg_type']
598 msg_type = msg['header']['msg_type']
595 reply_type = msg_type.split('_')[0] + '_reply'
599 reply_type = msg_type.split('_')[0] + '_reply'
596
600
597 status = {'status' : 'aborted'}
601 status = {'status' : 'aborted'}
598 md = {'engine' : self.ident}
602 md = {'engine' : self.ident}
599 md.update(status)
603 md.update(status)
600 reply_msg = self.session.send(stream, reply_type, metadata=md,
604 reply_msg = self.session.send(stream, reply_type, metadata=md,
601 content=status, parent=msg, ident=idents)
605 content=status, parent=msg, ident=idents)
602 self.log.debug("%s", reply_msg)
606 self.log.debug("%s", reply_msg)
603 # We need to wait a bit for requests to come in. This can probably
607 # We need to wait a bit for requests to come in. This can probably
604 # be set shorter for true asynchronous clients.
608 # be set shorter for true asynchronous clients.
605 poller.poll(50)
609 poller.poll(50)
606
610
607
611
608 def _no_raw_input(self):
612 def _no_raw_input(self):
609 """Raise StdinNotImplentedError if active frontend doesn't support
613 """Raise StdinNotImplentedError if active frontend doesn't support
610 stdin."""
614 stdin."""
611 raise StdinNotImplementedError("raw_input was called, but this "
615 raise StdinNotImplementedError("raw_input was called, but this "
612 "frontend does not support stdin.")
616 "frontend does not support stdin.")
613
617
614 def getpass(self, prompt=''):
618 def getpass(self, prompt=''):
615 """Forward getpass to frontends
619 """Forward getpass to frontends
616
620
617 Raises
621 Raises
618 ------
622 ------
619 StdinNotImplentedError if active frontend doesn't support stdin.
623 StdinNotImplentedError if active frontend doesn't support stdin.
620 """
624 """
621 if not self._allow_stdin:
625 if not self._allow_stdin:
622 raise StdinNotImplementedError(
626 raise StdinNotImplementedError(
623 "getpass was called, but this frontend does not support input requests."
627 "getpass was called, but this frontend does not support input requests."
624 )
628 )
625 return self._input_request(prompt,
629 return self._input_request(prompt,
626 self._parent_ident,
630 self._parent_ident,
627 self._parent_header,
631 self._parent_header,
628 password=True,
632 password=True,
629 )
633 )
630
634
631 def raw_input(self, prompt=''):
635 def raw_input(self, prompt=''):
632 """Forward raw_input to frontends
636 """Forward raw_input to frontends
633
637
634 Raises
638 Raises
635 ------
639 ------
636 StdinNotImplentedError if active frontend doesn't support stdin.
640 StdinNotImplentedError if active frontend doesn't support stdin.
637 """
641 """
638 if not self._allow_stdin:
642 if not self._allow_stdin:
639 raise StdinNotImplementedError(
643 raise StdinNotImplementedError(
640 "raw_input was called, but this frontend does not support input requests."
644 "raw_input was called, but this frontend does not support input requests."
641 )
645 )
642 return self._input_request(prompt,
646 return self._input_request(prompt,
643 self._parent_ident,
647 self._parent_ident,
644 self._parent_header,
648 self._parent_header,
645 password=False,
649 password=False,
646 )
650 )
647
651
648 def _input_request(self, prompt, ident, parent, password=False):
652 def _input_request(self, prompt, ident, parent, password=False):
649 # Flush output before making the request.
653 # Flush output before making the request.
650 sys.stderr.flush()
654 sys.stderr.flush()
651 sys.stdout.flush()
655 sys.stdout.flush()
652 # flush the stdin socket, to purge stale replies
656 # flush the stdin socket, to purge stale replies
653 while True:
657 while True:
654 try:
658 try:
655 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
659 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
656 except zmq.ZMQError as e:
660 except zmq.ZMQError as e:
657 if e.errno == zmq.EAGAIN:
661 if e.errno == zmq.EAGAIN:
658 break
662 break
659 else:
663 else:
660 raise
664 raise
661
665
662 # Send the input request.
666 # Send the input request.
663 content = json_clean(dict(prompt=prompt, password=password))
667 content = json_clean(dict(prompt=prompt, password=password))
664 self.session.send(self.stdin_socket, u'input_request', content, parent,
668 self.session.send(self.stdin_socket, u'input_request', content, parent,
665 ident=ident)
669 ident=ident)
666
670
667 # Await a response.
671 # Await a response.
668 while True:
672 while True:
669 try:
673 try:
670 ident, reply = self.session.recv(self.stdin_socket, 0)
674 ident, reply = self.session.recv(self.stdin_socket, 0)
671 except Exception:
675 except Exception:
672 self.log.warn("Invalid Message:", exc_info=True)
676 self.log.warn("Invalid Message:", exc_info=True)
673 except KeyboardInterrupt:
677 except KeyboardInterrupt:
674 # re-raise KeyboardInterrupt, to truncate traceback
678 # re-raise KeyboardInterrupt, to truncate traceback
675 raise KeyboardInterrupt
679 raise KeyboardInterrupt
676 else:
680 else:
677 break
681 break
678 try:
682 try:
679 value = py3compat.unicode_to_str(reply['content']['value'])
683 value = py3compat.unicode_to_str(reply['content']['value'])
680 except:
684 except:
681 self.log.error("Bad input_reply: %s", parent)
685 self.log.error("Bad input_reply: %s", parent)
682 value = ''
686 value = ''
683 if value == '\x04':
687 if value == '\x04':
684 # EOF
688 # EOF
685 raise EOFError
689 raise EOFError
686 return value
690 return value
687
691
688 def _at_shutdown(self):
692 def _at_shutdown(self):
689 """Actions taken at shutdown by the kernel, called by python's atexit.
693 """Actions taken at shutdown by the kernel, called by python's atexit.
690 """
694 """
691 # io.rprint("Kernel at_shutdown") # dbg
695 # io.rprint("Kernel at_shutdown") # dbg
692 if self._shutdown_message is not None:
696 if self._shutdown_message is not None:
693 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
697 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
694 self.log.debug("%s", self._shutdown_message)
698 self.log.debug("%s", self._shutdown_message)
695 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
699 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
General Comments 0
You need to be logged in to leave comments. Login now