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