##// END OF EJS Templates
log exceptions in Comm handlers
MinRK -
Show More
@@ -1,169 +1,183
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2013 The IPython Development Team
2 // Copyright (C) 2013 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Comm and CommManager bases
9 // Comm and CommManager bases
10 //============================================================================
10 //============================================================================
11 /**
11 /**
12 * Base Comm classes
12 * Base Comm classes
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 * @submodule comm
15 * @submodule comm
16 */
16 */
17
17
18 var IPython = (function (IPython) {
18 var IPython = (function (IPython) {
19 "use strict";
19 "use strict";
20
20
21 //-----------------------------------------------------------------------
21 //-----------------------------------------------------------------------
22 // CommManager class
22 // CommManager class
23 //-----------------------------------------------------------------------
23 //-----------------------------------------------------------------------
24
24
25 var CommManager = function (kernel) {
25 var CommManager = function (kernel) {
26 this.comms = {};
26 this.comms = {};
27 this.targets = {};
27 this.targets = {};
28 if (kernel !== undefined) {
28 if (kernel !== undefined) {
29 this.init_kernel(kernel);
29 this.init_kernel(kernel);
30 }
30 }
31 };
31 };
32
32
33 CommManager.prototype.init_kernel = function (kernel) {
33 CommManager.prototype.init_kernel = function (kernel) {
34 // connect the kernel, and register message handlers
34 // connect the kernel, and register message handlers
35 this.kernel = kernel;
35 this.kernel = kernel;
36 var msg_types = ['comm_open', 'comm_msg', 'comm_close'];
36 var msg_types = ['comm_open', 'comm_msg', 'comm_close'];
37 for (var i = 0; i < msg_types.length; i++) {
37 for (var i = 0; i < msg_types.length; i++) {
38 var msg_type = msg_types[i];
38 var msg_type = msg_types[i];
39 kernel.register_iopub_handler(msg_type, $.proxy(this[msg_type], this));
39 kernel.register_iopub_handler(msg_type, $.proxy(this[msg_type], this));
40 }
40 }
41 };
41 };
42
42
43 CommManager.prototype.register_target = function (target_name, f) {
43 CommManager.prototype.register_target = function (target_name, f) {
44 // Register a target function for a given target name
44 // Register a target function for a given target name
45 this.targets[target_name] = f;
45 this.targets[target_name] = f;
46 };
46 };
47
47
48 CommManager.prototype.unregister_target = function (target_name, f) {
48 CommManager.prototype.unregister_target = function (target_name, f) {
49 // Unregister a target function for a given target name
49 // Unregister a target function for a given target name
50 delete this.targets[target_name];
50 delete this.targets[target_name];
51 };
51 };
52
52
53 CommManager.prototype.register_comm = function (comm) {
53 CommManager.prototype.register_comm = function (comm) {
54 // Register a comm in the mapping
54 // Register a comm in the mapping
55 this.comms[comm.comm_id] = comm;
55 this.comms[comm.comm_id] = comm;
56 comm.kernel = this.kernel;
56 comm.kernel = this.kernel;
57 return comm.comm_id;
57 return comm.comm_id;
58 };
58 };
59
59
60 CommManager.prototype.unregister_comm = function (comm_id) {
60 CommManager.prototype.unregister_comm = function (comm_id) {
61 // Remove a comm from the mapping
61 // Remove a comm from the mapping
62 delete this.comms[comm_id];
62 delete this.comms[comm_id];
63 };
63 };
64
64
65 // comm message handlers
65 // comm message handlers
66
66
67 CommManager.prototype.comm_open = function (msg) {
67 CommManager.prototype.comm_open = function (msg) {
68 var content = msg.content;
68 var content = msg.content;
69 var f = this.targets[content.target_name];
69 var f = this.targets[content.target_name];
70 if (f === undefined) {
70 if (f === undefined) {
71 console.log("No such target registered: ", content.target_name);
71 console.log("No such target registered: ", content.target_name);
72 console.log("Available targets are: ", this.targets);
72 console.log("Available targets are: ", this.targets);
73 return;
73 return;
74 }
74 }
75 var comm = new Comm(content.comm_id);
75 var comm = new Comm(content.comm_id);
76 this.register_comm(comm);
76 this.register_comm(comm);
77 try {
77 f(comm, msg);
78 f(comm, msg);
79 } catch (e) {
80 console.log("Exception opening new comm:", e, msg);
81 comm.close();
82 this.unregister_comm(comm);
83 }
78 };
84 };
79
85
80 CommManager.prototype.comm_close = function (msg) {
86 CommManager.prototype.comm_close = function (msg) {
81 var content = msg.content;
87 var content = msg.content;
82 var comm = this.comms[content.comm_id];
88 var comm = this.comms[content.comm_id];
83 if (comm === undefined) {
89 if (comm === undefined) {
84 return;
90 return;
85 }
91 }
86 delete this.comms[content.comm_id];
92 delete this.comms[content.comm_id];
93 try {
87 comm.handle_close(msg);
94 comm.handle_close(msg);
95 } catch (e) {
96 console.log("Exception closing comm: ", e, msg);
97 }
88 };
98 };
89
99
90 CommManager.prototype.comm_msg = function (msg) {
100 CommManager.prototype.comm_msg = function (msg) {
91 var content = msg.content;
101 var content = msg.content;
92 var comm = this.comms[content.comm_id];
102 var comm = this.comms[content.comm_id];
93 if (comm === undefined) {
103 if (comm === undefined) {
94 return;
104 return;
95 }
105 }
106 try {
96 comm.handle_msg(msg);
107 comm.handle_msg(msg);
108 } catch (e) {
109 console.log("Exception handling comm msg: ", e, msg);
110 }
97 };
111 };
98
112
99 //-----------------------------------------------------------------------
113 //-----------------------------------------------------------------------
100 // Comm base class
114 // Comm base class
101 //-----------------------------------------------------------------------
115 //-----------------------------------------------------------------------
102
116
103 var Comm = function (comm_id, target_name) {
117 var Comm = function (comm_id, target_name) {
104 this.comm_id = comm_id || new IPython.utils.uuid();
118 this.comm_id = comm_id || new IPython.utils.uuid();
105 this.target_name = target_name;
119 this.target_name = target_name;
106 this._msg_callback = this._close_callback = null;
120 this._msg_callback = this._close_callback = null;
107 };
121 };
108
122
109 // methods for sending messages
123 // methods for sending messages
110 Comm.prototype.open = function (data, callbacks, metadata) {
124 Comm.prototype.open = function (data, callbacks, metadata) {
111 var content = {
125 var content = {
112 comm_id : this.comm_id,
126 comm_id : this.comm_id,
113 target_name : this.target_name,
127 target_name : this.target_name,
114 data : data || {},
128 data : data || {},
115 };
129 };
116 return this.kernel.send_shell_message("comm_open", content, callbacks, metadata);
130 return this.kernel.send_shell_message("comm_open", content, callbacks, metadata);
117 };
131 };
118
132
119 Comm.prototype.send = function (data, callbacks, metadata) {
133 Comm.prototype.send = function (data, callbacks, metadata) {
120 var content = {
134 var content = {
121 comm_id : this.comm_id,
135 comm_id : this.comm_id,
122 data : data || {},
136 data : data || {},
123 };
137 };
124 return this.kernel.send_shell_message("comm_msg", content, callbacks, metadata);
138 return this.kernel.send_shell_message("comm_msg", content, callbacks, metadata);
125 };
139 };
126
140
127 Comm.prototype.close = function (data, callbacks, metadata) {
141 Comm.prototype.close = function (data, callbacks, metadata) {
128 var content = {
142 var content = {
129 comm_id : this.comm_id,
143 comm_id : this.comm_id,
130 data : data || {},
144 data : data || {},
131 };
145 };
132 return this.kernel.send_shell_message("comm_close", content, callbacks, metadata);
146 return this.kernel.send_shell_message("comm_close", content, callbacks, metadata);
133 };
147 };
134
148
135 // methods for registering callbacks for incoming messages
149 // methods for registering callbacks for incoming messages
136 Comm.prototype._register_callback = function (key, callback) {
150 Comm.prototype._register_callback = function (key, callback) {
137 this['_' + key + '_callback'] = callback;
151 this['_' + key + '_callback'] = callback;
138 };
152 };
139
153
140 Comm.prototype.on_msg = function (callback) {
154 Comm.prototype.on_msg = function (callback) {
141 this._register_callback('msg', callback);
155 this._register_callback('msg', callback);
142 };
156 };
143
157
144 Comm.prototype.on_close = function (callback) {
158 Comm.prototype.on_close = function (callback) {
145 this._register_callback('close', callback);
159 this._register_callback('close', callback);
146 };
160 };
147
161
148 // methods for handling incoming messages
162 // methods for handling incoming messages
149
163
150 Comm.prototype._maybe_callback = function (key, msg) {
164 Comm.prototype._maybe_callback = function (key, msg) {
151 var callback = this['_' + key + '_callback'];
165 var callback = this['_' + key + '_callback'];
152 if (callback) callback(msg);
166 if (callback) callback(msg);
153 };
167 };
154
168
155 Comm.prototype.handle_msg = function (msg) {
169 Comm.prototype.handle_msg = function (msg) {
156 this._maybe_callback('msg', msg);
170 this._maybe_callback('msg', msg);
157 };
171 };
158
172
159 Comm.prototype.handle_close = function (msg) {
173 Comm.prototype.handle_close = function (msg) {
160 this._maybe_callback('close', msg);
174 this._maybe_callback('close', msg);
161 };
175 };
162
176
163 IPython.CommManager = CommManager;
177 IPython.CommManager = CommManager;
164 IPython.Comm = Comm;
178 IPython.Comm = Comm;
165
179
166 return IPython;
180 return IPython;
167
181
168 }(IPython));
182 }(IPython));
169
183
@@ -1,170 +1,185
1 """Base class to manage comms"""
1 """Base class to manage comms"""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2013 The IPython Development Team
4 # Copyright (C) 2013 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 import sys
14 import sys
15
15
16 from IPython.config import LoggingConfigurable
16 from IPython.config import LoggingConfigurable
17 from IPython.core.prompts import LazyEvaluate
17 from IPython.core.prompts import LazyEvaluate
18 from IPython.core.getipython import get_ipython
18 from IPython.core.getipython import get_ipython
19
19
20 from IPython.utils.importstring import import_item
20 from IPython.utils.importstring import import_item
21 from IPython.utils.traitlets import Instance, Unicode, Dict, Any
21 from IPython.utils.traitlets import Instance, Unicode, Dict, Any
22
22
23 from .comm import Comm
23 from .comm import Comm
24
24
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # Code
26 # Code
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28
28
29 def lazy_keys(dikt):
29 def lazy_keys(dikt):
30 """Return lazy-evaluated string representation of a dictionary's keys
30 """Return lazy-evaluated string representation of a dictionary's keys
31
31
32 Key list is only constructed if it will actually be used.
32 Key list is only constructed if it will actually be used.
33 Used for debug-logging.
33 Used for debug-logging.
34 """
34 """
35 return LazyEvaluate(lambda d: list(d.keys()))
35 return LazyEvaluate(lambda d: list(d.keys()))
36
36
37
37
38 def with_output(method):
38 def with_output(method):
39 """method decorator for ensuring output is handled properly in a message handler
39 """method decorator for ensuring output is handled properly in a message handler
40
40
41 - sets parent header before entering the method
41 - sets parent header before entering the method
42 - publishes busy/idle
42 - publishes busy/idle
43 - flushes stdout/stderr after
43 - flushes stdout/stderr after
44 """
44 """
45 def method_with_output(self, stream, ident, msg):
45 def method_with_output(self, stream, ident, msg):
46 parent = msg['header']
46 parent = msg['header']
47 self.shell.set_parent(parent)
47 self.shell.set_parent(parent)
48 self.shell.kernel._publish_status('busy')
48 self.shell.kernel._publish_status('busy')
49 try:
49 try:
50 return method(self, stream, ident, msg)
50 return method(self, stream, ident, msg)
51 finally:
51 finally:
52 sys.stdout.flush()
52 sys.stdout.flush()
53 sys.stderr.flush()
53 sys.stderr.flush()
54 self.shell.kernel._publish_status('idle')
54 self.shell.kernel._publish_status('idle')
55
55
56 return method_with_output
56 return method_with_output
57
57
58
58
59 class CommManager(LoggingConfigurable):
59 class CommManager(LoggingConfigurable):
60 """Manager for Comms in the Kernel"""
60 """Manager for Comms in the Kernel"""
61
61
62 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
62 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
63 def _shell_default(self):
63 def _shell_default(self):
64 return get_ipython()
64 return get_ipython()
65 iopub_socket = Any()
65 iopub_socket = Any()
66 def _iopub_socket_default(self):
66 def _iopub_socket_default(self):
67 return self.shell.kernel.iopub_socket
67 return self.shell.kernel.iopub_socket
68 session = Instance('IPython.kernel.zmq.session.Session')
68 session = Instance('IPython.kernel.zmq.session.Session')
69 def _session_default(self):
69 def _session_default(self):
70 if self.shell is None:
70 if self.shell is None:
71 return
71 return
72 return self.shell.kernel.session
72 return self.shell.kernel.session
73
73
74 comms = Dict()
74 comms = Dict()
75 targets = Dict()
75 targets = Dict()
76
76
77 # Public APIs
77 # Public APIs
78
78
79 def register_target(self, target_name, f):
79 def register_target(self, target_name, f):
80 """Register a callable f for a given target name
80 """Register a callable f for a given target name
81
81
82 f will be called with a Comm object as its only argument
82 f will be called with two arguments when a comm_open message is received with `target`:
83 when a comm_open message is received with `target`.
83
84 - the Comm instance
85 - the `comm_open` message itself.
84
86
85 f can be a Python callable or an import string for one.
87 f can be a Python callable or an import string for one.
86 """
88 """
87 if isinstance(f, basestring):
89 if isinstance(f, basestring):
88 f = import_item(f)
90 f = import_item(f)
89
91
90 self.targets[target_name] = f
92 self.targets[target_name] = f
91
93
92 def unregister_target(self, target_name, f):
94 def unregister_target(self, target_name, f):
93 """Unregister a callable registered with register_target"""
95 """Unregister a callable registered with register_target"""
94 return self.targets.pop(target_name);
96 return self.targets.pop(target_name);
95
97
96 def register_comm(self, comm):
98 def register_comm(self, comm):
97 """Register a new comm"""
99 """Register a new comm"""
98 comm_id = comm.comm_id
100 comm_id = comm.comm_id
99 comm.shell = self.shell
101 comm.shell = self.shell
100 comm.iopub_socket = self.iopub_socket
102 comm.iopub_socket = self.iopub_socket
101 self.comms[comm_id] = comm
103 self.comms[comm_id] = comm
102 return comm_id
104 return comm_id
103
105
104 def unregister_comm(self, comm_id):
106 def unregister_comm(self, comm_id):
105 """Unregister a comm, and close its counterpart"""
107 """Unregister a comm, and close its counterpart"""
106 # unlike get_comm, this should raise a KeyError
108 # unlike get_comm, this should raise a KeyError
107 comm = self.comms.pop(comm_id)
109 comm = self.comms.pop(comm_id)
108 comm.close()
110 comm.close()
109
111
110 def get_comm(self, comm_id):
112 def get_comm(self, comm_id):
111 """Get a comm with a particular id
113 """Get a comm with a particular id
112
114
113 Returns the comm if found, otherwise None.
115 Returns the comm if found, otherwise None.
114
116
115 This will not raise an error,
117 This will not raise an error,
116 it will log messages if the comm cannot be found.
118 it will log messages if the comm cannot be found.
117 """
119 """
118 if comm_id not in self.comms:
120 if comm_id not in self.comms:
119 self.log.error("No such comm: %s", comm_id)
121 self.log.error("No such comm: %s", comm_id)
120 self.log.debug("Current comms: %s", lazy_keys(self.comms))
122 self.log.debug("Current comms: %s", lazy_keys(self.comms))
121 return
123 return
122 # call, because we store weakrefs
124 # call, because we store weakrefs
123 comm = self.comms[comm_id]
125 comm = self.comms[comm_id]
124 return comm
126 return comm
125
127
126 # Message handlers
128 # Message handlers
127 @with_output
129 @with_output
128 def comm_open(self, stream, ident, msg):
130 def comm_open(self, stream, ident, msg):
129 """Handler for comm_open messages"""
131 """Handler for comm_open messages"""
130 content = msg['content']
132 content = msg['content']
131 comm_id = content['comm_id']
133 comm_id = content['comm_id']
132 target_name = content['target_name']
134 target_name = content['target_name']
133 f = self.targets.get(target_name, None)
135 f = self.targets.get(target_name, None)
134 comm = Comm(comm_id=comm_id,
136 comm = Comm(comm_id=comm_id,
135 shell=self.shell,
137 shell=self.shell,
136 iopub_socket=self.iopub_socket,
138 iopub_socket=self.iopub_socket,
137 primary=False,
139 primary=False,
138 )
140 )
139 if f is None:
141 if f is None:
140 self.log.error("No such comm target registered: %s", target_name)
142 self.log.error("No such comm target registered: %s", target_name)
141 comm.close()
143 comm.close()
142 return
144 return
143 self.register_comm(comm)
145 self.register_comm(comm)
146 try:
144 f(comm, msg)
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)
145
152
146 @with_output
153 @with_output
147 def comm_msg(self, stream, ident, msg):
154 def comm_msg(self, stream, ident, msg):
148 """Handler for comm_msg messages"""
155 """Handler for comm_msg messages"""
149 content = msg['content']
156 content = msg['content']
150 comm_id = content['comm_id']
157 comm_id = content['comm_id']
151 comm = self.get_comm(comm_id)
158 comm = self.get_comm(comm_id)
152 if comm is None:
159 if comm is None:
153 # no such comm
160 # no such comm
154 return
161 return
162 try:
155 comm.handle_msg(msg)
163 comm.handle_msg(msg)
164 except Exception:
165 self.log.error("Exception in comm_msg for %s", comm_id, exc_info=True)
156
166
157 @with_output
167 @with_output
158 def comm_close(self, stream, ident, msg):
168 def comm_close(self, stream, ident, msg):
159 """Handler for comm_close messages"""
169 """Handler for comm_close messages"""
160 content = msg['content']
170 content = msg['content']
161 comm_id = content['comm_id']
171 comm_id = content['comm_id']
162 comm = self.get_comm(comm_id)
172 comm = self.get_comm(comm_id)
163 if comm is None:
173 if comm is None:
164 # no such comm
174 # no such comm
175 self.log.debug("No such comm to close: %s", comm_id)
165 return
176 return
166 del self.comms[comm_id]
177 del self.comms[comm_id]
178
179 try:
167 comm.handle_close(msg)
180 comm.handle_close(msg)
181 except Exception:
182 self.log.error("Exception handling comm_close for %s", comm_id, exc_info=True)
168
183
169
184
170 __all__ = ['CommManager']
185 __all__ = ['CommManager']
General Comments 0
You need to be logged in to leave comments. Login now