##// END OF EJS Templates
Unregister comm if there is an error in publishing the comm open message
Jason Grout -
Show More
@@ -1,155 +1,158 b''
1 1 """Base class for a Comm"""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 import uuid
7 7
8 8 from IPython.config import LoggingConfigurable
9 9 from IPython.kernel.zmq.kernelbase import Kernel
10 10
11 11 from IPython.utils.jsonutil import json_clean
12 12 from IPython.utils.traitlets import Instance, Unicode, Bytes, Bool, Dict, Any
13 13
14 14
15 15 class Comm(LoggingConfigurable):
16 16
17 17 # If this is instantiated by a non-IPython kernel, shell will be None
18 18 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
19 19 allow_none=True)
20 20 kernel = Instance('IPython.kernel.zmq.kernelbase.Kernel')
21 21 def _kernel_default(self):
22 22 if Kernel.initialized():
23 23 return Kernel.instance()
24 24
25 25 iopub_socket = Any()
26 26 def _iopub_socket_default(self):
27 27 return self.kernel.iopub_socket
28 28 session = Instance('IPython.kernel.zmq.session.Session')
29 29 def _session_default(self):
30 30 if self.kernel is not None:
31 31 return self.kernel.session
32 32
33 33 target_name = Unicode('comm')
34 34
35 35 topic = Bytes()
36 36 def _topic_default(self):
37 37 return ('comm-%s' % self.comm_id).encode('ascii')
38 38
39 39 _open_data = Dict(help="data dict, if any, to be included in comm_open")
40 40 _close_data = Dict(help="data dict, if any, to be included in comm_close")
41 41
42 42 _msg_callback = Any()
43 43 _close_callback = Any()
44 44
45 45 _closed = Bool(True)
46 46 comm_id = Unicode()
47 47 def _comm_id_default(self):
48 48 return uuid.uuid4().hex
49 49
50 50 primary = Bool(True, help="Am I the primary or secondary Comm?")
51 51
52 52 def __init__(self, target_name='', data=None, **kwargs):
53 53 if target_name:
54 54 kwargs['target_name'] = target_name
55 55 super(Comm, self).__init__(**kwargs)
56 56 if self.primary:
57 57 # I am primary, open my peer.
58 58 self.open(data)
59 59 else:
60 60 self._closed = False
61 61
62 62 def _publish_msg(self, msg_type, data=None, metadata=None, buffers=None, **keys):
63 63 """Helper for sending a comm message on IOPub"""
64 64 data = {} if data is None else data
65 65 metadata = {} if metadata is None else metadata
66 66 content = json_clean(dict(data=data, comm_id=self.comm_id, **keys))
67 67 self.session.send(self.iopub_socket, msg_type,
68 68 content,
69 69 metadata=json_clean(metadata),
70 70 parent=self.kernel._parent_header,
71 71 ident=self.topic,
72 72 buffers=buffers,
73 73 )
74 74
75 75 def __del__(self):
76 76 """trigger close on gc"""
77 77 self.close()
78 78
79 79 # publishing messages
80 80
81 81 def open(self, data=None, metadata=None, buffers=None):
82 82 """Open the frontend-side version of this comm"""
83 83 if data is None:
84 84 data = self._open_data
85 85 comm_manager = getattr(self.kernel, 'comm_manager', None)
86 86 if comm_manager is None:
87 87 raise RuntimeError("Comms cannot be opened without a kernel "
88 88 "and a comm_manager attached to that kernel.")
89 89
90 90 comm_manager.register_comm(self)
91 self._publish_msg('comm_open',
92 data=data, metadata=metadata, buffers=buffers,
93 target_name=self.target_name,
94 )
95 self._closed = False
91 try:
92 self._publish_msg('comm_open',
93 data=data, metadata=metadata, buffers=buffers,
94 target_name=self.target_name)
95 self._closed = False
96 except:
97 comm_manager.unregister_comm(self)
98 raise
96 99
97 100 def close(self, data=None, metadata=None, buffers=None):
98 101 """Close the frontend-side version of this comm"""
99 102 if self._closed:
100 103 # only close once
101 104 return
102 105 self._closed = True
103 106 if data is None:
104 107 data = self._close_data
105 108 self._publish_msg('comm_close',
106 109 data=data, metadata=metadata, buffers=buffers,
107 110 )
108 111 self.kernel.comm_manager.unregister_comm(self)
109 112
110 113 def send(self, data=None, metadata=None, buffers=None):
111 114 """Send a message to the frontend-side version of this comm"""
112 115 self._publish_msg('comm_msg',
113 116 data=data, metadata=metadata, buffers=buffers,
114 117 )
115 118
116 119 # registering callbacks
117 120
118 121 def on_close(self, callback):
119 122 """Register a callback for comm_close
120 123
121 124 Will be called with the `data` of the close message.
122 125
123 126 Call `on_close(None)` to disable an existing callback.
124 127 """
125 128 self._close_callback = callback
126 129
127 130 def on_msg(self, callback):
128 131 """Register a callback for comm_msg
129 132
130 133 Will be called with the `data` of any comm_msg messages.
131 134
132 135 Call `on_msg(None)` to disable an existing callback.
133 136 """
134 137 self._msg_callback = callback
135 138
136 139 # handling of incoming messages
137 140
138 141 def handle_close(self, msg):
139 142 """Handle a comm_close message"""
140 143 self.log.debug("handle_close[%s](%s)", self.comm_id, msg)
141 144 if self._close_callback:
142 145 self._close_callback(msg)
143 146
144 147 def handle_msg(self, msg):
145 148 """Handle a comm_msg message"""
146 149 self.log.debug("handle_msg[%s](%s)", self.comm_id, msg)
147 150 if self._msg_callback:
148 151 if self.shell:
149 152 self.shell.events.trigger('pre_execute')
150 153 self._msg_callback(msg)
151 154 if self.shell:
152 155 self.shell.events.trigger('post_execute')
153 156
154 157
155 158 __all__ = ['Comm']
General Comments 0
You need to be logged in to leave comments. Login now