Show More
@@ -507,6 +507,12 b' class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler):' | |||||
507 | # under Python 2.x for some reason |
|
507 | # under Python 2.x for some reason | |
508 | msg = msg.encode('utf8', 'replace') |
|
508 | msg = msg.encode('utf8', 'replace') | |
509 | try: |
|
509 | try: | |
|
510 | identity, msg = msg.split(':', 1) | |||
|
511 | self.session.session = identity.decode('ascii') | |||
|
512 | except Exception: | |||
|
513 | logging.error("First ws message didn't have the form 'identity:[cookie]' - %r", msg) | |||
|
514 | ||||
|
515 | try: | |||
510 | self.request._cookies = Cookie.SimpleCookie(msg) |
|
516 | self.request._cookies = Cookie.SimpleCookie(msg) | |
511 | except: |
|
517 | except: | |
512 | self.log.warn("couldn't parse cookie string: %s",msg, exc_info=True) |
|
518 | self.log.warn("couldn't parse cookie string: %s",msg, exc_info=True) | |
@@ -519,23 +525,28 b' class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler):' | |||||
519 | self.on_message = self.save_on_message |
|
525 | self.on_message = self.save_on_message | |
520 |
|
526 | |||
521 |
|
527 | |||
522 |
class |
|
528 | class ZMQChannelHandler(AuthenticatedZMQStreamHandler): | |
|
529 | ||||
|
530 | @property | |||
|
531 | def max_msg_size(self): | |||
|
532 | return self.settings.get('max_msg_size', 65535) | |||
|
533 | ||||
|
534 | def create_stream(self): | |||
|
535 | km = self.kernel_manager | |||
|
536 | meth = getattr(km, 'connect_%s' % self.channel) | |||
|
537 | self.zmq_stream = meth(self.kernel_id, identity=self.session.bsession) | |||
523 |
|
538 | |||
524 | def initialize(self, *args, **kwargs): |
|
539 | def initialize(self, *args, **kwargs): | |
525 |
self. |
|
540 | self.zmq_stream = None | |
526 |
|
541 | |||
527 | def on_first_message(self, msg): |
|
542 | def on_first_message(self, msg): | |
528 | try: |
|
543 | try: | |
529 |
super( |
|
544 | super(ZMQChannelHandler, self).on_first_message(msg) | |
530 | except web.HTTPError: |
|
545 | except web.HTTPError: | |
531 | self.close() |
|
546 | self.close() | |
532 | return |
|
547 | return | |
533 | km = self.kernel_manager |
|
|||
534 | kernel_id = self.kernel_id |
|
|||
535 | km.add_restart_callback(kernel_id, self.on_kernel_restarted) |
|
|||
536 | km.add_restart_callback(kernel_id, self.on_restart_failed, 'dead') |
|
|||
537 | try: |
|
548 | try: | |
538 | self.iopub_stream = km.connect_iopub(kernel_id) |
|
549 | self.create_stream() | |
539 | except web.HTTPError: |
|
550 | except web.HTTPError: | |
540 | # WebSockets don't response to traditional error codes so we |
|
551 | # WebSockets don't response to traditional error codes so we | |
541 | # close the connection. |
|
552 | # close the connection. | |
@@ -543,29 +554,32 b' class IOPubHandler(AuthenticatedZMQStreamHandler):' | |||||
543 | self.stream.close() |
|
554 | self.stream.close() | |
544 | self.close() |
|
555 | self.close() | |
545 | else: |
|
556 | else: | |
546 |
self. |
|
557 | self.zmq_stream.on_recv(self._on_zmq_reply) | |
547 |
|
558 | |||
548 | def on_message(self, msg): |
|
559 | def on_message(self, msg): | |
549 | pass |
|
560 | if len(msg) < self.max_msg_size: | |
550 |
|
561 | msg = jsonapi.loads(msg) | ||
551 | def _send_status_message(self, status): |
|
562 | self.session.send(self.zmq_stream, msg) | |
552 | msg = self.session.msg("status", |
|
|||
553 | {'execution_state': status} |
|
|||
554 | ) |
|
|||
555 | self.write_message(jsonapi.dumps(msg, default=date_default)) |
|
|||
556 |
|
||||
557 | def on_kernel_restarted(self): |
|
|||
558 | self.log.warn("kernel %s restarted", self.kernel_id) |
|
|||
559 | self._send_status_message('restarting') |
|
|||
560 |
|
||||
561 | def on_restart_failed(self): |
|
|||
562 | self.log.error("kernel %s restarted failed!", self.kernel_id) |
|
|||
563 | self._send_status_message('dead') |
|
|||
564 |
|
563 | |||
565 | def on_close(self): |
|
564 | def on_close(self): | |
566 | # This method can be called twice, once by self.kernel_died and once |
|
565 | # This method can be called twice, once by self.kernel_died and once | |
567 | # from the WebSocket close event. If the WebSocket connection is |
|
566 | # from the WebSocket close event. If the WebSocket connection is | |
568 | # closed before the ZMQ streams are setup, they could be None. |
|
567 | # closed before the ZMQ streams are setup, they could be None. | |
|
568 | if self.zmq_stream is not None and not self.zmq_stream.closed(): | |||
|
569 | self.zmq_stream.on_recv(None) | |||
|
570 | self.zmq_stream.close() | |||
|
571 | ||||
|
572 | ||||
|
573 | class IOPubHandler(ZMQChannelHandler): | |||
|
574 | channel = 'iopub' | |||
|
575 | ||||
|
576 | def create_stream(self): | |||
|
577 | super(IOPubHandler, self).create_stream() | |||
|
578 | km = self.kernel_manager | |||
|
579 | km.add_restart_callback(self.kernel_id, self.on_kernel_restarted) | |||
|
580 | km.add_restart_callback(self.kernel_id, self.on_restart_failed, 'dead') | |||
|
581 | ||||
|
582 | def on_close(self): | |||
569 | km = self.kernel_manager |
|
583 | km = self.kernel_manager | |
570 | if self.kernel_id in km: |
|
584 | if self.kernel_id in km: | |
571 | km.remove_restart_callback( |
|
585 | km.remove_restart_callback( | |
@@ -574,48 +588,31 b' class IOPubHandler(AuthenticatedZMQStreamHandler):' | |||||
574 | km.remove_restart_callback( |
|
588 | km.remove_restart_callback( | |
575 | self.kernel_id, self.on_restart_failed, 'dead', |
|
589 | self.kernel_id, self.on_restart_failed, 'dead', | |
576 | ) |
|
590 | ) | |
577 | if self.iopub_stream is not None and not self.iopub_stream.closed(): |
|
591 | super(IOPubHandler, self).on_close() | |
578 | self.iopub_stream.on_recv(None) |
|
|||
579 | self.iopub_stream.close() |
|
|||
580 |
|
||||
581 |
|
||||
582 | class ShellHandler(AuthenticatedZMQStreamHandler): |
|
|||
583 |
|
592 | |||
584 | @property |
|
593 | def _send_status_message(self, status): | |
585 | def max_msg_size(self): |
|
594 | msg = self.session.msg("status", | |
586 | return self.settings.get('max_msg_size', 65535) |
|
595 | {'execution_state': status} | |
|
596 | ) | |||
|
597 | self.write_message(jsonapi.dumps(msg, default=date_default)) | |||
587 |
|
598 | |||
588 | def initialize(self, *args, **kwargs): |
|
599 | def on_kernel_restarted(self): | |
589 | self.shell_stream = None |
|
600 | logging.warn("kernel %s restarted", self.kernel_id) | |
|
601 | self._send_status_message('restarting') | |||
590 |
|
602 | |||
591 |
def on_ |
|
603 | def on_restart_failed(self): | |
592 | try: |
|
604 | logging.error("kernel %s restarted failed!", self.kernel_id) | |
593 | super(ShellHandler, self).on_first_message(msg) |
|
605 | self._send_status_message('dead') | |
594 | except web.HTTPError: |
|
|||
595 | self.close() |
|
|||
596 | return |
|
|||
597 | km = self.kernel_manager |
|
|||
598 | kernel_id = self.kernel_id |
|
|||
599 | try: |
|
|||
600 | self.shell_stream = km.connect_shell(kernel_id) |
|
|||
601 | except web.HTTPError: |
|
|||
602 | # WebSockets don't response to traditional error codes so we |
|
|||
603 | # close the connection. |
|
|||
604 | if not self.stream.closed(): |
|
|||
605 | self.stream.close() |
|
|||
606 | self.close() |
|
|||
607 | else: |
|
|||
608 | self.shell_stream.on_recv(self._on_zmq_reply) |
|
|||
609 |
|
606 | |||
610 | def on_message(self, msg): |
|
607 | def on_message(self, msg): | |
611 | if len(msg) < self.max_msg_size: |
|
608 | """IOPub messages make no sense""" | |
612 | msg = jsonapi.loads(msg) |
|
609 | pass | |
613 | self.session.send(self.shell_stream, msg) |
|
|||
614 |
|
610 | |||
615 | def on_close(self): |
|
611 | class ShellHandler(ZMQChannelHandler): | |
616 | # Make sure the stream exists and is not already closed. |
|
612 | channel = 'shell' | |
617 | if self.shell_stream is not None and not self.shell_stream.closed(): |
|
613 | ||
618 | self.shell_stream.close() |
|
614 | class StdinHandler(ZMQChannelHandler): | |
|
615 | channel = 'stdin' | |||
619 |
|
616 | |||
620 |
|
617 | |||
621 | #----------------------------------------------------------------------------- |
|
618 | #----------------------------------------------------------------------------- |
@@ -66,7 +66,7 b' from IPython.frontend.html.notebook import DEFAULT_STATIC_FILES_PATH' | |||||
66 | from .kernelmanager import MappingKernelManager |
|
66 | from .kernelmanager import MappingKernelManager | |
67 | from .handlers import (LoginHandler, LogoutHandler, |
|
67 | from .handlers import (LoginHandler, LogoutHandler, | |
68 | ProjectDashboardHandler, NewHandler, NamedNotebookHandler, |
|
68 | ProjectDashboardHandler, NewHandler, NamedNotebookHandler, | |
69 | MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler, |
|
69 | MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler, StdinHandler, | |
70 | ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler, |
|
70 | ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler, | |
71 | RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler, |
|
71 | RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler, | |
72 | MainClusterHandler, ClusterProfileHandler, ClusterActionHandler, |
|
72 | MainClusterHandler, ClusterProfileHandler, ClusterActionHandler, | |
@@ -160,6 +160,7 b' class NotebookWebApplication(web.Application):' | |||||
160 | (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler), |
|
160 | (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler), | |
161 | (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler), |
|
161 | (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler), | |
162 | (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler), |
|
162 | (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler), | |
|
163 | (r"/kernels/%s/stdin" % _kernel_id_regex, StdinHandler), | |||
163 | (r"/notebooks", NotebookRootHandler), |
|
164 | (r"/notebooks", NotebookRootHandler), | |
164 | (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler), |
|
165 | (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler), | |
165 | (r"/rstservice/render", RSTHandler), |
|
166 | (r"/rstservice/render", RSTHandler), |
@@ -936,6 +936,9 b' pre,code,kbd,samp{white-space:pre-wrap;}' | |||||
936 | a{text-decoration:underline;} |
|
936 | a{text-decoration:underline;} | |
937 | p{margin-bottom:0;} |
|
937 | p{margin-bottom:0;} | |
938 | a.heading-anchor:link,a.heading-anchor:visited{text-decoration:none;color:inherit;} |
|
938 | a.heading-anchor:link,a.heading-anchor:visited{text-decoration:none;color:inherit;} | |
|
939 | div.raw_input{padding-top:0px;padding-bottom:0px;height:1em;line-height:1em;font-family:monospace;} | |||
|
940 | span.input_prompt{font-family:inherit;} | |||
|
941 | input.raw_input{font-family:inherit;font-size:inherit;color:inherit;width:auto;margin:-2px 0px 0px 1px;padding-left:1px;padding-top:2px;height:1em;} | |||
939 | @media print{body{overflow:visible !important;} div#notebook{overflow:visible !important;} .ui-widget-content{border:0px;} #save_widget{margin:0px !important;} #header,#pager,#pager_splitter,#menubar,#toolbar{display:none !important;} .cell{border:none !important;} .toolbar{display:none;}}.rendered_html{color:black;}.rendered_html em{font-style:italic;} |
|
942 | @media print{body{overflow:visible !important;} div#notebook{overflow:visible !important;} .ui-widget-content{border:0px;} #save_widget{margin:0px !important;} #header,#pager,#pager_splitter,#menubar,#toolbar{display:none !important;} .cell{border:none !important;} .toolbar{display:none;}}.rendered_html{color:black;}.rendered_html em{font-style:italic;} | |
940 | .rendered_html strong{font-weight:bold;} |
|
943 | .rendered_html strong{font-weight:bold;} | |
941 | .rendered_html u{text-decoration:underline;} |
|
944 | .rendered_html u{text-decoration:underline;} |
@@ -245,7 +245,8 b' var IPython = (function (IPython) {' | |||||
245 | 'execute_reply': $.proxy(this._handle_execute_reply, this), |
|
245 | 'execute_reply': $.proxy(this._handle_execute_reply, this), | |
246 | 'output': $.proxy(this.output_area.handle_output, this.output_area), |
|
246 | 'output': $.proxy(this.output_area.handle_output, this.output_area), | |
247 | 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area), |
|
247 | 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area), | |
248 | 'set_next_input': $.proxy(this._handle_set_next_input, this) |
|
248 | 'set_next_input': $.proxy(this._handle_set_next_input, this), | |
|
249 | 'input_request': $.proxy(this._handle_input_request, this) | |||
249 | }; |
|
250 | }; | |
250 | var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false}); |
|
251 | var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false}); | |
251 | }; |
|
252 | }; | |
@@ -260,11 +261,24 b' var IPython = (function (IPython) {' | |||||
260 | $([IPython.events]).trigger('set_dirty.Notebook', {'value': true}); |
|
261 | $([IPython.events]).trigger('set_dirty.Notebook', {'value': true}); | |
261 | } |
|
262 | } | |
262 |
|
263 | |||
|
264 | /** | |||
|
265 | * @method _handle_set_next_input | |||
|
266 | * @private | |||
|
267 | */ | |||
263 | CodeCell.prototype._handle_set_next_input = function (text) { |
|
268 | CodeCell.prototype._handle_set_next_input = function (text) { | |
264 | var data = {'cell': this, 'text': text} |
|
269 | var data = {'cell': this, 'text': text} | |
265 | $([IPython.events]).trigger('set_next_input.Notebook', data); |
|
270 | $([IPython.events]).trigger('set_next_input.Notebook', data); | |
266 | } |
|
271 | } | |
267 |
|
272 | |||
|
273 | /** | |||
|
274 | * @method _handle_input_request | |||
|
275 | * @private | |||
|
276 | */ | |||
|
277 | CodeCell.prototype._handle_input_request = function (content) { | |||
|
278 | this.output_area.append_raw_input(content); | |||
|
279 | } | |||
|
280 | ||||
|
281 | ||||
268 | // Basic cell manipulation. |
|
282 | // Basic cell manipulation. | |
269 |
|
283 | |||
270 | CodeCell.prototype.select = function () { |
|
284 | CodeCell.prototype.select = function () { |
@@ -28,6 +28,7 b' var IPython = (function (IPython) {' | |||||
28 | this.kernel_id = null; |
|
28 | this.kernel_id = null; | |
29 | this.shell_channel = null; |
|
29 | this.shell_channel = null; | |
30 | this.iopub_channel = null; |
|
30 | this.iopub_channel = null; | |
|
31 | this.stdin_channel = null; | |||
31 | this.base_url = base_url; |
|
32 | this.base_url = base_url; | |
32 | this.running = false; |
|
33 | this.running = false; | |
33 | this.username = "username"; |
|
34 | this.username = "username"; | |
@@ -127,9 +128,12 b' var IPython = (function (IPython) {' | |||||
127 | var ws_url = this.ws_url + this.kernel_url; |
|
128 | var ws_url = this.ws_url + this.kernel_url; | |
128 | console.log("Starting WebSockets:", ws_url); |
|
129 | console.log("Starting WebSockets:", ws_url); | |
129 | this.shell_channel = new this.WebSocket(ws_url + "/shell"); |
|
130 | this.shell_channel = new this.WebSocket(ws_url + "/shell"); | |
|
131 | this.stdin_channel = new this.WebSocket(ws_url + "/stdin"); | |||
130 | this.iopub_channel = new this.WebSocket(ws_url + "/iopub"); |
|
132 | this.iopub_channel = new this.WebSocket(ws_url + "/iopub"); | |
131 | send_cookie = function(){ |
|
133 | send_cookie = function(){ | |
132 | this.send(document.cookie); |
|
134 | // send the session id so the Session object Python-side | |
|
135 | // has the same identity | |||
|
136 | this.send(that.session_id + ':' + document.cookie); | |||
133 | }; |
|
137 | }; | |
134 | var already_called_onclose = false; // only alert once |
|
138 | var already_called_onclose = false; // only alert once | |
135 | var ws_closed_early = function(evt){ |
|
139 | var ws_closed_early = function(evt){ | |
@@ -150,21 +154,26 b' var IPython = (function (IPython) {' | |||||
150 | that._websocket_closed(ws_url, false); |
|
154 | that._websocket_closed(ws_url, false); | |
151 | } |
|
155 | } | |
152 | }; |
|
156 | }; | |
153 | this.shell_channel.onopen = send_cookie; |
|
157 | var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel]; | |
154 | this.shell_channel.onclose = ws_closed_early; |
|
158 | for (var i=0; i < channels.length; i++) { | |
155 |
|
|
159 | channels[i].onopen = send_cookie; | |
156 |
|
|
160 | channels[i].onclose = ws_closed_early; | |
|
161 | } | |||
157 | // switch from early-close to late-close message after 1s |
|
162 | // switch from early-close to late-close message after 1s | |
158 | setTimeout(function() { |
|
163 | setTimeout(function() { | |
159 | if (that.shell_channel !== null) { |
|
164 | for (var i=0; i < channels.length; i++) { | |
160 | that.shell_channel.onclose = ws_closed_late; |
|
165 | if (channels[i] !== null) { | |
|
166 | channels[i].onclose = ws_closed_late; | |||
161 | } |
|
167 | } | |
162 | if (that.iopub_channel !== null) { |
|
|||
163 | that.iopub_channel.onclose = ws_closed_late; |
|
|||
164 | } |
|
168 | } | |
165 | }, 1000); |
|
169 | }, 1000); | |
166 | this.shell_channel.onmessage = $.proxy(this._handle_shell_reply,this); |
|
170 | this.shell_channel.onmessage = $.proxy(this._handle_shell_reply, this); | |
167 | this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply,this); |
|
171 | this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply, this); | |
|
172 | this.stdin_channel.onmessage = $.proxy(this._handle_input_request, this); | |||
|
173 | ||||
|
174 | $([IPython.events]).on('send_input_reply.Kernel', function(evt, data) { | |||
|
175 | that.send_input_reply(data); | |||
|
176 | }); | |||
168 | }; |
|
177 | }; | |
169 |
|
178 | |||
170 | /** |
|
179 | /** | |
@@ -172,16 +181,14 b' var IPython = (function (IPython) {' | |||||
172 | * @method stop_channels |
|
181 | * @method stop_channels | |
173 | */ |
|
182 | */ | |
174 | Kernel.prototype.stop_channels = function () { |
|
183 | Kernel.prototype.stop_channels = function () { | |
175 | if (this.shell_channel !== null) { |
|
184 | var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel]; | |
176 | this.shell_channel.onclose = function (evt) {}; |
|
185 | for (var i=0; i < channels.length; i++) { | |
177 | this.shell_channel.close(); |
|
186 | if ( channels[i] !== null ) { | |
178 | this.shell_channel = null; |
|
187 | channels[i].onclose = function (evt) {}; | |
179 | }; |
|
188 | channels[i].close(); | |
180 | if (this.iopub_channel !== null) { |
|
189 | } | |
181 | this.iopub_channel.onclose = function (evt) {}; |
|
|||
182 | this.iopub_channel.close(); |
|
|||
183 | this.iopub_channel = null; |
|
|||
184 | }; |
|
190 | }; | |
|
191 | this.shell_channel = this.iopub_channel = this.stdin_channel = null; | |||
185 | }; |
|
192 | }; | |
186 |
|
193 | |||
187 | // Main public methods. |
|
194 | // Main public methods. | |
@@ -284,6 +291,9 b' var IPython = (function (IPython) {' | |||||
284 | user_expressions : {}, |
|
291 | user_expressions : {}, | |
285 | allow_stdin : false |
|
292 | allow_stdin : false | |
286 | }; |
|
293 | }; | |
|
294 | if (callbacks.input_request !== undefined) { | |||
|
295 | content.allow_stdin = true; | |||
|
296 | } | |||
287 | $.extend(true, content, options) |
|
297 | $.extend(true, content, options) | |
288 | $([IPython.events]).trigger('execution_request.Kernel', {kernel: this, content:content}); |
|
298 | $([IPython.events]).trigger('execution_request.Kernel', {kernel: this, content:content}); | |
289 | var msg = this._get_msg("execute_request", content); |
|
299 | var msg = this._get_msg("execute_request", content); | |
@@ -343,8 +353,18 b' var IPython = (function (IPython) {' | |||||
343 | }; |
|
353 | }; | |
344 | }; |
|
354 | }; | |
345 |
|
355 | |||
|
356 | Kernel.prototype.send_input_reply = function (input) { | |||
|
357 | var content = { | |||
|
358 | value : input, | |||
|
359 | }; | |||
|
360 | $([IPython.events]).trigger('input_reply.Kernel', {kernel: this, content:content}); | |||
|
361 | var msg = this._get_msg("input_reply", content); | |||
|
362 | this.stdin_channel.send(JSON.stringify(msg)); | |||
|
363 | return msg.header.msg_id; | |||
|
364 | }; | |||
|
365 | ||||
346 |
|
366 | |||
347 |
// Reply handlers |
|
367 | // Reply handlers | |
348 |
|
368 | |||
349 | Kernel.prototype.get_callbacks_for_msg = function (msg_id) { |
|
369 | Kernel.prototype.get_callbacks_for_msg = function (msg_id) { | |
350 | var callbacks = this._msg_callbacks[msg_id]; |
|
370 | var callbacks = this._msg_callbacks[msg_id]; | |
@@ -433,6 +453,26 b' var IPython = (function (IPython) {' | |||||
433 | }; |
|
453 | }; | |
434 |
|
454 | |||
435 |
|
455 | |||
|
456 | Kernel.prototype._handle_input_request = function (e) { | |||
|
457 | var request = $.parseJSON(e.data); | |||
|
458 | var header = request.header; | |||
|
459 | var content = request.content; | |||
|
460 | var metadata = request.metadata; | |||
|
461 | var msg_type = header.msg_type; | |||
|
462 | if (msg_type !== 'input_request') { | |||
|
463 | console.log("Invalid input request!", request); | |||
|
464 | return; | |||
|
465 | } | |||
|
466 | var callbacks = this.get_callbacks_for_msg(request.parent_header.msg_id); | |||
|
467 | if (callbacks !== undefined) { | |||
|
468 | var cb = callbacks[msg_type]; | |||
|
469 | if (cb !== undefined) { | |||
|
470 | cb(content, metadata); | |||
|
471 | } | |||
|
472 | }; | |||
|
473 | }; | |||
|
474 | ||||
|
475 | ||||
436 | IPython.Kernel = Kernel; |
|
476 | IPython.Kernel = Kernel; | |
437 |
|
477 | |||
438 | return IPython; |
|
478 | return IPython; |
@@ -449,6 +449,55 b' var IPython = (function (IPython) {' | |||||
449 | element.append(toinsert); |
|
449 | element.append(toinsert); | |
450 | }; |
|
450 | }; | |
451 |
|
451 | |||
|
452 | OutputArea.prototype.append_raw_input = function (content) { | |||
|
453 | var that = this; | |||
|
454 | this.expand(); | |||
|
455 | this.flush_clear_timeout(); | |||
|
456 | var area = this.create_output_area(); | |||
|
457 | ||||
|
458 | area.append( | |||
|
459 | $("<div/>") | |||
|
460 | .addClass("box-flex1 output_subarea raw_input") | |||
|
461 | .append( | |||
|
462 | $("<span/>") | |||
|
463 | .addClass("input_prompt") | |||
|
464 | .text(content.prompt) | |||
|
465 | ) | |||
|
466 | .append( | |||
|
467 | $("<input/>") | |||
|
468 | .addClass("raw_input") | |||
|
469 | .attr('type', 'text') | |||
|
470 | .attr("size", 80) | |||
|
471 | .keydown(function (event, ui) { | |||
|
472 | // make sure we submit on enter, | |||
|
473 | // and don't re-execute the *cell* on shift-enter | |||
|
474 | if (event.which === utils.keycodes.ENTER) { | |||
|
475 | that._submit_raw_input(); | |||
|
476 | return false; | |||
|
477 | } | |||
|
478 | }) | |||
|
479 | ) | |||
|
480 | ); | |||
|
481 | this.element.append(area); | |||
|
482 | area.find("input.raw_input").focus(); | |||
|
483 | } | |||
|
484 | OutputArea.prototype._submit_raw_input = function (evt) { | |||
|
485 | var container = this.element.find("div.raw_input"); | |||
|
486 | var theprompt = container.find("span.input_prompt"); | |||
|
487 | var theinput = container.find("input.raw_input"); | |||
|
488 | var value = theinput.attr("value"); | |||
|
489 | var content = { | |||
|
490 | output_type : 'stream', | |||
|
491 | name : 'stdout', | |||
|
492 | text : theprompt.text() + value + '\n' | |||
|
493 | } | |||
|
494 | // remove form container | |||
|
495 | container.parent().remove(); | |||
|
496 | // replace with plaintext version in stdout | |||
|
497 | this.append_output(content, false); | |||
|
498 | $([IPython.events]).trigger('send_input_reply.Kernel', value); | |||
|
499 | } | |||
|
500 | ||||
452 |
|
501 | |||
453 | OutputArea.prototype.handle_clear_output = function (content) { |
|
502 | OutputArea.prototype.handle_clear_output = function (content) { | |
454 | this.clear_output(content.stdout, content.stderr, content.other); |
|
503 | this.clear_output(content.stdout, content.stderr, content.other); |
@@ -477,3 +477,26 b' a.heading-anchor:link, a.heading-anchor:visited {' | |||||
477 | text-decoration: none; |
|
477 | text-decoration: none; | |
478 | color: inherit; |
|
478 | color: inherit; | |
479 | } |
|
479 | } | |
|
480 | ||||
|
481 | /* raw_input styles */ | |||
|
482 | ||||
|
483 | div.raw_input { | |||
|
484 | padding-top: 0px; | |||
|
485 | padding-bottom: 0px; | |||
|
486 | height: 1em; | |||
|
487 | line-height: 1em; | |||
|
488 | font-family: monospace; | |||
|
489 | } | |||
|
490 | span.input_prompt { | |||
|
491 | font-family: inherit; | |||
|
492 | } | |||
|
493 | input.raw_input { | |||
|
494 | font-family: inherit; | |||
|
495 | font-size: inherit; | |||
|
496 | color: inherit; | |||
|
497 | width: auto; | |||
|
498 | margin: -2px 0px 0px 1px; | |||
|
499 | padding-left: 1px; | |||
|
500 | padding-top: 2px; | |||
|
501 | height: 1em; | |||
|
502 | } |
@@ -746,6 +746,15 b' class Kernel(Configurable):' | |||||
746 | # Flush output before making the request. |
|
746 | # Flush output before making the request. | |
747 | sys.stderr.flush() |
|
747 | sys.stderr.flush() | |
748 | sys.stdout.flush() |
|
748 | sys.stdout.flush() | |
|
749 | # flush the stdin socket, to purge stale replies | |||
|
750 | while True: | |||
|
751 | try: | |||
|
752 | self.stdin_socket.recv_multipart(zmq.NOBLOCK) | |||
|
753 | except zmq.ZMQError as e: | |||
|
754 | if e.errno == zmq.EAGAIN: | |||
|
755 | break | |||
|
756 | else: | |||
|
757 | raise | |||
749 |
|
758 | |||
750 | # Send the input request. |
|
759 | # Send the input request. | |
751 | content = json_clean(dict(prompt=prompt)) |
|
760 | content = json_clean(dict(prompt=prompt)) |
General Comments 0
You need to be logged in to leave comments.
Login now