##// END OF EJS Templates
Don't eat comm close error silently.
Jonathan Frederic -
Show More
@@ -1,156 +1,157 b''
1 """Base class to manage comms"""
1 """Base class to manage comms"""
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 sys
6 import sys
7
7
8 from IPython.config import LoggingConfigurable
8 from IPython.config import LoggingConfigurable
9 from IPython.core.prompts import LazyEvaluate
9 from IPython.core.prompts import LazyEvaluate
10 from IPython.core.getipython import get_ipython
10 from IPython.core.getipython import get_ipython
11
11
12 from IPython.utils.importstring import import_item
12 from IPython.utils.importstring import import_item
13 from IPython.utils.py3compat import string_types
13 from IPython.utils.py3compat import string_types
14 from IPython.utils.traitlets import Instance, Unicode, Dict, Any
14 from IPython.utils.traitlets import Instance, Unicode, Dict, Any
15
15
16 from .comm import Comm
16 from .comm import Comm
17
17
18
18
19 def lazy_keys(dikt):
19 def lazy_keys(dikt):
20 """Return lazy-evaluated string representation of a dictionary's keys
20 """Return lazy-evaluated string representation of a dictionary's keys
21
21
22 Key list is only constructed if it will actually be used.
22 Key list is only constructed if it will actually be used.
23 Used for debug-logging.
23 Used for debug-logging.
24 """
24 """
25 return LazyEvaluate(lambda d: list(d.keys()))
25 return LazyEvaluate(lambda d: list(d.keys()))
26
26
27
27
28 class CommManager(LoggingConfigurable):
28 class CommManager(LoggingConfigurable):
29 """Manager for Comms in the Kernel"""
29 """Manager for Comms in the Kernel"""
30
30
31 # If this is instantiated by a non-IPython kernel, shell will be None
31 # If this is instantiated by a non-IPython kernel, shell will be None
32 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
32 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
33 allow_none=True)
33 allow_none=True)
34 kernel = Instance('IPython.kernel.zmq.kernelbase.Kernel')
34 kernel = Instance('IPython.kernel.zmq.kernelbase.Kernel')
35
35
36 iopub_socket = Any()
36 iopub_socket = Any()
37 def _iopub_socket_default(self):
37 def _iopub_socket_default(self):
38 return self.kernel.iopub_socket
38 return self.kernel.iopub_socket
39 session = Instance('IPython.kernel.zmq.session.Session')
39 session = Instance('IPython.kernel.zmq.session.Session')
40 def _session_default(self):
40 def _session_default(self):
41 return self.kernel.session
41 return self.kernel.session
42
42
43 comms = Dict()
43 comms = Dict()
44 targets = Dict()
44 targets = Dict()
45
45
46 # Public APIs
46 # Public APIs
47
47
48 def register_target(self, target_name, f):
48 def register_target(self, target_name, f):
49 """Register a callable f for a given target name
49 """Register a callable f for a given target name
50
50
51 f will be called with two arguments when a comm_open message is received with `target`:
51 f will be called with two arguments when a comm_open message is received with `target`:
52
52
53 - the Comm instance
53 - the Comm instance
54 - the `comm_open` message itself.
54 - the `comm_open` message itself.
55
55
56 f can be a Python callable or an import string for one.
56 f can be a Python callable or an import string for one.
57 """
57 """
58 if isinstance(f, string_types):
58 if isinstance(f, string_types):
59 f = import_item(f)
59 f = import_item(f)
60
60
61 self.targets[target_name] = f
61 self.targets[target_name] = f
62
62
63 def unregister_target(self, target_name, f):
63 def unregister_target(self, target_name, f):
64 """Unregister a callable registered with register_target"""
64 """Unregister a callable registered with register_target"""
65 return self.targets.pop(target_name);
65 return self.targets.pop(target_name);
66
66
67 def register_comm(self, comm):
67 def register_comm(self, comm):
68 """Register a new comm"""
68 """Register a new comm"""
69 comm_id = comm.comm_id
69 comm_id = comm.comm_id
70 comm.shell = self.shell
70 comm.shell = self.shell
71 comm.kernel = self.kernel
71 comm.kernel = self.kernel
72 comm.iopub_socket = self.iopub_socket
72 comm.iopub_socket = self.iopub_socket
73 self.comms[comm_id] = comm
73 self.comms[comm_id] = comm
74 return comm_id
74 return comm_id
75
75
76 def unregister_comm(self, comm):
76 def unregister_comm(self, comm):
77 """Unregister a comm, and close its counterpart"""
77 """Unregister a comm, and close its counterpart"""
78 # unlike get_comm, this should raise a KeyError
78 # unlike get_comm, this should raise a KeyError
79 comm = self.comms.pop(comm.comm_id)
79 comm = self.comms.pop(comm.comm_id)
80
80
81 def get_comm(self, comm_id):
81 def get_comm(self, comm_id):
82 """Get a comm with a particular id
82 """Get a comm with a particular id
83
83
84 Returns the comm if found, otherwise None.
84 Returns the comm if found, otherwise None.
85
85
86 This will not raise an error,
86 This will not raise an error,
87 it will log messages if the comm cannot be found.
87 it will log messages if the comm cannot be found.
88 """
88 """
89 if comm_id not in self.comms:
89 if comm_id not in self.comms:
90 self.log.error("No such comm: %s", comm_id)
90 self.log.error("No such comm: %s", comm_id)
91 self.log.debug("Current comms: %s", lazy_keys(self.comms))
91 self.log.debug("Current comms: %s", lazy_keys(self.comms))
92 return
92 return
93 # call, because we store weakrefs
93 # call, because we store weakrefs
94 comm = self.comms[comm_id]
94 comm = self.comms[comm_id]
95 return comm
95 return comm
96
96
97 # Message handlers
97 # Message handlers
98 def comm_open(self, stream, ident, msg):
98 def comm_open(self, stream, ident, msg):
99 """Handler for comm_open messages"""
99 """Handler for comm_open messages"""
100 content = msg['content']
100 content = msg['content']
101 comm_id = content['comm_id']
101 comm_id = content['comm_id']
102 target_name = content['target_name']
102 target_name = content['target_name']
103 f = self.targets.get(target_name, None)
103 f = self.targets.get(target_name, None)
104 comm = Comm(comm_id=comm_id,
104 comm = Comm(comm_id=comm_id,
105 shell=self.shell,
105 shell=self.shell,
106 kernel=self.kernel,
106 kernel=self.kernel,
107 iopub_socket=self.iopub_socket,
107 iopub_socket=self.iopub_socket,
108 primary=False,
108 primary=False,
109 )
109 )
110 self.register_comm(comm)
110 self.register_comm(comm)
111 if f is None:
111 if f is None:
112 self.log.error("No such comm target registered: %s", target_name)
112 self.log.error("No such comm target registered: %s", target_name)
113 else:
113 else:
114 try:
114 try:
115 f(comm, msg)
115 f(comm, msg)
116 return
116 return
117 except Exception:
117 except Exception:
118 self.log.error("Exception opening comm with target: %s", target_name, exc_info=True)
118 self.log.error("Exception opening comm with target: %s", target_name, exc_info=True)
119
119
120 # Failure.
120 # Failure.
121 try:
121 try:
122 comm.close()
122 comm.close()
123 except:
123 except:
124 pass # Eat errors, nomnomnom
124 self.log.error("""Could not close comm during `comm_open` failure
125 clean-up. The comm may not have been opened yet.""", exc_info=True)
125
126
126 def comm_msg(self, stream, ident, msg):
127 def comm_msg(self, stream, ident, msg):
127 """Handler for comm_msg messages"""
128 """Handler for comm_msg messages"""
128 content = msg['content']
129 content = msg['content']
129 comm_id = content['comm_id']
130 comm_id = content['comm_id']
130 comm = self.get_comm(comm_id)
131 comm = self.get_comm(comm_id)
131 if comm is None:
132 if comm is None:
132 # no such comm
133 # no such comm
133 return
134 return
134 try:
135 try:
135 comm.handle_msg(msg)
136 comm.handle_msg(msg)
136 except Exception:
137 except Exception:
137 self.log.error("Exception in comm_msg for %s", comm_id, exc_info=True)
138 self.log.error("Exception in comm_msg for %s", comm_id, exc_info=True)
138
139
139 def comm_close(self, stream, ident, msg):
140 def comm_close(self, stream, ident, msg):
140 """Handler for comm_close messages"""
141 """Handler for comm_close messages"""
141 content = msg['content']
142 content = msg['content']
142 comm_id = content['comm_id']
143 comm_id = content['comm_id']
143 comm = self.get_comm(comm_id)
144 comm = self.get_comm(comm_id)
144 if comm is None:
145 if comm is None:
145 # no such comm
146 # no such comm
146 self.log.debug("No such comm to close: %s", comm_id)
147 self.log.debug("No such comm to close: %s", comm_id)
147 return
148 return
148 del self.comms[comm_id]
149 del self.comms[comm_id]
149
150
150 try:
151 try:
151 comm.handle_close(msg)
152 comm.handle_close(msg)
152 except Exception:
153 except Exception:
153 self.log.error("Exception handling comm_close for %s", comm_id, exc_info=True)
154 self.log.error("Exception handling comm_close for %s", comm_id, exc_info=True)
154
155
155
156
156 __all__ = ['CommManager']
157 __all__ = ['CommManager']
General Comments 0
You need to be logged in to leave comments. Login now