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,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 |
|
|
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 ( |
|
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 ( |
|
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 ( |
|
302 | CodeCell.prototype._handle_input_request = function (msg) { | |
280 |
this.output_area.append_raw_input( |
|
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 || " "; |
|
353 | var ns = prompt_value || " "; | |
331 | return 'In [' + ns + ']:' |
|
354 | return 'In [' + 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++){ |
|
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( |
|
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 ( |
|
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 |
|
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 ( |
|
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 ( |
|
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 |
|
|
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 |
|
|
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 = |
|
337 | var defstring = content.call_def; | |
338 | if (defstring == null) { |
|
338 | if (defstring == null) { | |
339 |
defstring = |
|
339 | defstring = content.init_definition; | |
340 | } |
|
340 | } | |
341 | if (defstring == null) { |
|
341 | if (defstring == null) { | |
342 |
defstring = |
|
342 | defstring = content.definition; | |
343 | } |
|
343 | } | |
344 |
|
344 | |||
345 |
var docstring = |
|
345 | var docstring = content.call_docstring; | |
346 | if (docstring == null) { |
|
346 | if (docstring == null) { | |
347 |
docstring = |
|
347 | docstring = content.init_docstring; | |
348 | } |
|
348 | } | |
349 | if (docstring == null) { |
|
349 | if (docstring == null) { | |
350 |
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_ |
|
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 = |
|
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 |
|
246 | * Get info on an object | |
222 | * |
|
247 | * | |
223 | * @async |
|
|||
224 | * @param objname {string} |
|
248 | * @param objname {string} | |
225 |
* @param callback { |
|
249 | * @param callback {function} | |
226 |
* @method object_info |
|
250 | * @method object_info | |
227 | * |
|
|||
228 | * @example |
|
|||
229 | * |
|
251 | * | |
230 |
* When calling this method pass a callback |
|
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 |
|
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 |
|
|
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 |
|
278 | * @param [callbacks] {Object} With the following keys (all optional) | |
265 |
* @param callbacks. |
|
279 | * @param callbacks.shell.reply {function} | |
266 |
* @param callbacks. |
|
280 | * @param callbacks.shell.payload.[payload_name] {function} | |
267 |
* @param callbacks. |
|
281 | * @param callbacks.iopub.output {function} | |
268 |
* @param callbacks. |
|
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 |
* |
|
306 | * reply : execute_reply_callback, | |
292 | * 'clear_output': clear_output_callback, |
|
307 | * payload : { | |
293 |
* |
|
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 |
|
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 |
|
|
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 |
|
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 |
|
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, callback |
|
353 | Kernel.prototype.complete = function (line, cursor_pos, callback) { | |
353 |
|
|
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 |
|
|
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 |
|
446 | var parent_id = reply.parent_header.msg_id; | |
424 |
var callbacks = this.get_callbacks_for_msg( |
|
447 | var callbacks = this.get_callbacks_for_msg(parent_id); | |
425 |
if (callbacks |
|
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. |
|
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 ( |
|
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 |
|
|
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 |
|
571 | if (callbacks) { | |
510 |
|
|
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 |
|
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 |
|
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 |
|
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. |
|
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