##// END OF EJS Templates
Support specifying requirejs modules for comm targets
Thomas Kluyver -
Show More
@@ -1,190 +1,210 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 'base/js/utils',
7 'base/js/utils',
8 ], function(IPython, $, utils) {
8 ], function(IPython, $, utils) {
9 "use strict";
9 "use strict";
10
10
11 //-----------------------------------------------------------------------
11 //-----------------------------------------------------------------------
12 // CommManager class
12 // CommManager class
13 //-----------------------------------------------------------------------
13 //-----------------------------------------------------------------------
14
14
15 var CommManager = function (kernel) {
15 var CommManager = function (kernel) {
16 this.comms = {};
16 this.comms = {};
17 this.targets = {};
17 this.targets = {};
18 if (kernel !== undefined) {
18 if (kernel !== undefined) {
19 this.init_kernel(kernel);
19 this.init_kernel(kernel);
20 }
20 }
21 };
21 };
22
22
23 CommManager.prototype.init_kernel = function (kernel) {
23 CommManager.prototype.init_kernel = function (kernel) {
24 // connect the kernel, and register message handlers
24 // connect the kernel, and register message handlers
25 this.kernel = kernel;
25 this.kernel = kernel;
26 var msg_types = ['comm_open', 'comm_msg', 'comm_close'];
26 var msg_types = ['comm_open', 'comm_msg', 'comm_close'];
27 for (var i = 0; i < msg_types.length; i++) {
27 for (var i = 0; i < msg_types.length; i++) {
28 var msg_type = msg_types[i];
28 var msg_type = msg_types[i];
29 kernel.register_iopub_handler(msg_type, $.proxy(this[msg_type], this));
29 kernel.register_iopub_handler(msg_type, $.proxy(this[msg_type], this));
30 }
30 }
31 };
31 };
32
32
33 CommManager.prototype.new_comm = function (target_name, data, callbacks, metadata) {
33 CommManager.prototype.new_comm = function (target_name, data, callbacks, metadata) {
34 // Create a new Comm, register it, and open its Kernel-side counterpart
34 // Create a new Comm, register it, and open its Kernel-side counterpart
35 // Mimics the auto-registration in `Comm.__init__` in the IPython Comm
35 // Mimics the auto-registration in `Comm.__init__` in the IPython Comm
36 var comm = new Comm(target_name);
36 var comm = new Comm(target_name);
37 this.register_comm(comm);
37 this.register_comm(comm);
38 comm.open(data, callbacks, metadata);
38 comm.open(data, callbacks, metadata);
39 return comm;
39 return comm;
40 };
40 };
41
41
42 CommManager.prototype.register_target = function (target_name, f) {
42 CommManager.prototype.register_target = function (target_name, f) {
43 // Register a target function for a given target name
43 // Register a target function for a given target name
44 this.targets[target_name] = f;
44 this.targets[target_name] = f;
45 };
45 };
46
46
47 CommManager.prototype.unregister_target = function (target_name, f) {
47 CommManager.prototype.unregister_target = function (target_name, f) {
48 // Unregister a target function for a given target name
48 // Unregister a target function for a given target name
49 delete this.targets[target_name];
49 delete this.targets[target_name];
50 };
50 };
51
51
52 CommManager.prototype.register_comm = function (comm) {
52 CommManager.prototype.register_comm = function (comm) {
53 // Register a comm in the mapping
53 // Register a comm in the mapping
54 this.comms[comm.comm_id] = comm;
54 this.comms[comm.comm_id] = comm;
55 comm.kernel = this.kernel;
55 comm.kernel = this.kernel;
56 return comm.comm_id;
56 return comm.comm_id;
57 };
57 };
58
58
59 CommManager.prototype.unregister_comm = function (comm) {
59 CommManager.prototype.unregister_comm = function (comm) {
60 // Remove a comm from the mapping
60 // Remove a comm from the mapping
61 delete this.comms[comm.comm_id];
61 delete this.comms[comm.comm_id];
62 };
62 };
63
63
64 // comm message handlers
64 // comm message handlers
65
65
66 CommManager.prototype.comm_open = function (msg) {
66 CommManager.prototype.comm_open = function (msg) {
67 var content = msg.content;
67 var content = msg.content;
68 var f = this.targets[content.target_name];
68 var that = this;
69 if (f === undefined) {
69
70 console.log("No such target registered: ", content.target_name);
70 var instantiate_comm = function(target) {
71 console.log("Available targets are: ", this.targets);
71 var comm = new Comm(content.target_name, content.comm_id);
72 return;
72 that.register_comm(comm);
73 }
73 try {
74 var comm = new Comm(content.target_name, content.comm_id);
74 target(comm, msg);
75 this.register_comm(comm);
75 } catch (e) {
76 try {
76 console.log("Exception opening new comm:", e, e.stack, msg);
77 f(comm, msg);
77 comm.close();
78 } catch (e) {
78 that.unregister_comm(comm);
79 console.log("Exception opening new comm:", e, e.stack, msg);
79 }
80 comm.close();
80 };
81 this.unregister_comm(comm);
81
82 if (content.target_module) {
83 // Load requirejs module for comm target
84 require([content.target_module], function(mod) {
85 var target = mod[content.target_name];
86 if (target !== undefined) {
87 instantiate_comm(target)
88 } else {
89 console.log("Comm target " + content.target_name +
90 " not found in module " + content.target_module);
91 }
92 }, function(err) { console.log(err); });
93 } else {
94 // No requirejs module specified: look for target in registry
95 var f = this.targets[content.target_name];
96 if (f === undefined) {
97 console.log("No such target registered: ", content.target_name);
98 console.log("Available targets are: ", this.targets);
99 return;
100 }
101 instantiate_comm(f)
82 }
102 }
83 };
103 };
84
104
85 CommManager.prototype.comm_close = function (msg) {
105 CommManager.prototype.comm_close = function (msg) {
86 var content = msg.content;
106 var content = msg.content;
87 var comm = this.comms[content.comm_id];
107 var comm = this.comms[content.comm_id];
88 if (comm === undefined) {
108 if (comm === undefined) {
89 return;
109 return;
90 }
110 }
91 this.unregister_comm(comm);
111 this.unregister_comm(comm);
92 try {
112 try {
93 comm.handle_close(msg);
113 comm.handle_close(msg);
94 } catch (e) {
114 } catch (e) {
95 console.log("Exception closing comm: ", e, e.stack, msg);
115 console.log("Exception closing comm: ", e, e.stack, msg);
96 }
116 }
97 };
117 };
98
118
99 CommManager.prototype.comm_msg = function (msg) {
119 CommManager.prototype.comm_msg = function (msg) {
100 var content = msg.content;
120 var content = msg.content;
101 var comm = this.comms[content.comm_id];
121 var comm = this.comms[content.comm_id];
102 if (comm === undefined) {
122 if (comm === undefined) {
103 return;
123 return;
104 }
124 }
105 try {
125 try {
106 comm.handle_msg(msg);
126 comm.handle_msg(msg);
107 } catch (e) {
127 } catch (e) {
108 console.log("Exception handling comm msg: ", e, e.stack, msg);
128 console.log("Exception handling comm msg: ", e, e.stack, msg);
109 }
129 }
110 };
130 };
111
131
112 //-----------------------------------------------------------------------
132 //-----------------------------------------------------------------------
113 // Comm base class
133 // Comm base class
114 //-----------------------------------------------------------------------
134 //-----------------------------------------------------------------------
115
135
116 var Comm = function (target_name, comm_id) {
136 var Comm = function (target_name, comm_id) {
117 this.target_name = target_name;
137 this.target_name = target_name;
118 this.comm_id = comm_id || utils.uuid();
138 this.comm_id = comm_id || utils.uuid();
119 this._msg_callback = this._close_callback = null;
139 this._msg_callback = this._close_callback = null;
120 };
140 };
121
141
122 // methods for sending messages
142 // methods for sending messages
123 Comm.prototype.open = function (data, callbacks, metadata) {
143 Comm.prototype.open = function (data, callbacks, metadata) {
124 var content = {
144 var content = {
125 comm_id : this.comm_id,
145 comm_id : this.comm_id,
126 target_name : this.target_name,
146 target_name : this.target_name,
127 data : data || {},
147 data : data || {},
128 };
148 };
129 return this.kernel.send_shell_message("comm_open", content, callbacks, metadata);
149 return this.kernel.send_shell_message("comm_open", content, callbacks, metadata);
130 };
150 };
131
151
132 Comm.prototype.send = function (data, callbacks, metadata, buffers) {
152 Comm.prototype.send = function (data, callbacks, metadata, buffers) {
133 var content = {
153 var content = {
134 comm_id : this.comm_id,
154 comm_id : this.comm_id,
135 data : data || {},
155 data : data || {},
136 };
156 };
137 return this.kernel.send_shell_message("comm_msg", content, callbacks, metadata, buffers);
157 return this.kernel.send_shell_message("comm_msg", content, callbacks, metadata, buffers);
138 };
158 };
139
159
140 Comm.prototype.close = function (data, callbacks, metadata) {
160 Comm.prototype.close = function (data, callbacks, metadata) {
141 var content = {
161 var content = {
142 comm_id : this.comm_id,
162 comm_id : this.comm_id,
143 data : data || {},
163 data : data || {},
144 };
164 };
145 return this.kernel.send_shell_message("comm_close", content, callbacks, metadata);
165 return this.kernel.send_shell_message("comm_close", content, callbacks, metadata);
146 };
166 };
147
167
148 // methods for registering callbacks for incoming messages
168 // methods for registering callbacks for incoming messages
149 Comm.prototype._register_callback = function (key, callback) {
169 Comm.prototype._register_callback = function (key, callback) {
150 this['_' + key + '_callback'] = callback;
170 this['_' + key + '_callback'] = callback;
151 };
171 };
152
172
153 Comm.prototype.on_msg = function (callback) {
173 Comm.prototype.on_msg = function (callback) {
154 this._register_callback('msg', callback);
174 this._register_callback('msg', callback);
155 };
175 };
156
176
157 Comm.prototype.on_close = function (callback) {
177 Comm.prototype.on_close = function (callback) {
158 this._register_callback('close', callback);
178 this._register_callback('close', callback);
159 };
179 };
160
180
161 // methods for handling incoming messages
181 // methods for handling incoming messages
162
182
163 Comm.prototype._maybe_callback = function (key, msg) {
183 Comm.prototype._maybe_callback = function (key, msg) {
164 var callback = this['_' + key + '_callback'];
184 var callback = this['_' + key + '_callback'];
165 if (callback) {
185 if (callback) {
166 try {
186 try {
167 callback(msg);
187 callback(msg);
168 } catch (e) {
188 } catch (e) {
169 console.log("Exception in Comm callback", e, e.stack, msg);
189 console.log("Exception in Comm callback", e, e.stack, msg);
170 }
190 }
171 }
191 }
172 };
192 };
173
193
174 Comm.prototype.handle_msg = function (msg) {
194 Comm.prototype.handle_msg = function (msg) {
175 this._maybe_callback('msg', msg);
195 this._maybe_callback('msg', msg);
176 };
196 };
177
197
178 Comm.prototype.handle_close = function (msg) {
198 Comm.prototype.handle_close = function (msg) {
179 this._maybe_callback('close', msg);
199 this._maybe_callback('close', msg);
180 };
200 };
181
201
182 // For backwards compatability.
202 // For backwards compatability.
183 IPython.CommManager = CommManager;
203 IPython.CommManager = CommManager;
184 IPython.Comm = Comm;
204 IPython.Comm = Comm;
185
205
186 return {
206 return {
187 'CommManager': CommManager,
207 'CommManager': CommManager,
188 'Comm': Comm
208 'Comm': Comm
189 };
209 };
190 });
210 });
@@ -1,158 +1,162 b''
1 """Base class for a Comm"""
1 """Base class for a Comm"""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import uuid
6 import uuid
7
7
8 from IPython.config import LoggingConfigurable
8 from IPython.config import LoggingConfigurable
9 from IPython.kernel.zmq.kernelbase import Kernel
9 from IPython.kernel.zmq.kernelbase import Kernel
10
10
11 from IPython.utils.jsonutil import json_clean
11 from IPython.utils.jsonutil import json_clean
12 from IPython.utils.traitlets import Instance, Unicode, Bytes, Bool, Dict, Any
12 from IPython.utils.traitlets import Instance, Unicode, Bytes, Bool, Dict, Any
13
13
14
14
15 class Comm(LoggingConfigurable):
15 class Comm(LoggingConfigurable):
16
16
17 # If this is instantiated by a non-IPython kernel, shell will be None
17 # If this is instantiated by a non-IPython kernel, shell will be None
18 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
18 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
19 allow_none=True)
19 allow_none=True)
20 kernel = Instance('IPython.kernel.zmq.kernelbase.Kernel')
20 kernel = Instance('IPython.kernel.zmq.kernelbase.Kernel')
21 def _kernel_default(self):
21 def _kernel_default(self):
22 if Kernel.initialized():
22 if Kernel.initialized():
23 return Kernel.instance()
23 return Kernel.instance()
24
24
25 iopub_socket = Any()
25 iopub_socket = Any()
26 def _iopub_socket_default(self):
26 def _iopub_socket_default(self):
27 return self.kernel.iopub_socket
27 return self.kernel.iopub_socket
28 session = Instance('IPython.kernel.zmq.session.Session')
28 session = Instance('IPython.kernel.zmq.session.Session')
29 def _session_default(self):
29 def _session_default(self):
30 if self.kernel is not None:
30 if self.kernel is not None:
31 return self.kernel.session
31 return self.kernel.session
32
32
33 target_name = Unicode('comm')
33 target_name = Unicode('comm')
34 target_module = Unicode(None, allow_none=True, help="""requirejs module from
35 which to load comm target.""")
34
36
35 topic = Bytes()
37 topic = Bytes()
36 def _topic_default(self):
38 def _topic_default(self):
37 return ('comm-%s' % self.comm_id).encode('ascii')
39 return ('comm-%s' % self.comm_id).encode('ascii')
38
40
39 _open_data = Dict(help="data dict, if any, to be included in comm_open")
41 _open_data = Dict(help="data dict, if any, to be included in comm_open")
40 _close_data = Dict(help="data dict, if any, to be included in comm_close")
42 _close_data = Dict(help="data dict, if any, to be included in comm_close")
41
43
42 _msg_callback = Any()
44 _msg_callback = Any()
43 _close_callback = Any()
45 _close_callback = Any()
44
46
45 _closed = Bool(True)
47 _closed = Bool(True)
46 comm_id = Unicode()
48 comm_id = Unicode()
47 def _comm_id_default(self):
49 def _comm_id_default(self):
48 return uuid.uuid4().hex
50 return uuid.uuid4().hex
49
51
50 primary = Bool(True, help="Am I the primary or secondary Comm?")
52 primary = Bool(True, help="Am I the primary or secondary Comm?")
51
53
52 def __init__(self, target_name='', data=None, **kwargs):
54 def __init__(self, target_name='', data=None, **kwargs):
53 if target_name:
55 if target_name:
54 kwargs['target_name'] = target_name
56 kwargs['target_name'] = target_name
55 super(Comm, self).__init__(**kwargs)
57 super(Comm, self).__init__(**kwargs)
56 if self.primary:
58 if self.primary:
57 # I am primary, open my peer.
59 # I am primary, open my peer.
58 self.open(data)
60 self.open(data)
59 else:
61 else:
60 self._closed = False
62 self._closed = False
61
63
62 def _publish_msg(self, msg_type, data=None, metadata=None, buffers=None, **keys):
64 def _publish_msg(self, msg_type, data=None, metadata=None, buffers=None, **keys):
63 """Helper for sending a comm message on IOPub"""
65 """Helper for sending a comm message on IOPub"""
64 data = {} if data is None else data
66 data = {} if data is None else data
65 metadata = {} if metadata is None else metadata
67 metadata = {} if metadata is None else metadata
66 content = json_clean(dict(data=data, comm_id=self.comm_id, **keys))
68 content = json_clean(dict(data=data, comm_id=self.comm_id, **keys))
67 self.session.send(self.iopub_socket, msg_type,
69 self.session.send(self.iopub_socket, msg_type,
68 content,
70 content,
69 metadata=json_clean(metadata),
71 metadata=json_clean(metadata),
70 parent=self.kernel._parent_header,
72 parent=self.kernel._parent_header,
71 ident=self.topic,
73 ident=self.topic,
72 buffers=buffers,
74 buffers=buffers,
73 )
75 )
74
76
75 def __del__(self):
77 def __del__(self):
76 """trigger close on gc"""
78 """trigger close on gc"""
77 self.close()
79 self.close()
78
80
79 # publishing messages
81 # publishing messages
80
82
81 def open(self, data=None, metadata=None, buffers=None):
83 def open(self, data=None, metadata=None, buffers=None):
82 """Open the frontend-side version of this comm"""
84 """Open the frontend-side version of this comm"""
83 if data is None:
85 if data is None:
84 data = self._open_data
86 data = self._open_data
85 comm_manager = getattr(self.kernel, 'comm_manager', None)
87 comm_manager = getattr(self.kernel, 'comm_manager', None)
86 if comm_manager is None:
88 if comm_manager is None:
87 raise RuntimeError("Comms cannot be opened without a kernel "
89 raise RuntimeError("Comms cannot be opened without a kernel "
88 "and a comm_manager attached to that kernel.")
90 "and a comm_manager attached to that kernel.")
89
91
90 comm_manager.register_comm(self)
92 comm_manager.register_comm(self)
91 try:
93 try:
92 self._publish_msg('comm_open',
94 self._publish_msg('comm_open',
93 data=data, metadata=metadata, buffers=buffers,
95 data=data, metadata=metadata, buffers=buffers,
94 target_name=self.target_name)
96 target_name=self.target_name,
97 target_module=self.target_module,
98 )
95 self._closed = False
99 self._closed = False
96 except:
100 except:
97 comm_manager.unregister_comm(self)
101 comm_manager.unregister_comm(self)
98 raise
102 raise
99
103
100 def close(self, data=None, metadata=None, buffers=None):
104 def close(self, data=None, metadata=None, buffers=None):
101 """Close the frontend-side version of this comm"""
105 """Close the frontend-side version of this comm"""
102 if self._closed:
106 if self._closed:
103 # only close once
107 # only close once
104 return
108 return
105 self._closed = True
109 self._closed = True
106 if data is None:
110 if data is None:
107 data = self._close_data
111 data = self._close_data
108 self._publish_msg('comm_close',
112 self._publish_msg('comm_close',
109 data=data, metadata=metadata, buffers=buffers,
113 data=data, metadata=metadata, buffers=buffers,
110 )
114 )
111 self.kernel.comm_manager.unregister_comm(self)
115 self.kernel.comm_manager.unregister_comm(self)
112
116
113 def send(self, data=None, metadata=None, buffers=None):
117 def send(self, data=None, metadata=None, buffers=None):
114 """Send a message to the frontend-side version of this comm"""
118 """Send a message to the frontend-side version of this comm"""
115 self._publish_msg('comm_msg',
119 self._publish_msg('comm_msg',
116 data=data, metadata=metadata, buffers=buffers,
120 data=data, metadata=metadata, buffers=buffers,
117 )
121 )
118
122
119 # registering callbacks
123 # registering callbacks
120
124
121 def on_close(self, callback):
125 def on_close(self, callback):
122 """Register a callback for comm_close
126 """Register a callback for comm_close
123
127
124 Will be called with the `data` of the close message.
128 Will be called with the `data` of the close message.
125
129
126 Call `on_close(None)` to disable an existing callback.
130 Call `on_close(None)` to disable an existing callback.
127 """
131 """
128 self._close_callback = callback
132 self._close_callback = callback
129
133
130 def on_msg(self, callback):
134 def on_msg(self, callback):
131 """Register a callback for comm_msg
135 """Register a callback for comm_msg
132
136
133 Will be called with the `data` of any comm_msg messages.
137 Will be called with the `data` of any comm_msg messages.
134
138
135 Call `on_msg(None)` to disable an existing callback.
139 Call `on_msg(None)` to disable an existing callback.
136 """
140 """
137 self._msg_callback = callback
141 self._msg_callback = callback
138
142
139 # handling of incoming messages
143 # handling of incoming messages
140
144
141 def handle_close(self, msg):
145 def handle_close(self, msg):
142 """Handle a comm_close message"""
146 """Handle a comm_close message"""
143 self.log.debug("handle_close[%s](%s)", self.comm_id, msg)
147 self.log.debug("handle_close[%s](%s)", self.comm_id, msg)
144 if self._close_callback:
148 if self._close_callback:
145 self._close_callback(msg)
149 self._close_callback(msg)
146
150
147 def handle_msg(self, msg):
151 def handle_msg(self, msg):
148 """Handle a comm_msg message"""
152 """Handle a comm_msg message"""
149 self.log.debug("handle_msg[%s](%s)", self.comm_id, msg)
153 self.log.debug("handle_msg[%s](%s)", self.comm_id, msg)
150 if self._msg_callback:
154 if self._msg_callback:
151 if self.shell:
155 if self.shell:
152 self.shell.events.trigger('pre_execute')
156 self.shell.events.trigger('pre_execute')
153 self._msg_callback(msg)
157 self._msg_callback(msg)
154 if self.shell:
158 if self.shell:
155 self.shell.events.trigger('post_execute')
159 self.shell.events.trigger('post_execute')
156
160
157
161
158 __all__ = ['Comm']
162 __all__ = ['Comm']
General Comments 0
You need to be logged in to leave comments. Login now