##// END OF EJS Templates
Merge pull request #4195 from minrk/widget-msg...
Min RK -
r13319:de09adf2 merge
parent child Browse files
Show More
@@ -0,0 +1,198 b''
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2013 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
8 //============================================================================
9 // Comm and CommManager bases
10 //============================================================================
11 /**
12 * Base Comm classes
13 * @module IPython
14 * @namespace IPython
15 * @submodule comm
16 */
17
18 var IPython = (function (IPython) {
19 "use strict";
20
21 //-----------------------------------------------------------------------
22 // CommManager class
23 //-----------------------------------------------------------------------
24
25 var CommManager = function (kernel) {
26 this.comms = {};
27 this.targets = {};
28 if (kernel !== undefined) {
29 this.init_kernel(kernel);
30 }
31 };
32
33 CommManager.prototype.init_kernel = function (kernel) {
34 // connect the kernel, and register message handlers
35 this.kernel = kernel;
36 var msg_types = ['comm_open', 'comm_msg', 'comm_close'];
37 for (var i = 0; i < msg_types.length; i++) {
38 var msg_type = msg_types[i];
39 kernel.register_iopub_handler(msg_type, $.proxy(this[msg_type], this));
40 }
41 };
42
43 CommManager.prototype.new_comm = function (target_name, data, callbacks, metadata) {
44 // Create a new Comm, register it, and open its Kernel-side counterpart
45 // Mimics the auto-registration in `Comm.__init__` in the IPython Comm
46 var comm = new Comm(target_name);
47 this.register_comm(comm);
48 comm.open(data, callbacks, metadata);
49 return comm;
50 };
51
52 CommManager.prototype.register_target = function (target_name, f) {
53 // Register a target function for a given target name
54 this.targets[target_name] = f;
55 };
56
57 CommManager.prototype.unregister_target = function (target_name, f) {
58 // Unregister a target function for a given target name
59 delete this.targets[target_name];
60 };
61
62 CommManager.prototype.register_comm = function (comm) {
63 // Register a comm in the mapping
64 this.comms[comm.comm_id] = comm;
65 comm.kernel = this.kernel;
66 return comm.comm_id;
67 };
68
69 CommManager.prototype.unregister_comm = function (comm_id) {
70 // Remove a comm from the mapping
71 delete this.comms[comm_id];
72 };
73
74 // comm message handlers
75
76 CommManager.prototype.comm_open = function (msg) {
77 var content = msg.content;
78 var f = this.targets[content.target_name];
79 if (f === undefined) {
80 console.log("No such target registered: ", content.target_name);
81 console.log("Available targets are: ", this.targets);
82 return;
83 }
84 var comm = new Comm(content.target_name, content.comm_id);
85 this.register_comm(comm);
86 try {
87 f(comm, msg);
88 } catch (e) {
89 console.log("Exception opening new comm:", e, msg);
90 comm.close();
91 this.unregister_comm(comm);
92 }
93 };
94
95 CommManager.prototype.comm_close = function (msg) {
96 var content = msg.content;
97 var comm = this.comms[content.comm_id];
98 if (comm === undefined) {
99 return;
100 }
101 delete this.comms[content.comm_id];
102 try {
103 comm.handle_close(msg);
104 } catch (e) {
105 console.log("Exception closing comm: ", e, msg);
106 }
107 };
108
109 CommManager.prototype.comm_msg = function (msg) {
110 var content = msg.content;
111 var comm = this.comms[content.comm_id];
112 if (comm === undefined) {
113 return;
114 }
115 try {
116 comm.handle_msg(msg);
117 } catch (e) {
118 console.log("Exception handling comm msg: ", e, msg);
119 }
120 };
121
122 //-----------------------------------------------------------------------
123 // Comm base class
124 //-----------------------------------------------------------------------
125
126 var Comm = function (target_name, comm_id) {
127 this.target_name = target_name;
128 this.comm_id = comm_id || IPython.utils.uuid();
129 this._msg_callback = this._close_callback = null;
130 };
131
132 // methods for sending messages
133 Comm.prototype.open = function (data, callbacks, metadata) {
134 var content = {
135 comm_id : this.comm_id,
136 target_name : this.target_name,
137 data : data || {},
138 };
139 return this.kernel.send_shell_message("comm_open", content, callbacks, metadata);
140 };
141
142 Comm.prototype.send = function (data, callbacks, metadata) {
143 var content = {
144 comm_id : this.comm_id,
145 data : data || {},
146 };
147 return this.kernel.send_shell_message("comm_msg", content, callbacks, metadata);
148 };
149
150 Comm.prototype.close = function (data, callbacks, metadata) {
151 var content = {
152 comm_id : this.comm_id,
153 data : data || {},
154 };
155 return this.kernel.send_shell_message("comm_close", content, callbacks, metadata);
156 };
157
158 // methods for registering callbacks for incoming messages
159 Comm.prototype._register_callback = function (key, callback) {
160 this['_' + key + '_callback'] = callback;
161 };
162
163 Comm.prototype.on_msg = function (callback) {
164 this._register_callback('msg', callback);
165 };
166
167 Comm.prototype.on_close = function (callback) {
168 this._register_callback('close', callback);
169 };
170
171 // methods for handling incoming messages
172
173 Comm.prototype._maybe_callback = function (key, msg) {
174 var callback = this['_' + key + '_callback'];
175 if (callback) {
176 try {
177 callback(msg);
178 } catch (e) {
179 console.log("Exception in Comm callback", e, msg);
180 }
181 }
182 };
183
184 Comm.prototype.handle_msg = function (msg) {
185 this._maybe_callback('msg', msg);
186 };
187
188 Comm.prototype.handle_close = function (msg) {
189 this._maybe_callback('close', msg);
190 };
191
192 IPython.CommManager = CommManager;
193 IPython.Comm = Comm;
194
195 return IPython;
196
197 }(IPython));
198
@@ -0,0 +1,2 b''
1 from .manager import *
2 from .comm import *
@@ -0,0 +1,140 b''
1 """Base class for a Comm"""
2
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2013 The IPython Development Team
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9
10 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
13
14 import uuid
15
16 from IPython.config import LoggingConfigurable
17 from IPython.core.getipython import get_ipython
18
19 from IPython.utils.traitlets import Instance, Unicode, Bytes, Bool, Dict, Any
20
21 #-----------------------------------------------------------------------------
22 # Code
23 #-----------------------------------------------------------------------------
24
25 class Comm(LoggingConfigurable):
26
27 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
28 def _shell_default(self):
29 return get_ipython()
30
31 iopub_socket = Any()
32 def _iopub_socket_default(self):
33 return self.shell.kernel.iopub_socket
34 session = Instance('IPython.kernel.zmq.session.Session')
35 def _session_default(self):
36 if self.shell is None:
37 return
38 return self.shell.kernel.session
39
40 target_name = Unicode('comm')
41
42 topic = Bytes()
43 def _topic_default(self):
44 return ('comm-%s' % self.comm_id).encode('ascii')
45
46 _open_data = Dict(help="data dict, if any, to be included in comm_open")
47 _close_data = Dict(help="data dict, if any, to be included in comm_close")
48
49 _msg_callback = Any()
50 _close_callback = Any()
51
52 _closed = Bool(False)
53 comm_id = Unicode()
54 def _comm_id_default(self):
55 return uuid.uuid4().hex
56
57 primary = Bool(True, help="Am I the primary or secondary Comm?")
58
59 def __init__(self, target_name='', data=None, **kwargs):
60 if target_name:
61 kwargs['target_name'] = target_name
62 super(Comm, self).__init__(**kwargs)
63 get_ipython().comm_manager.register_comm(self)
64 if self.primary:
65 # I am primary, open my peer.
66 self.open(data)
67
68 def _publish_msg(self, msg_type, data=None, metadata=None, **keys):
69 """Helper for sending a comm message on IOPub"""
70 data = {} if data is None else data
71 metadata = {} if metadata is None else metadata
72 self.session.send(self.iopub_socket, msg_type,
73 dict(data=data, comm_id=self.comm_id, **keys),
74 metadata=metadata,
75 parent=self.shell.get_parent(),
76 ident=self.topic,
77 )
78
79 def __del__(self):
80 """trigger close on gc"""
81 self.close()
82
83 # publishing messages
84
85 def open(self, data=None, metadata=None):
86 """Open the frontend-side version of this comm"""
87 if data is None:
88 data = self._open_data
89 self._publish_msg('comm_open', data, metadata, target_name=self.target_name)
90
91 def close(self, data=None, metadata=None):
92 """Close the frontend-side version of this comm"""
93 if self._closed:
94 # only close once
95 return
96 if data is None:
97 data = self._close_data
98 self._publish_msg('comm_close', data, metadata)
99 self._closed = True
100
101 def send(self, data=None, metadata=None):
102 """Send a message to the frontend-side version of this comm"""
103 self._publish_msg('comm_msg', data, metadata)
104
105 # registering callbacks
106
107 def on_close(self, callback):
108 """Register a callback for comm_close
109
110 Will be called with the `data` of the close message.
111
112 Call `on_close(None)` to disable an existing callback.
113 """
114 self._close_callback = callback
115
116 def on_msg(self, callback):
117 """Register a callback for comm_msg
118
119 Will be called with the `data` of any comm_msg messages.
120
121 Call `on_msg(None)` to disable an existing callback.
122 """
123 self._msg_callback = callback
124
125 # handling of incoming messages
126
127 def handle_close(self, msg):
128 """Handle a comm_close message"""
129 self.log.debug("handle_close[%s](%s)", self.comm_id, msg)
130 if self._close_callback:
131 self._close_callback(msg)
132
133 def handle_msg(self, msg):
134 """Handle a comm_msg message"""
135 self.log.debug("handle_msg[%s](%s)", self.comm_id, msg)
136 if self._msg_callback:
137 self._msg_callback(msg)
138
139
140 __all__ = ['Comm']
@@ -0,0 +1,185 b''
1 """Base class to manage comms"""
2
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2013 The IPython Development Team
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9
10 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
13
14 import sys
15
16 from IPython.config import LoggingConfigurable
17 from IPython.core.prompts import LazyEvaluate
18 from IPython.core.getipython import get_ipython
19
20 from IPython.utils.importstring import import_item
21 from IPython.utils.traitlets import Instance, Unicode, Dict, Any
22
23 from .comm import Comm
24
25 #-----------------------------------------------------------------------------
26 # Code
27 #-----------------------------------------------------------------------------
28
29 def lazy_keys(dikt):
30 """Return lazy-evaluated string representation of a dictionary's keys
31
32 Key list is only constructed if it will actually be used.
33 Used for debug-logging.
34 """
35 return LazyEvaluate(lambda d: list(d.keys()))
36
37
38 def with_output(method):
39 """method decorator for ensuring output is handled properly in a message handler
40
41 - sets parent header before entering the method
42 - publishes busy/idle
43 - flushes stdout/stderr after
44 """
45 def method_with_output(self, stream, ident, msg):
46 parent = msg['header']
47 self.shell.set_parent(parent)
48 self.shell.kernel._publish_status('busy', parent)
49 try:
50 return method(self, stream, ident, msg)
51 finally:
52 sys.stdout.flush()
53 sys.stderr.flush()
54 self.shell.kernel._publish_status('idle', parent)
55
56 return method_with_output
57
58
59 class CommManager(LoggingConfigurable):
60 """Manager for Comms in the Kernel"""
61
62 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
63 def _shell_default(self):
64 return get_ipython()
65 iopub_socket = Any()
66 def _iopub_socket_default(self):
67 return self.shell.kernel.iopub_socket
68 session = Instance('IPython.kernel.zmq.session.Session')
69 def _session_default(self):
70 if self.shell is None:
71 return
72 return self.shell.kernel.session
73
74 comms = Dict()
75 targets = Dict()
76
77 # Public APIs
78
79 def register_target(self, target_name, f):
80 """Register a callable f for a given target name
81
82 f will be called with two arguments when a comm_open message is received with `target`:
83
84 - the Comm instance
85 - the `comm_open` message itself.
86
87 f can be a Python callable or an import string for one.
88 """
89 if isinstance(f, basestring):
90 f = import_item(f)
91
92 self.targets[target_name] = f
93
94 def unregister_target(self, target_name, f):
95 """Unregister a callable registered with register_target"""
96 return self.targets.pop(target_name);
97
98 def register_comm(self, comm):
99 """Register a new comm"""
100 comm_id = comm.comm_id
101 comm.shell = self.shell
102 comm.iopub_socket = self.iopub_socket
103 self.comms[comm_id] = comm
104 return comm_id
105
106 def unregister_comm(self, comm_id):
107 """Unregister a comm, and close its counterpart"""
108 # unlike get_comm, this should raise a KeyError
109 comm = self.comms.pop(comm_id)
110 comm.close()
111
112 def get_comm(self, comm_id):
113 """Get a comm with a particular id
114
115 Returns the comm if found, otherwise None.
116
117 This will not raise an error,
118 it will log messages if the comm cannot be found.
119 """
120 if comm_id not in self.comms:
121 self.log.error("No such comm: %s", comm_id)
122 self.log.debug("Current comms: %s", lazy_keys(self.comms))
123 return
124 # call, because we store weakrefs
125 comm = self.comms[comm_id]
126 return comm
127
128 # Message handlers
129 @with_output
130 def comm_open(self, stream, ident, msg):
131 """Handler for comm_open messages"""
132 content = msg['content']
133 comm_id = content['comm_id']
134 target_name = content['target_name']
135 f = self.targets.get(target_name, None)
136 comm = Comm(comm_id=comm_id,
137 shell=self.shell,
138 iopub_socket=self.iopub_socket,
139 primary=False,
140 )
141 if f is None:
142 self.log.error("No such comm target registered: %s", target_name)
143 comm.close()
144 return
145 self.register_comm(comm)
146 try:
147 f(comm, msg)
148 except Exception:
149 self.log.error("Exception opening comm with target: %s", target_name, exc_info=True)
150 comm.close()
151 self.unregister_comm(comm_id)
152
153 @with_output
154 def comm_msg(self, stream, ident, msg):
155 """Handler for comm_msg messages"""
156 content = msg['content']
157 comm_id = content['comm_id']
158 comm = self.get_comm(comm_id)
159 if comm is None:
160 # no such comm
161 return
162 try:
163 comm.handle_msg(msg)
164 except Exception:
165 self.log.error("Exception in comm_msg for %s", comm_id, exc_info=True)
166
167 @with_output
168 def comm_close(self, stream, ident, msg):
169 """Handler for comm_close messages"""
170 content = msg['content']
171 comm_id = content['comm_id']
172 comm = self.get_comm(comm_id)
173 if comm is None:
174 # no such comm
175 self.log.debug("No such comm to close: %s", comm_id)
176 return
177 del self.comms[comm_id]
178
179 try:
180 comm.handle_close(msg)
181 except Exception:
182 self.log.error("Exception handling comm_close for %s", comm_id, exc_info=True)
183
184
185 __all__ = ['CommManager']
@@ -0,0 +1,269 b''
1 {
2 "metadata": {
3 "name": ""
4 },
5 "nbformat": 3,
6 "nbformat_minor": 0,
7 "worksheets": [
8 {
9 "cells": [
10 {
11 "cell_type": "heading",
12 "level": 1,
13 "metadata": {},
14 "source": [
15 "Basic Output"
16 ]
17 },
18 {
19 "cell_type": "code",
20 "collapsed": false,
21 "input": [
22 "from IPython.display import display"
23 ],
24 "language": "python",
25 "metadata": {},
26 "outputs": []
27 },
28 {
29 "cell_type": "code",
30 "collapsed": false,
31 "input": [
32 "print 'hi'"
33 ],
34 "language": "python",
35 "metadata": {},
36 "outputs": []
37 },
38 {
39 "cell_type": "code",
40 "collapsed": false,
41 "input": [
42 "display('hi')"
43 ],
44 "language": "python",
45 "metadata": {},
46 "outputs": []
47 },
48 {
49 "cell_type": "code",
50 "collapsed": false,
51 "input": [
52 "1"
53 ],
54 "language": "python",
55 "metadata": {},
56 "outputs": []
57 },
58 {
59 "cell_type": "code",
60 "collapsed": false,
61 "input": [
62 "%matplotlib inline\n",
63 "import matplotlib.pyplot as plt\n",
64 "plt.plot([1,3,2])"
65 ],
66 "language": "python",
67 "metadata": {},
68 "outputs": []
69 },
70 {
71 "cell_type": "code",
72 "collapsed": false,
73 "input": [
74 "%%javascript\n",
75 "console.log(\"I ran!\");"
76 ],
77 "language": "python",
78 "metadata": {},
79 "outputs": []
80 },
81 {
82 "cell_type": "code",
83 "collapsed": false,
84 "input": [
85 "%%html\n",
86 "<b>bold</b>"
87 ],
88 "language": "python",
89 "metadata": {},
90 "outputs": []
91 },
92 {
93 "cell_type": "code",
94 "collapsed": false,
95 "input": [
96 "%%latex\n",
97 "$$\n",
98 "a = 5\n",
99 "$$"
100 ],
101 "language": "python",
102 "metadata": {},
103 "outputs": []
104 },
105 {
106 "cell_type": "heading",
107 "level": 1,
108 "metadata": {},
109 "source": [
110 "input_request"
111 ]
112 },
113 {
114 "cell_type": "code",
115 "collapsed": false,
116 "input": [
117 "raw_input(\"prompt > \")"
118 ],
119 "language": "python",
120 "metadata": {},
121 "outputs": []
122 },
123 {
124 "cell_type": "heading",
125 "level": 1,
126 "metadata": {},
127 "source": [
128 "set_next_input"
129 ]
130 },
131 {
132 "cell_type": "code",
133 "collapsed": false,
134 "input": [
135 "%%writefile tst.py\n",
136 "def foo():\n",
137 " pass\n"
138 ],
139 "language": "python",
140 "metadata": {},
141 "outputs": []
142 },
143 {
144 "cell_type": "code",
145 "collapsed": false,
146 "input": [
147 "%load tst.py"
148 ],
149 "language": "python",
150 "metadata": {},
151 "outputs": []
152 },
153 {
154 "cell_type": "heading",
155 "level": 1,
156 "metadata": {},
157 "source": [
158 "Pager in execute_reply"
159 ]
160 },
161 {
162 "cell_type": "code",
163 "collapsed": false,
164 "input": [
165 "plt?"
166 ],
167 "language": "python",
168 "metadata": {},
169 "outputs": []
170 },
171 {
172 "cell_type": "heading",
173 "level": 1,
174 "metadata": {},
175 "source": [
176 "object_info"
177 ]
178 },
179 {
180 "cell_type": "code",
181 "collapsed": false,
182 "input": [
183 "# press tab after parentheses\n",
184 "int("
185 ],
186 "language": "python",
187 "metadata": {},
188 "outputs": []
189 },
190 {
191 "cell_type": "heading",
192 "level": 1,
193 "metadata": {},
194 "source": [
195 "complete"
196 ]
197 },
198 {
199 "cell_type": "code",
200 "collapsed": false,
201 "input": [
202 "# pres tab after f\n",
203 "f"
204 ],
205 "language": "python",
206 "metadata": {},
207 "outputs": []
208 },
209 {
210 "cell_type": "heading",
211 "level": 1,
212 "metadata": {},
213 "source": [
214 "clear_output"
215 ]
216 },
217 {
218 "cell_type": "code",
219 "collapsed": false,
220 "input": [
221 "import sys\n",
222 "from IPython.display import clear_output"
223 ],
224 "language": "python",
225 "metadata": {},
226 "outputs": []
227 },
228 {
229 "cell_type": "code",
230 "collapsed": false,
231 "input": [
232 "for i in range(10):\n",
233 " clear_output()\n",
234 " time.sleep(0.25)\n",
235 " print i\n",
236 " sys.stdout.flush()\n",
237 " time.sleep(0.25)\n"
238 ],
239 "language": "python",
240 "metadata": {},
241 "outputs": []
242 },
243 {
244 "cell_type": "code",
245 "collapsed": false,
246 "input": [
247 "for i in range(10):\n",
248 " clear_output(wait=True)\n",
249 " time.sleep(0.25)\n",
250 " print i\n",
251 " sys.stdout.flush()\n"
252 ],
253 "language": "python",
254 "metadata": {},
255 "outputs": []
256 },
257 {
258 "cell_type": "code",
259 "collapsed": false,
260 "input": [],
261 "language": "python",
262 "metadata": {},
263 "outputs": []
264 }
265 ],
266 "metadata": {}
267 }
268 ]
269 } No newline at end of file
@@ -507,6 +507,7 b' class InteractiveShell(SingletonConfigurable):'
507 self.init_pdb()
507 self.init_pdb()
508 self.init_extension_manager()
508 self.init_extension_manager()
509 self.init_payload()
509 self.init_payload()
510 self.init_comms()
510 self.hooks.late_startup_hook()
511 self.hooks.late_startup_hook()
511 atexit.register(self.atexit_operations)
512 atexit.register(self.atexit_operations)
512
513
@@ -2317,7 +2318,15 b' class InteractiveShell(SingletonConfigurable):'
2317 def init_payload(self):
2318 def init_payload(self):
2318 self.payload_manager = PayloadManager(parent=self)
2319 self.payload_manager = PayloadManager(parent=self)
2319 self.configurables.append(self.payload_manager)
2320 self.configurables.append(self.payload_manager)
2320
2321
2322 #-------------------------------------------------------------------------
2323 # Things related to widgets
2324 #-------------------------------------------------------------------------
2325
2326 def init_comms(self):
2327 # not implemented in the base class
2328 pass
2329
2321 #-------------------------------------------------------------------------
2330 #-------------------------------------------------------------------------
2322 # Things related to the prefilter
2331 # Things related to the prefilter
2323 #-------------------------------------------------------------------------
2332 #-------------------------------------------------------------------------
@@ -390,6 +390,18 b' IPython.utils = (function (IPython) {'
390 test.remove();
390 test.remove();
391 return Math.floor(points*pixel_per_point);
391 return Math.floor(points*pixel_per_point);
392 };
392 };
393
394 var always_new = function (constructor) {
395 // wrapper around contructor to avoid requiring `var a = new constructor()`
396 // useful for passing constructors as callbacks,
397 // not for programmer laziness.
398 // from http://programmers.stackexchange.com/questions/118798
399 return function () {
400 var obj = Object.create(constructor.prototype);
401 constructor.apply(obj, arguments);
402 return obj;
403 };
404 };
393
405
394
406
395 var url_path_join = function () {
407 var url_path_join = function () {
@@ -447,6 +459,7 b' IPython.utils = (function (IPython) {'
447 points_to_pixels : points_to_pixels,
459 points_to_pixels : points_to_pixels,
448 url_path_join : url_path_join,
460 url_path_join : url_path_join,
449 splitext : splitext,
461 splitext : splitext,
462 always_new : always_new,
450 browser : browser
463 browser : browser
451 };
464 };
452
465
@@ -17,7 +17,7 b''
17
17
18
18
19 /* local util for codemirror */
19 /* local util for codemirror */
20 var posEq = function(a, b) {return a.line == b.line && a.ch == b.ch;}
20 var posEq = function(a, b) {return a.line == b.line && a.ch == b.ch;};
21
21
22 /**
22 /**
23 *
23 *
@@ -27,16 +27,16 b' var posEq = function(a, b) {return a.line == b.line && a.ch == b.ch;}'
27 */
27 */
28 CodeMirror.commands.delSpaceToPrevTabStop = function(cm){
28 CodeMirror.commands.delSpaceToPrevTabStop = function(cm){
29 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
29 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
30 if (!posEq(from, to)) {cm.replaceRange("", from, to); return}
30 if (!posEq(from, to)) { cm.replaceRange("", from, to); return; }
31 var cur = cm.getCursor(), line = cm.getLine(cur.line);
31 var cur = cm.getCursor(), line = cm.getLine(cur.line);
32 var tabsize = cm.getOption('tabSize');
32 var tabsize = cm.getOption('tabSize');
33 var chToPrevTabStop = cur.ch-(Math.ceil(cur.ch/tabsize)-1)*tabsize;
33 var chToPrevTabStop = cur.ch-(Math.ceil(cur.ch/tabsize)-1)*tabsize;
34 var from = {ch:cur.ch-chToPrevTabStop,line:cur.line}
34 from = {ch:cur.ch-chToPrevTabStop,line:cur.line};
35 var select = cm.getRange(from,cur)
35 var select = cm.getRange(from,cur);
36 if( select.match(/^\ +$/) != null){
36 if( select.match(/^\ +$/) !== null){
37 cm.replaceRange("",from,cur)
37 cm.replaceRange("",from,cur);
38 } else {
38 } else {
39 cm.deleteH(-1,"char")
39 cm.deleteH(-1,"char");
40 }
40 }
41 };
41 };
42
42
@@ -104,7 +104,7 b' var IPython = (function (IPython) {'
104 * @method auto_highlight
104 * @method auto_highlight
105 */
105 */
106 CodeCell.prototype.auto_highlight = function () {
106 CodeCell.prototype.auto_highlight = function () {
107 this._auto_highlight(IPython.config.cell_magic_highlight)
107 this._auto_highlight(IPython.config.cell_magic_highlight);
108 };
108 };
109
109
110 /** @method create_element */
110 /** @method create_element */
@@ -117,7 +117,7 b' var IPython = (function (IPython) {'
117 this.celltoolbar = new IPython.CellToolbar(this);
117 this.celltoolbar = new IPython.CellToolbar(this);
118
118
119 var input = $('<div></div>').addClass('input');
119 var input = $('<div></div>').addClass('input');
120 var vbox = $('<div/>').addClass('vbox box-flex1')
120 var vbox = $('<div/>').addClass('vbox box-flex1');
121 input.append($('<div/>').addClass('prompt input_prompt'));
121 input.append($('<div/>').addClass('prompt input_prompt'));
122 vbox.append(this.celltoolbar.element);
122 vbox.append(this.celltoolbar.element);
123 var input_area = $('<div/>').addClass('input_area');
123 var input_area = $('<div/>').addClass('input_area');
@@ -152,7 +152,7 b' var IPython = (function (IPython) {'
152 // they are sent, and remove tooltip if any, except for tab again
152 // they are sent, and remove tooltip if any, except for tab again
153 if (event.type === 'keydown' && event.which != key.TAB ) {
153 if (event.type === 'keydown' && event.which != key.TAB ) {
154 IPython.tooltip.remove_and_cancel_tooltip();
154 IPython.tooltip.remove_and_cancel_tooltip();
155 };
155 }
156
156
157 var cur = editor.getCursor();
157 var cur = editor.getCursor();
158 if (event.keyCode === key.ENTER){
158 if (event.keyCode === key.ENTER){
@@ -177,7 +177,7 b' var IPython = (function (IPython) {'
177 return false;
177 return false;
178 } else {
178 } else {
179 return true;
179 return true;
180 };
180 }
181 } else if (event.which === key.ESC) {
181 } else if (event.which === key.ESC) {
182 return IPython.tooltip.remove_and_cancel_tooltip(true);
182 return IPython.tooltip.remove_and_cancel_tooltip(true);
183 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
183 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
@@ -188,7 +188,7 b' var IPython = (function (IPython) {'
188 return false;
188 return false;
189 } else {
189 } else {
190 return true;
190 return true;
191 };
191 }
192 } else if (event.keyCode === key.TAB && event.type == 'keydown' && event.shiftKey) {
192 } else if (event.keyCode === key.TAB && event.type == 'keydown' && event.shiftKey) {
193 if (editor.somethingSelected()){
193 if (editor.somethingSelected()){
194 var anchor = editor.getCursor("anchor");
194 var anchor = editor.getCursor("anchor");
@@ -203,7 +203,7 b' var IPython = (function (IPython) {'
203 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
203 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
204 // Tab completion.
204 // Tab completion.
205 //Do not trim here because of tooltip
205 //Do not trim here because of tooltip
206 if (editor.somethingSelected()){return false}
206 if (editor.somethingSelected()) { return false; }
207 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
207 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
208 if (pre_cursor.trim() === "") {
208 if (pre_cursor.trim() === "") {
209 // Don't autocomplete if the part of the line before the cursor
209 // Don't autocomplete if the part of the line before the cursor
@@ -219,12 +219,12 b' var IPython = (function (IPython) {'
219 event.stop();
219 event.stop();
220 this.completer.startCompletion();
220 this.completer.startCompletion();
221 return true;
221 return true;
222 };
222 }
223 } else {
223 } else {
224 // keypress/keyup also trigger on TAB press, and we don't want to
224 // keypress/keyup also trigger on TAB press, and we don't want to
225 // use those to disable tab completion.
225 // use those to disable tab completion.
226 return false;
226 return false;
227 };
227 }
228 return false;
228 return false;
229 };
229 };
230
230
@@ -233,7 +233,7 b' var IPython = (function (IPython) {'
233
233
234 CodeCell.prototype.set_kernel = function (kernel) {
234 CodeCell.prototype.set_kernel = function (kernel) {
235 this.kernel = kernel;
235 this.kernel = kernel;
236 }
236 };
237
237
238 /**
238 /**
239 * Execute current code cell to the kernel
239 * Execute current code cell to the kernel
@@ -243,42 +243,65 b' var IPython = (function (IPython) {'
243 this.output_area.clear_output();
243 this.output_area.clear_output();
244 this.set_input_prompt('*');
244 this.set_input_prompt('*');
245 this.element.addClass("running");
245 this.element.addClass("running");
246 var callbacks = {
246 if (this.last_msg_id) {
247 'execute_reply': $.proxy(this._handle_execute_reply, this),
247 this.kernel.clear_callbacks_for_msg(this.last_msg_id);
248 'output': $.proxy(this.output_area.handle_output, this.output_area),
248 }
249 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
249 var callbacks = this.get_callbacks();
250 'set_next_input': $.proxy(this._handle_set_next_input, this),
250
251 'input_request': $.proxy(this._handle_input_request, this)
252 };
253 this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
251 this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
254 };
252 };
253
254 /**
255 * Construct the default callbacks for
256 * @method get_callbacks
257 */
258 CodeCell.prototype.get_callbacks = function () {
259 return {
260 shell : {
261 reply : $.proxy(this._handle_execute_reply, this),
262 payload : {
263 set_next_input : $.proxy(this._handle_set_next_input, this),
264 page : $.proxy(this._open_with_pager, this)
265 }
266 },
267 iopub : {
268 output : $.proxy(this.output_area.handle_output, this.output_area),
269 clear_output : $.proxy(this.output_area.handle_clear_output, this.output_area),
270 },
271 input : $.proxy(this._handle_input_request, this)
272 };
273 };
274
275 CodeCell.prototype._open_with_pager = function (payload) {
276 $([IPython.events]).trigger('open_with_text.Pager', payload);
277 };
255
278
256 /**
279 /**
257 * @method _handle_execute_reply
280 * @method _handle_execute_reply
258 * @private
281 * @private
259 */
282 */
260 CodeCell.prototype._handle_execute_reply = function (content) {
283 CodeCell.prototype._handle_execute_reply = function (msg) {
261 this.set_input_prompt(content.execution_count);
284 this.set_input_prompt(msg.content.execution_count);
262 this.element.removeClass("running");
285 this.element.removeClass("running");
263 $([IPython.events]).trigger('set_dirty.Notebook', {value: true});
286 $([IPython.events]).trigger('set_dirty.Notebook', {value: true});
264 }
287 };
265
288
266 /**
289 /**
267 * @method _handle_set_next_input
290 * @method _handle_set_next_input
268 * @private
291 * @private
269 */
292 */
270 CodeCell.prototype._handle_set_next_input = function (text) {
293 CodeCell.prototype._handle_set_next_input = function (payload) {
271 var data = {'cell': this, 'text': text}
294 var data = {'cell': this, 'text': payload.text};
272 $([IPython.events]).trigger('set_next_input.Notebook', data);
295 $([IPython.events]).trigger('set_next_input.Notebook', data);
273 }
296 };
274
297
275 /**
298 /**
276 * @method _handle_input_request
299 * @method _handle_input_request
277 * @private
300 * @private
278 */
301 */
279 CodeCell.prototype._handle_input_request = function (content) {
302 CodeCell.prototype._handle_input_request = function (msg) {
280 this.output_area.append_raw_input(content);
303 this.output_area.append_raw_input(msg);
281 }
304 };
282
305
283
306
284 // Basic cell manipulation.
307 // Basic cell manipulation.
@@ -328,21 +351,23 b' var IPython = (function (IPython) {'
328
351
329 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
352 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
330 var ns = prompt_value || "&nbsp;";
353 var ns = prompt_value || "&nbsp;";
331 return 'In&nbsp;[' + ns + ']:'
354 return 'In&nbsp;[' + ns + ']:';
332 };
355 };
333
356
334 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
357 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
335 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
358 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
336 for(var i=1; i < lines_number; i++){html.push(['...:'])};
359 for(var i=1; i < lines_number; i++) {
337 return html.join('</br>')
360 html.push(['...:']);
361 }
362 return html.join('<br/>');
338 };
363 };
339
364
340 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
365 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
341
366
342
367
343 CodeCell.prototype.set_input_prompt = function (number) {
368 CodeCell.prototype.set_input_prompt = function (number) {
344 var nline = 1
369 var nline = 1;
345 if( this.code_mirror != undefined) {
370 if (this.code_mirror !== undefined) {
346 nline = this.code_mirror.lineCount();
371 nline = this.code_mirror.lineCount();
347 }
372 }
348 this.input_prompt_number = number;
373 this.input_prompt_number = number;
@@ -407,16 +432,16 b' var IPython = (function (IPython) {'
407 this.set_input_prompt(data.prompt_number);
432 this.set_input_prompt(data.prompt_number);
408 } else {
433 } else {
409 this.set_input_prompt();
434 this.set_input_prompt();
410 };
435 }
411 this.output_area.fromJSON(data.outputs);
436 this.output_area.fromJSON(data.outputs);
412 if (data.collapsed !== undefined) {
437 if (data.collapsed !== undefined) {
413 if (data.collapsed) {
438 if (data.collapsed) {
414 this.collapse();
439 this.collapse();
415 } else {
440 } else {
416 this.expand();
441 this.expand();
417 };
442 }
418 };
443 }
419 };
444 }
420 };
445 };
421
446
422
447
@@ -426,7 +451,7 b' var IPython = (function (IPython) {'
426 data.cell_type = 'code';
451 data.cell_type = 'code';
427 if (this.input_prompt_number) {
452 if (this.input_prompt_number) {
428 data.prompt_number = this.input_prompt_number;
453 data.prompt_number = this.input_prompt_number;
429 };
454 }
430 var outputs = this.output_area.toJSON();
455 var outputs = this.output_area.toJSON();
431 data.outputs = outputs;
456 data.outputs = outputs;
432 data.language = 'python';
457 data.language = 'python';
@@ -438,4 +463,4 b' var IPython = (function (IPython) {'
438 IPython.CodeCell = CodeCell;
463 IPython.CodeCell = CodeCell;
439
464
440 return IPython;
465 return IPython;
441 }(IPython)); No newline at end of file
466 }(IPython));
@@ -150,16 +150,14 b' var IPython = (function (IPython) {'
150 matched_text: ""
150 matched_text: ""
151 })
151 })
152 } else {
152 } else {
153 var callbacks = {
153 this.cell.kernel.complete(line, cur.ch, $.proxy(this.finish_completing, this));
154 'complete_reply': $.proxy(this.finish_completing, this)
155 };
156 this.cell.kernel.complete(line, cur.ch, callbacks);
157 }
154 }
158 };
155 };
159
156
160 Completer.prototype.finish_completing = function (content) {
157 Completer.prototype.finish_completing = function (msg) {
161 // let's build a function that wrap all that stuff into what is needed
158 // let's build a function that wrap all that stuff into what is needed
162 // for the new completer:
159 // for the new completer:
160 var content = msg.content;
163 var matched_text = content.matched_text;
161 var matched_text = content.matched_text;
164 var matches = content.matches;
162 var matches = content.matches;
165
163
@@ -96,7 +96,7 b' function (marked) {'
96 // only do this once
96 // only do this once
97 $([IPython.events]).off('notebook_loaded.Notebook', first_load);
97 $([IPython.events]).off('notebook_loaded.Notebook', first_load);
98 };
98 };
99
99
100 $([IPython.events]).on('notebook_loaded.Notebook', first_load);
100 $([IPython.events]).on('notebook_loaded.Notebook', first_load);
101 $([IPython.events]).trigger('app_initialized.NotebookApp');
101 $([IPython.events]).trigger('app_initialized.NotebookApp');
102 IPython.notebook.load_notebook(notebookName, notebookPath);
102 IPython.notebook.load_notebook(notebookName, notebookPath);
@@ -231,9 +231,10 b' var IPython = (function (IPython) {'
231 };
231 };
232
232
233
233
234 OutputArea.prototype.handle_output = function (msg_type, content) {
234 OutputArea.prototype.handle_output = function (msg) {
235 var json = {};
235 var json = {};
236 json.output_type = msg_type;
236 var msg_type = json.output_type = msg.header.msg_type;
237 var content = msg.content;
237 if (msg_type === "stream") {
238 if (msg_type === "stream") {
238 json.text = content.data;
239 json.text = content.data;
239 json.stream = content.name;
240 json.stream = content.name;
@@ -564,9 +565,10 b' var IPython = (function (IPython) {'
564 element.append(toinsert);
565 element.append(toinsert);
565 };
566 };
566
567
567 OutputArea.prototype.append_raw_input = function (content) {
568 OutputArea.prototype.append_raw_input = function (msg) {
568 var that = this;
569 var that = this;
569 this.expand();
570 this.expand();
571 var content = msg.content;
570 var area = this.create_output_area();
572 var area = this.create_output_area();
571
573
572 // disable any other raw_inputs, if they are left around
574 // disable any other raw_inputs, if they are left around
@@ -618,8 +620,8 b' var IPython = (function (IPython) {'
618 }
620 }
619
621
620
622
621 OutputArea.prototype.handle_clear_output = function (content) {
623 OutputArea.prototype.handle_clear_output = function (msg) {
622 this.clear_output(content.wait);
624 this.clear_output(msg.content.wait);
623 };
625 };
624
626
625
627
@@ -218,16 +218,15 b' var IPython = (function (IPython) {'
218 // remove everything after last open bracket
218 // remove everything after last open bracket
219 line = line.replace(endBracket, "");
219 line = line.replace(endBracket, "");
220 return Tooltip.last_token_re.exec(line)
220 return Tooltip.last_token_re.exec(line)
221 }
221 };
222
223
222
224 Tooltip.prototype._request_tooltip = function (cell, line) {
223 Tooltip.prototype._request_tooltip = function (cell, line) {
225 var callbacks = {
224 var callbacks = { shell : {
226 'object_info_reply': $.proxy(this._show, this)
225 reply : $.proxy(this._show, this)
227 }
226 }};
228 var oir_token = this.extract_oir_token(line);
227 var oir_token = this.extract_oir_token(line);
229 var msg_id = cell.kernel.object_info_request(oir_token, callbacks);
228 var msg_id = cell.kernel.object_info_request(oir_token, callbacks);
230 }
229 };
231
230
232 // make an imediate completion request
231 // make an imediate completion request
233 Tooltip.prototype.request = function (cell, hide_if_no_docstring) {
232 Tooltip.prototype.request = function (cell, hide_if_no_docstring) {
@@ -301,7 +300,8 b' var IPython = (function (IPython) {'
301 Tooltip.prototype._show = function (reply) {
300 Tooltip.prototype._show = function (reply) {
302 // move the bubble if it is not hidden
301 // move the bubble if it is not hidden
303 // otherwise fade it
302 // otherwise fade it
304 this.name = reply.name;
303 var content = reply.content;
304 this.name = content.name;
305
305
306 // do some math to have the tooltip arrow on more or less on left or right
306 // do some math to have the tooltip arrow on more or less on left or right
307 // width of the editor
307 // width of the editor
@@ -334,20 +334,20 b' var IPython = (function (IPython) {'
334 });
334 });
335
335
336 // build docstring
336 // build docstring
337 var defstring = reply.call_def;
337 var defstring = content.call_def;
338 if (defstring == null) {
338 if (defstring == null) {
339 defstring = reply.init_definition;
339 defstring = content.init_definition;
340 }
340 }
341 if (defstring == null) {
341 if (defstring == null) {
342 defstring = reply.definition;
342 defstring = content.definition;
343 }
343 }
344
344
345 var docstring = reply.call_docstring;
345 var docstring = content.call_docstring;
346 if (docstring == null) {
346 if (docstring == null) {
347 docstring = reply.init_docstring;
347 docstring = content.init_docstring;
348 }
348 }
349 if (docstring == null) {
349 if (docstring == null) {
350 docstring = reply.docstring;
350 docstring = content.docstring;
351 }
351 }
352
352
353 if (docstring == null) {
353 if (docstring == null) {
@@ -16,7 +16,8 b''
16 */
16 */
17
17
18 var IPython = (function (IPython) {
18 var IPython = (function (IPython) {
19
19 "use strict";
20
20 var utils = IPython.utils;
21 var utils = IPython.utils;
21
22
22 // Initialization and connection.
23 // Initialization and connection.
@@ -41,12 +42,15 b' var IPython = (function (IPython) {'
41 this.WebSocket = MozWebSocket;
42 this.WebSocket = MozWebSocket;
42 } else {
43 } else {
43 alert('Your browser does not have WebSocket support, please try Chrome, Safari or Firefox β‰₯ 6. Firefox 4 and 5 are also supported by you have to enable WebSockets in about:config.');
44 alert('Your browser does not have WebSocket support, please try Chrome, Safari or Firefox β‰₯ 6. Firefox 4 and 5 are also supported by you have to enable WebSockets in about:config.');
44 };
45 }
46
45 this.bind_events();
47 this.bind_events();
48 this.init_iopub_handlers();
49 this.comm_manager = new IPython.CommManager(this);
46 };
50 };
47
51
48
52
49 Kernel.prototype._get_msg = function (msg_type, content) {
53 Kernel.prototype._get_msg = function (msg_type, content, metadata) {
50 var msg = {
54 var msg = {
51 header : {
55 header : {
52 msg_id : utils.uuid(),
56 msg_id : utils.uuid(),
@@ -54,19 +58,32 b' var IPython = (function (IPython) {'
54 session : this.session_id,
58 session : this.session_id,
55 msg_type : msg_type
59 msg_type : msg_type
56 },
60 },
57 metadata : {},
61 metadata : metadata || {},
58 content : content,
62 content : content,
59 parent_header : {}
63 parent_header : {}
60 };
64 };
61 return msg;
65 return msg;
62 };
66 };
63
67
64 Kernel.prototype.bind_events = function() {
68 Kernel.prototype.bind_events = function () {
65 var that = this;
69 var that = this;
66 $([IPython.events]).on('send_input_reply.Kernel', function(evt, data) {
70 $([IPython.events]).on('send_input_reply.Kernel', function(evt, data) {
67 that.send_input_reply(data);
71 that.send_input_reply(data);
68 });
72 });
69 }
73 };
74
75 // Initialize the iopub handlers
76
77 Kernel.prototype.init_iopub_handlers = function () {
78 var output_types = ['stream', 'display_data', 'pyout', 'pyerr'];
79 this._iopub_handlers = {};
80 this.register_iopub_handler('status', $.proxy(this._handle_status_message, this));
81 this.register_iopub_handler('clear_output', $.proxy(this._handle_clear_output, this));
82
83 for (var i=0; i < output_types.length; i++) {
84 this.register_iopub_handler(output_types[i], $.proxy(this._handle_output_message, this));
85 }
86 };
70
87
71 /**
88 /**
72 * Start the Python kernel
89 * Start the Python kernel
@@ -81,7 +98,7 b' var IPython = (function (IPython) {'
81 $.proxy(this._kernel_started, this),
98 $.proxy(this._kernel_started, this),
82 'json'
99 'json'
83 );
100 );
84 };
101 }
85 };
102 };
86
103
87 /**
104 /**
@@ -101,7 +118,7 b' var IPython = (function (IPython) {'
101 $.proxy(this._kernel_started, this),
118 $.proxy(this._kernel_started, this),
102 'json'
119 'json'
103 );
120 );
104 };
121 }
105 };
122 };
106
123
107
124
@@ -110,11 +127,11 b' var IPython = (function (IPython) {'
110 this.running = true;
127 this.running = true;
111 this.kernel_id = json.id;
128 this.kernel_id = json.id;
112 var ws_url = json.ws_url;
129 var ws_url = json.ws_url;
113 if (ws_url.match(/wss?:\/\//) == null) {
130 if (ws_url.match(/wss?:\/\//) === null) {
114 // trailing 's' in https will become wss for secure web sockets
131 // trailing 's' in https will become wss for secure web sockets
115 prot = location.protocol.replace('http', 'ws') + "//";
132 var prot = location.protocol.replace('http', 'ws') + "//";
116 ws_url = prot + location.host + ws_url;
133 ws_url = prot + location.host + ws_url;
117 };
134 }
118 this.ws_url = ws_url;
135 this.ws_url = ws_url;
119 this.kernel_url = utils.url_path_join(this.base_url, this.kernel_id);
136 this.kernel_url = utils.url_path_join(this.base_url, this.kernel_id);
120 this.start_channels();
137 this.start_channels();
@@ -176,7 +193,7 b' var IPython = (function (IPython) {'
176 }
193 }
177 }, 1000);
194 }, 1000);
178 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply, this);
195 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply, this);
179 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply, this);
196 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_message, this);
180 this.stdin_channel.onmessage = $.proxy(this._handle_input_request, this);
197 this.stdin_channel.onmessage = $.proxy(this._handle_input_request, this);
181 };
198 };
182
199
@@ -208,64 +225,62 b' var IPython = (function (IPython) {'
208 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
225 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
209 for (var i=0; i < channels.length; i++) {
226 for (var i=0; i < channels.length; i++) {
210 if ( channels[i] !== null ) {
227 if ( channels[i] !== null ) {
211 channels[i].onclose = function (evt) {};
228 channels[i].onclose = null;
212 channels[i].close();
229 channels[i].close();
213 }
230 }
214 };
231 }
215 this.shell_channel = this.iopub_channel = this.stdin_channel = null;
232 this.shell_channel = this.iopub_channel = this.stdin_channel = null;
216 };
233 };
217
234
218 // Main public methods.
235 // Main public methods.
236
237 // send a message on the Kernel's shell channel
238 Kernel.prototype.send_shell_message = function (msg_type, content, callbacks, metadata) {
239 var msg = this._get_msg(msg_type, content, metadata);
240 this.shell_channel.send(JSON.stringify(msg));
241 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
242 return msg.header.msg_id;
243 };
219
244
220 /**
245 /**
221 * Get info on object asynchronoulsy
246 * Get info on an object
222 *
247 *
223 * @async
224 * @param objname {string}
248 * @param objname {string}
225 * @param callback {dict}
249 * @param callback {function}
226 * @method object_info_request
250 * @method object_info
227 *
228 * @example
229 *
251 *
230 * When calling this method pass a callbacks structure of the form:
252 * When calling this method, pass a callback function that expects one argument.
231 *
253 * The callback will be passed the complete `object_info_reply` message documented
232 * callbacks = {
254 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
233 * 'object_info_reply': object_info_reply_callback
234 * }
235 *
236 * The `object_info_reply_callback` will be passed the content object of the
237 *
238 * `object_into_reply` message documented in
239 * [IPython dev documentation](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
240 */
255 */
241 Kernel.prototype.object_info_request = function (objname, callbacks) {
256 Kernel.prototype.object_info = function (objname, callback) {
242 if(typeof(objname)!=null && objname!=null)
257 var callbacks;
243 {
258 if (callback) {
259 callbacks = { shell : { reply : callback } };
260 }
261
262 if (typeof(objname) !== null && objname !== null) {
244 var content = {
263 var content = {
245 oname : objname.toString(),
264 oname : objname.toString(),
246 detail_level : 0,
265 detail_level : 0,
247 };
266 };
248 var msg = this._get_msg("object_info_request", content);
267 return this.send_shell_message("object_info_request", content, callbacks);
249 this.shell_channel.send(JSON.stringify(msg));
250 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
251 return msg.header.msg_id;
252 }
268 }
253 return;
269 return;
254 }
270 };
255
271
256 /**
272 /**
257 * Execute given code into kernel, and pass result to callback.
273 * Execute given code into kernel, and pass result to callback.
258 *
274 *
259 * TODO: document input_request in callbacks
260 *
261 * @async
275 * @async
262 * @method execute
276 * @method execute
263 * @param {string} code
277 * @param {string} code
264 * @param [callbacks] {Object} With the optional following keys
278 * @param [callbacks] {Object} With the following keys (all optional)
265 * @param callbacks.'execute_reply' {function}
279 * @param callbacks.shell.reply {function}
266 * @param callbacks.'output' {function}
280 * @param callbacks.shell.payload.[payload_name] {function}
267 * @param callbacks.'clear_output' {function}
281 * @param callbacks.iopub.output {function}
268 * @param callbacks.'set_next_input' {function}
282 * @param callbacks.iopub.clear_output {function}
283 * @param callbacks.input {function}
269 * @param {object} [options]
284 * @param {object} [options]
270 * @param [options.silent=false] {Boolean}
285 * @param [options.silent=false] {Boolean}
271 * @param [options.user_expressions=empty_dict] {Dict}
286 * @param [options.user_expressions=empty_dict] {Dict}
@@ -287,27 +302,21 b' var IPython = (function (IPython) {'
287 * When calling this method pass a callbacks structure of the form:
302 * When calling this method pass a callbacks structure of the form:
288 *
303 *
289 * callbacks = {
304 * callbacks = {
290 * 'execute_reply': execute_reply_callback,
305 * shell : {
291 * 'output': output_callback,
306 * reply : execute_reply_callback,
292 * 'clear_output': clear_output_callback,
307 * payload : {
293 * 'set_next_input': set_next_input_callback
308 * set_next_input : set_next_input_callback,
309 * }
310 * },
311 * iopub : {
312 * output : output_callback,
313 * clear_output : clear_output_callback,
314 * },
315 * input : raw_input_callback
294 * }
316 * }
295 *
317 *
296 * The `execute_reply_callback` will be passed the content and metadata
318 * Each callback will be passed the entire message as a single arugment.
297 * objects of the `execute_reply` message documented
319 * Payload handlers will be passed the corresponding payload and the execute_reply message.
298 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#execute)
299 *
300 * The `output_callback` will be passed `msg_type` ('stream','display_data','pyout','pyerr')
301 * of the output and the content and metadata objects of the PUB/SUB channel that contains the
302 * output:
303 *
304 * http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket
305 *
306 * The `clear_output_callback` will be passed a content object that contains
307 * stdout, stderr and other fields that are booleans, as well as the metadata object.
308 *
309 * The `set_next_input_callback` will be passed the text that should become the next
310 * input cell.
311 */
320 */
312 Kernel.prototype.execute = function (code, callbacks, options) {
321 Kernel.prototype.execute = function (code, callbacks, options) {
313
322
@@ -320,47 +329,39 b' var IPython = (function (IPython) {'
320 allow_stdin : false
329 allow_stdin : false
321 };
330 };
322 callbacks = callbacks || {};
331 callbacks = callbacks || {};
323 if (callbacks.input_request !== undefined) {
332 if (callbacks.input !== undefined) {
324 content.allow_stdin = true;
333 content.allow_stdin = true;
325 }
334 }
326 $.extend(true, content, options)
335 $.extend(true, content, options);
327 $([IPython.events]).trigger('execution_request.Kernel', {kernel: this, content:content});
336 $([IPython.events]).trigger('execution_request.Kernel', {kernel: this, content:content});
328 var msg = this._get_msg("execute_request", content);
337 return this.send_shell_message("execute_request", content, callbacks);
329 this.shell_channel.send(JSON.stringify(msg));
330 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
331 return msg.header.msg_id;
332 };
338 };
333
339
334 /**
340 /**
335 * When calling this method pass a callbacks structure of the form:
341 * When calling this method, pass a function to be called with the `complete_reply` message
342 * as its only argument when it arrives.
336 *
343 *
337 * callbacks = {
344 * `complete_reply` is documented
338 * 'complete_reply': complete_reply_callback
339 * }
340 *
341 * The `complete_reply_callback` will be passed the content object of the
342 * `complete_reply` message documented
343 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
345 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
344 *
346 *
345 * @method complete
347 * @method complete
346 * @param line {integer}
348 * @param line {integer}
347 * @param cursor_pos {integer}
349 * @param cursor_pos {integer}
348 * @param {dict} callbacks
350 * @param callback {function}
349 * @param callbacks.complete_reply {function} `complete_reply_callback`
350 *
351 *
351 */
352 */
352 Kernel.prototype.complete = function (line, cursor_pos, callbacks) {
353 Kernel.prototype.complete = function (line, cursor_pos, callback) {
353 callbacks = callbacks || {};
354 var callbacks;
355 if (callback) {
356 callbacks = { shell : { reply : callback } };
357 }
354 var content = {
358 var content = {
355 text : '',
359 text : '',
356 line : line,
360 line : line,
357 block : null,
361 block : null,
358 cursor_pos : cursor_pos
362 cursor_pos : cursor_pos
359 };
363 };
360 var msg = this._get_msg("complete_request", content);
364 return this.send_shell_message("complete_request", content, callbacks);
361 this.shell_channel.send(JSON.stringify(msg));
362 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
363 return msg.header.msg_id;
364 };
365 };
365
366
366
367
@@ -368,7 +369,7 b' var IPython = (function (IPython) {'
368 if (this.running) {
369 if (this.running) {
369 $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this});
370 $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this});
370 $.post(this.kernel_url + "/interrupt");
371 $.post(this.kernel_url + "/interrupt");
371 };
372 }
372 };
373 };
373
374
374
375
@@ -380,7 +381,7 b' var IPython = (function (IPython) {'
380 type : "DELETE"
381 type : "DELETE"
381 };
382 };
382 $.ajax(this.kernel_url, settings);
383 $.ajax(this.kernel_url, settings);
383 };
384 }
384 };
385 };
385
386
386 Kernel.prototype.send_input_reply = function (input) {
387 Kernel.prototype.send_input_reply = function (input) {
@@ -396,9 +397,19 b' var IPython = (function (IPython) {'
396
397
397 // Reply handlers
398 // Reply handlers
398
399
400 Kernel.prototype.register_iopub_handler = function (msg_type, callback) {
401 this._iopub_handlers[msg_type] = callback;
402 };
403
404 Kernel.prototype.get_iopub_handler = function (msg_type) {
405 // get iopub handler for a specific message type
406 return this._iopub_handlers[msg_type];
407 };
408
409
399 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
410 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
400 var callbacks = this._msg_callbacks[msg_id];
411 // get callbacks for a specific message
401 return callbacks;
412 return this._msg_callbacks[msg_id];
402 };
413 };
403
414
404
415
@@ -407,91 +418,142 b' var IPython = (function (IPython) {'
407 delete this._msg_callbacks[msg_id];
418 delete this._msg_callbacks[msg_id];
408 }
419 }
409 };
420 };
410
421
411
422 /* Set callbacks for a particular message.
423 * Callbacks should be a struct of the following form:
424 * shell : {
425 *
426 * }
427
428 */
412 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
429 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
413 this._msg_callbacks[msg_id] = callbacks || {};
430 if (callbacks) {
431 // shallow-copy mapping, because we will modify it at the top level
432 var cbcopy = this._msg_callbacks[msg_id] = {};
433 cbcopy.shell = callbacks.shell;
434 cbcopy.iopub = callbacks.iopub;
435 cbcopy.input = callbacks.input;
436 this._msg_callbacks[msg_id] = cbcopy;
437 }
414 };
438 };
415
439
416
440
417 Kernel.prototype._handle_shell_reply = function (e) {
441 Kernel.prototype._handle_shell_reply = function (e) {
418 var reply = $.parseJSON(e.data);
442 var reply = $.parseJSON(e.data);
419 $([IPython.events]).trigger('shell_reply.Kernel', {kernel: this, reply:reply});
443 $([IPython.events]).trigger('shell_reply.Kernel', {kernel: this, reply:reply});
420 var header = reply.header;
421 var content = reply.content;
444 var content = reply.content;
422 var metadata = reply.metadata;
445 var metadata = reply.metadata;
423 var msg_type = header.msg_type;
446 var parent_id = reply.parent_header.msg_id;
424 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
447 var callbacks = this.get_callbacks_for_msg(parent_id);
425 if (callbacks !== undefined) {
448 if (!callbacks || !callbacks.shell) {
426 var cb = callbacks[msg_type];
449 return;
427 if (cb !== undefined) {
450 }
428 cb(content, metadata);
451 var shell_callbacks = callbacks.shell;
429 }
452
430 };
453 // clear callbacks on shell
431
454 delete callbacks.shell;
432 if (content.payload !== undefined) {
455 delete callbacks.input;
433 var payload = content.payload || [];
456 if (!callbacks.iopub) {
434 this._handle_payload(callbacks, payload);
457 this.clear_callbacks_for_msg(parent_id);
458 }
459
460 if (shell_callbacks.reply !== undefined) {
461 shell_callbacks.reply(reply);
462 }
463 if (content.payload && shell_callbacks.payload) {
464 this._handle_payloads(content.payload, shell_callbacks.payload, reply);
435 }
465 }
436 };
466 };
437
467
438
468
439 Kernel.prototype._handle_payload = function (callbacks, payload) {
469 Kernel.prototype._handle_payloads = function (payloads, payload_callbacks, msg) {
440 var l = payload.length;
470 var l = payloads.length;
441 // Payloads are handled by triggering events because we don't want the Kernel
471 // Payloads are handled by triggering events because we don't want the Kernel
442 // to depend on the Notebook or Pager classes.
472 // to depend on the Notebook or Pager classes.
443 for (var i=0; i<l; i++) {
473 for (var i=0; i<l; i++) {
444 if (payload[i].source === 'page') {
474 var payload = payloads[i];
445 var data = {'text':payload[i].text}
475 var callback = payload_callbacks[payload.source];
446 $([IPython.events]).trigger('open_with_text.Pager', data);
476 if (callback) {
447 } else if (payload[i].source === 'set_next_input') {
477 callback(payload, msg);
448 if (callbacks.set_next_input !== undefined) {
478 }
449 callbacks.set_next_input(payload[i].text)
479 }
480 };
481
482 Kernel.prototype._handle_status_message = function (msg) {
483 var execution_state = msg.content.execution_state;
484 var parent_id = msg.parent_header.msg_id;
485
486 // dispatch status msg callbacks, if any
487 var callbacks = this.get_callbacks_for_msg(parent_id);
488 if (callbacks && callbacks.iopub && callbacks.iopub.status) {
489 try {
490 callbacks.iopub.status(msg);
491 } catch (e) {
492 console.log("Exception in status msg handler", e);
493 }
494 }
495
496 if (execution_state === 'busy') {
497 $([IPython.events]).trigger('status_busy.Kernel', {kernel: this});
498 } else if (execution_state === 'idle') {
499 // clear callbacks on idle, there can be no more
500 if (callbacks !== undefined) {
501 delete callbacks.iopub;
502 delete callbacks.input;
503 if (!callbacks.shell) {
504 this.clear_callbacks_for_msg(parent_id);
450 }
505 }
451 }
506 }
452 };
507 // trigger status_idle event
508 $([IPython.events]).trigger('status_idle.Kernel', {kernel: this});
509 } else if (execution_state === 'restarting') {
510 // autorestarting is distinct from restarting,
511 // in that it means the kernel died and the server is restarting it.
512 // status_restarting sets the notification widget,
513 // autorestart shows the more prominent dialog.
514 $([IPython.events]).trigger('status_autorestarting.Kernel', {kernel: this});
515 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
516 } else if (execution_state === 'dead') {
517 this.stop_channels();
518 $([IPython.events]).trigger('status_dead.Kernel', {kernel: this});
519 }
520 };
521
522
523 // handle clear_output message
524 Kernel.prototype._handle_clear_output = function (msg) {
525 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
526 if (!callbacks || !callbacks.iopub) {
527 return;
528 }
529 var callback = callbacks.iopub.clear_output;
530 if (callback) {
531 callback(msg);
532 }
453 };
533 };
454
534
455
535
456 Kernel.prototype._handle_iopub_reply = function (e) {
536 // handle an output message (pyout, display_data, etc.)
457 var reply = $.parseJSON(e.data);
537 Kernel.prototype._handle_output_message = function (msg) {
458 var content = reply.content;
538 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
459 var msg_type = reply.header.msg_type;
539 if (!callbacks || !callbacks.iopub) {
460 var metadata = reply.metadata;
461 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
462 if (msg_type !== 'status' && callbacks === undefined) {
463 // Message not from one of this notebook's cells and there are no
464 // callbacks to handle it.
465 return;
540 return;
466 }
541 }
467 var output_types = ['stream','display_data','pyout','pyerr'];
542 var callback = callbacks.iopub.output;
468 if (output_types.indexOf(msg_type) >= 0) {
543 if (callback) {
469 var cb = callbacks['output'];
544 callback(msg);
470 if (cb !== undefined) {
545 }
471 cb(msg_type, content, metadata);
546 };
472 }
547
473 } else if (msg_type === 'status') {
548 // dispatch IOPub messages to respective handlers.
474 if (content.execution_state === 'busy') {
549 // each message type should have a handler.
475 $([IPython.events]).trigger('status_busy.Kernel', {kernel: this});
550 Kernel.prototype._handle_iopub_message = function (e) {
476 } else if (content.execution_state === 'idle') {
551 var msg = $.parseJSON(e.data);
477 $([IPython.events]).trigger('status_idle.Kernel', {kernel: this});
552
478 } else if (content.execution_state === 'restarting') {
553 var handler = this.get_iopub_handler(msg.header.msg_type);
479 // autorestarting is distinct from restarting,
554 if (handler !== undefined) {
480 // in that it means the kernel died and the server is restarting it.
555 handler(msg);
481 // status_restarting sets the notification widget,
556 }
482 // autorestart shows the more prominent dialog.
483 $([IPython.events]).trigger('status_autorestarting.Kernel', {kernel: this});
484 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
485 } else if (content.execution_state === 'dead') {
486 this.stop_channels();
487 $([IPython.events]).trigger('status_dead.Kernel', {kernel: this});
488 };
489 } else if (msg_type === 'clear_output') {
490 var cb = callbacks['clear_output'];
491 if (cb !== undefined) {
492 cb(content, metadata);
493 }
494 };
495 };
557 };
496
558
497
559
@@ -506,12 +568,11 b' var IPython = (function (IPython) {'
506 return;
568 return;
507 }
569 }
508 var callbacks = this.get_callbacks_for_msg(request.parent_header.msg_id);
570 var callbacks = this.get_callbacks_for_msg(request.parent_header.msg_id);
509 if (callbacks !== undefined) {
571 if (callbacks) {
510 var cb = callbacks[msg_type];
572 if (callbacks.input) {
511 if (cb !== undefined) {
573 callbacks.input(request);
512 cb(content, metadata);
513 }
574 }
514 };
575 }
515 };
576 };
516
577
517
578
@@ -232,6 +232,9 b' class="notebook_app"'
232 <script src="{{ static_url("base/js/events.js") }}" type="text/javascript" charset="utf-8"></script>
232 <script src="{{ static_url("base/js/events.js") }}" type="text/javascript" charset="utf-8"></script>
233 <script src="{{ static_url("base/js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
233 <script src="{{ static_url("base/js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
234 <script src="{{ static_url("base/js/dialog.js") }}" type="text/javascript" charset="utf-8"></script>
234 <script src="{{ static_url("base/js/dialog.js") }}" type="text/javascript" charset="utf-8"></script>
235 <script src="{{ static_url("services/kernels/js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
236 <script src="{{ static_url("services/kernels/js/comm.js") }}" type="text/javascript" charset="utf-8"></script>
237 <script src="{{ static_url("services/sessions/js/session.js") }}" type="text/javascript" charset="utf-8"></script>
235 <script src="{{ static_url("notebook/js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script>
238 <script src="{{ static_url("notebook/js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script>
236 <script src="{{ static_url("notebook/js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
239 <script src="{{ static_url("notebook/js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
237 <script src="{{ static_url("notebook/js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
240 <script src="{{ static_url("notebook/js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
@@ -240,8 +243,6 b' class="notebook_app"'
240 <script src="{{ static_url("notebook/js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
243 <script src="{{ static_url("notebook/js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
241 <script src="{{ static_url("notebook/js/completer.js") }}" type="text/javascript" charset="utf-8"></script>
244 <script src="{{ static_url("notebook/js/completer.js") }}" type="text/javascript" charset="utf-8"></script>
242 <script src="{{ static_url("notebook/js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
245 <script src="{{ static_url("notebook/js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
243 <script src="{{ static_url("services/kernels/js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
244 <script src="{{ static_url("services/sessions/js/session.js") }}" type="text/javascript" charset="utf-8"></script>
245 <script src="{{ static_url("notebook/js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
246 <script src="{{ static_url("notebook/js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
246 <script src="{{ static_url("notebook/js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
247 <script src="{{ static_url("notebook/js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
247 <script src="{{ static_url("notebook/js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
248 <script src="{{ static_url("notebook/js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
@@ -1,14 +1,5 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """A simple interactive kernel that talks to a frontend over 0MQ.
2 """An interactive kernel that talks to frontends over 0MQ."""
3
4 Things to do:
5
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 call set_parent on all the PUB objects with the message about to be executed.
8 * Implement random port and security key logic.
9 * Implement control messages.
10 * Implement event loop and poll version.
11 """
12
3
13 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
14 # Imports
5 # Imports
@@ -145,6 +136,7 b' class Kernel(Configurable):'
145 profile_dir = self.profile_dir,
136 profile_dir = self.profile_dir,
146 user_module = self.user_module,
137 user_module = self.user_module,
147 user_ns = self.user_ns,
138 user_ns = self.user_ns,
139 kernel = self,
148 )
140 )
149 self.shell.displayhook.session = self.session
141 self.shell.displayhook.session = self.session
150 self.shell.displayhook.pub_socket = self.iopub_socket
142 self.shell.displayhook.pub_socket = self.iopub_socket
@@ -168,11 +160,17 b' class Kernel(Configurable):'
168 for msg_type in msg_types:
160 for msg_type in msg_types:
169 self.shell_handlers[msg_type] = getattr(self, msg_type)
161 self.shell_handlers[msg_type] = getattr(self, msg_type)
170
162
163 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
164 comm_manager = self.shell.comm_manager
165 for msg_type in comm_msg_types:
166 self.shell_handlers[msg_type] = getattr(comm_manager, msg_type)
167
171 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
168 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
172 self.control_handlers = {}
169 self.control_handlers = {}
173 for msg_type in control_msg_types:
170 for msg_type in control_msg_types:
174 self.control_handlers[msg_type] = getattr(self, msg_type)
171 self.control_handlers[msg_type] = getattr(self, msg_type)
175
172
173
176 def dispatch_control(self, msg):
174 def dispatch_control(self, msg):
177 """dispatch control requests"""
175 """dispatch control requests"""
178 idents,msg = self.session.feed_identities(msg, copy=False)
176 idents,msg = self.session.feed_identities(msg, copy=False)
@@ -367,17 +365,7 b' class Kernel(Configurable):'
367 __builtin__.input = input
365 __builtin__.input = input
368
366
369 # Set the parent message of the display hook and out streams.
367 # Set the parent message of the display hook and out streams.
370 shell.displayhook.set_parent(parent)
368 shell.set_parent(parent)
371 shell.display_pub.set_parent(parent)
372 shell.data_pub.set_parent(parent)
373 try:
374 sys.stdout.set_parent(parent)
375 except AttributeError:
376 pass
377 try:
378 sys.stderr.set_parent(parent)
379 except AttributeError:
380 pass
381
369
382 # Re-broadcast our input for the benefit of listening clients, and
370 # Re-broadcast our input for the benefit of listening clients, and
383 # start computing output
371 # start computing output
@@ -583,17 +571,7 b' class Kernel(Configurable):'
583
571
584 # Set the parent message of the display hook and out streams.
572 # Set the parent message of the display hook and out streams.
585 shell = self.shell
573 shell = self.shell
586 shell.displayhook.set_parent(parent)
574 shell.set_parent(parent)
587 shell.display_pub.set_parent(parent)
588 shell.data_pub.set_parent(parent)
589 try:
590 sys.stdout.set_parent(parent)
591 except AttributeError:
592 pass
593 try:
594 sys.stderr.set_parent(parent)
595 except AttributeError:
596 pass
597
575
598 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
576 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
599 # self.iopub_socket.send(pyin_msg)
577 # self.iopub_socket.send(pyin_msg)
@@ -44,11 +44,12 b' from IPython.utils import openpy'
44 from IPython.utils.jsonutil import json_clean, encode_images
44 from IPython.utils.jsonutil import json_clean, encode_images
45 from IPython.utils.process import arg_split
45 from IPython.utils.process import arg_split
46 from IPython.utils import py3compat
46 from IPython.utils import py3compat
47 from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes
47 from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes, Any
48 from IPython.utils.warn import error
48 from IPython.utils.warn import error
49 from IPython.kernel.zmq.displayhook import ZMQShellDisplayHook
49 from IPython.kernel.zmq.displayhook import ZMQShellDisplayHook
50 from IPython.kernel.zmq.datapub import ZMQDataPublisher
50 from IPython.kernel.zmq.datapub import ZMQDataPublisher
51 from IPython.kernel.zmq.session import extract_header
51 from IPython.kernel.zmq.session import extract_header
52 from IPython.kernel.comm import CommManager
52 from session import Session
53 from session import Session
53
54
54 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
@@ -476,6 +477,8 b' class ZMQInteractiveShell(InteractiveShell):'
476 displayhook_class = Type(ZMQShellDisplayHook)
477 displayhook_class = Type(ZMQShellDisplayHook)
477 display_pub_class = Type(ZMQDisplayPublisher)
478 display_pub_class = Type(ZMQDisplayPublisher)
478 data_pub_class = Type(ZMQDataPublisher)
479 data_pub_class = Type(ZMQDataPublisher)
480 kernel = Any()
481 parent_header = Any()
479
482
480 # Override the traitlet in the parent class, because there's no point using
483 # Override the traitlet in the parent class, because there's no point using
481 # readline for the kernel. Can be removed when the readline code is moved
484 # readline for the kernel. Can be removed when the readline code is moved
@@ -585,6 +588,24 b' class ZMQInteractiveShell(InteractiveShell):'
585 )
588 )
586 self.payload_manager.write_payload(payload)
589 self.payload_manager.write_payload(payload)
587
590
591 def set_parent(self, parent):
592 """Set the parent header for associating output with its triggering input"""
593 self.parent_header = parent
594 self.displayhook.set_parent(parent)
595 self.display_pub.set_parent(parent)
596 self.data_pub.set_parent(parent)
597 try:
598 sys.stdout.set_parent(parent)
599 except AttributeError:
600 pass
601 try:
602 sys.stderr.set_parent(parent)
603 except AttributeError:
604 pass
605
606 def get_parent(self):
607 return self.parent_header
608
588 #-------------------------------------------------------------------------
609 #-------------------------------------------------------------------------
589 # Things related to magics
610 # Things related to magics
590 #-------------------------------------------------------------------------
611 #-------------------------------------------------------------------------
@@ -593,7 +614,10 b' class ZMQInteractiveShell(InteractiveShell):'
593 super(ZMQInteractiveShell, self).init_magics()
614 super(ZMQInteractiveShell, self).init_magics()
594 self.register_magics(KernelMagics)
615 self.register_magics(KernelMagics)
595 self.magics_manager.register_alias('ed', 'edit')
616 self.magics_manager.register_alias('ed', 'edit')
596
617
618 def init_comms(self):
619 self.comm_manager = CommManager(shell=self, parent=self)
620 self.configurables.append(self.comm_manager)
597
621
598
622
599 InteractiveShellABC.register(ZMQInteractiveShell)
623 InteractiveShellABC.register(ZMQInteractiveShell)
@@ -430,7 +430,9 b" When status is 'ok', the following extra fields are present::"
430 # Each execution payload is a dict with string keys that may have been
430 # Each execution payload is a dict with string keys that may have been
431 # produced by the code being executed. It is retrieved by the kernel at
431 # produced by the code being executed. It is retrieved by the kernel at
432 # the end of the execution and sent back to the front end, which can take
432 # the end of the execution and sent back to the front end, which can take
433 # action on it as needed. See main text for further details.
433 # action on it as needed.
434 # The only requirement of each payload dict is that it have a 'source' key,
435 # which is a string classifying the payload (e.g. 'pager').
434 'payload' : list(dict),
436 'payload' : list(dict),
435
437
436 # Results for the user_variables and user_expressions.
438 # Results for the user_variables and user_expressions.
@@ -1058,6 +1060,79 b" where the first part is the zmq.IDENTITY of the heart's DEALER on the engine, an"
1058 the rest is the message sent by the monitor. No Python code ever has any
1060 the rest is the message sent by the monitor. No Python code ever has any
1059 access to the message between the monitor's send, and the monitor's recv.
1061 access to the message between the monitor's send, and the monitor's recv.
1060
1062
1063 Custom Messages
1064 ===============
1065
1066 IPython 2.0 adds a messaging system for developers to add their own objects with Frontend
1067 and Kernel-side components, and allow them to communicate with each other.
1068 To do this, IPython adds a notion of a ``Comm``, which exists on both sides,
1069 and can communicate in either direction.
1070
1071 These messages are fully symmetrical - both the Kernel and the Frontend can send each message,
1072 and no messages expect a reply.
1073 The Kernel listens for these messages on the Shell channel,
1074 and the Frontend listens for them on the IOPub channel.
1075
1076 .. versionadded:: 2.0
1077
1078 Opening a Comm
1079 --------------
1080
1081 Opening a Comm produces a ``comm_open`` message, to be sent to the other side::
1082
1083 {
1084 'comm_id' : 'u-u-i-d',
1085 'target_name' : 'my_comm',
1086 'data' : {}
1087 }
1088
1089 Every Comm has an ID and a target name.
1090 The code handling the message on the receiving side is responsible for maintaining a mapping
1091 of target_name keys to constructors.
1092 After a ``comm_open`` message has been sent,
1093 there should be a corresponding Comm instance on both sides.
1094 The ``data`` key is always a dict and can be any extra JSON information used in initialization of the comm.
1095
1096 If the ``target_name`` key is not found on the receiving side,
1097 then it should immediately reply with a ``comm_close`` message to avoid an inconsistent state.
1098
1099 Comm Messages
1100 -------------
1101
1102 Comm messages are one-way communications to update comm state,
1103 used for synchronizing widget state, or simply requesting actions of a comm's counterpart.
1104
1105 Essentially, each comm pair defines their own message specification implemented inside the ``data`` dict.
1106
1107 There are no expected replies (of course, one side can send another ``comm_msg`` in reply).
1108
1109 Message type: ``comm_msg``::
1110
1111 {
1112 'comm_id' : 'u-u-i-d',
1113 'data' : {}
1114 }
1115
1116 Tearing Down Comms
1117 ------------------
1118
1119 Since comms live on both sides, when a comm is destroyed the other side must be notified.
1120 This is done with a ``comm_close`` message.
1121
1122 Message type: ``comm_close``::
1123
1124 {
1125 'comm_id' : 'u-u-i-d',
1126 'data' : {}
1127 }
1128
1129 Output Side Effects
1130 -------------------
1131
1132 Since comm messages can execute arbitrary user code,
1133 handlers should set the parent header and publish status busy / idle,
1134 just like an execute request.
1135
1061
1136
1062 ToDo
1137 ToDo
1063 ====
1138 ====
@@ -1070,9 +1145,4 b' Missing things include:'
1070 likely that with the payload concept we can build a full solution, but not
1145 likely that with the payload concept we can build a full solution, but not
1071 100% clear yet.
1146 100% clear yet.
1072
1147
1073 * Finishing the details of the heartbeat protocol.
1074
1075 * Signal handling: specify what kind of information kernel should broadcast (or
1076 not) when it receives signals.
1077
1078 .. include:: ../links.txt
1148 .. include:: ../links.txt
General Comments 0
You need to be logged in to leave comments. Login now