##// END OF EJS Templates
pass whole message to Comm handlers
MinRK -
Show More
@@ -1,150 +1,150 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 = {comm : Comm};
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, constructor) {
44 44 // Register a constructor for a given target key
45 45 this.targets[target] = constructor;
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 64 var callback = this.targets[content.target];
65 65 if (callback === undefined) {
66 66 console.log("No such target registered: ", content.target);
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 72 callback(comm);
73 comm.handle_open(content.data);
73 comm.handle_open(msg);
74 74 };
75 75
76 76 CommManager.prototype.comm_close = function (msg) {
77 77 var content = msg.content;
78 78 var comm = this.comms[content.comm_id];
79 79 if (comm === undefined) {
80 80 return;
81 81 }
82 82 delete this.comms[content.comm_id];
83 comm.handle_close(content.data);
83 comm.handle_close(msg);
84 84 };
85 85
86 86 CommManager.prototype.comm_msg = function (msg) {
87 87 var content = msg.content;
88 88 var comm = this.comms[content.comm_id];
89 89 if (comm === undefined) {
90 90 return;
91 91 }
92 comm.handle_msg(content.data);
92 comm.handle_msg(msg);
93 93 };
94 94
95 95 //-----------------------------------------------------------------------
96 96 // Comm base class
97 97 //-----------------------------------------------------------------------
98 98
99 var Comm = function (comm_id) {
99 var Comm = function (comm_id, target) {
100 100 this.comm_id = comm_id;
101 this.target = 'comm';
101 this.target = target || 'comm';
102 102 };
103 103
104 104 // methods for sending messages
105 105 Comm.prototype.open = function (data) {
106 106 var content = {
107 107 comm_id : this.comm_id,
108 108 target : this.target,
109 109 data : data || {},
110 110 };
111 111 this.kernel.send_shell_message("comm_open", content);
112 112 };
113 113
114 114 Comm.prototype.send = function (data) {
115 115 var content = {
116 116 comm_id : this.comm_id,
117 117 data : data || {},
118 118 };
119 119 return this.kernel.send_shell_message("comm_msg", content);
120 120 };
121 121
122 122 Comm.prototype.close = function (data) {
123 123 var content = {
124 124 comm_id : this.comm_id,
125 125 data : data || {},
126 126 };
127 127 return this.kernel.send_shell_message("comm_close", content);
128 128 };
129 129
130 130 // methods for handling incoming messages
131 131
132 Comm.prototype.handle_open = function (data) {
133 $([this]).trigger("comm_open", data);
132 Comm.prototype.handle_open = function (msg) {
133 $([this]).trigger("comm_open", msg);
134 134 };
135 135
136 Comm.prototype.handle_msg = function (data) {
137 $([this]).trigger("comm_msg", data);
136 Comm.prototype.handle_msg = function (msg) {
137 $([this]).trigger("comm_msg", msg);
138 138 };
139 139
140 Comm.prototype.handle_close = function (data) {
141 $([this]).trigger("comm_close", data);
140 Comm.prototype.handle_close = function (msg) {
141 $([this]).trigger("comm_close", msg);
142 142 };
143 143
144 144 IPython.CommManager = CommManager;
145 145 IPython.Comm = Comm;
146 146
147 147 return IPython;
148 148
149 149 }(IPython));
150 150
@@ -1,152 +1,149 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 iopub_socket = Any()
31 31 def _iopub_socket_default(self):
32 32 return self.shell.parent.iopub_socket
33 33 session = Instance('IPython.kernel.zmq.session.Session')
34 34 def _session_default(self):
35 35 if self.shell is None:
36 36 return
37 37 return self.shell.parent.session
38 38
39 39 target = Unicode('comm')
40 40
41 41 topic = Bytes()
42 42 def _topic_default(self):
43 43 return ('comm-%s' % self.comm_id).encode('ascii')
44 44
45 45 _open_data = Dict(help="data dict, if any, to be included in comm_close")
46 46 _close_data = Dict(help="data dict, if any, to be included in comm_close")
47 47
48 48 _open_callback = Any()
49 49 _msg_callback = Any()
50 50 _close_callback = Any()
51 51
52 52 _closed = Bool(False)
53 53 comm_id = Unicode()
54 54 def _comm_id_default(self):
55 55 return uuid.uuid4().hex
56 56
57 57 primary = Bool(True, help="Am I the primary or secondary Comm?")
58 58
59 def __init__(self, **kwargs):
59 def __init__(self, data=None, **kwargs):
60 60 super(Comm, self).__init__(**kwargs)
61 61 get_ipython().comm_manager.register_comm(self)
62 62 if self.primary:
63 63 # I am primary, open my peer
64 self.open()
65 else:
66 # I am secondary, handle creation
67 self.handle_open(self._open_data)
64 self.open(data)
68 65
69 66 def _publish_msg(self, msg_type, data=None, **keys):
70 67 """Helper for sending a comm message on IOPub"""
71 68 data = {} if data is None else data
72 69 self.session.send(self.iopub_socket, msg_type,
73 70 dict(data=data, comm_id=self.comm_id, **keys),
74 71 ident=self.topic,
75 72 )
76 73
77 74 def __del__(self):
78 75 """trigger close on gc"""
79 76 self.close()
80 77
81 78 # publishing messages
82 79
83 80 def open(self, data=None):
84 81 """Open the frontend-side version of this comm"""
85 82 if data is None:
86 83 data = self._open_data
87 84 self._publish_msg('comm_open', data, target=self.target)
88 85
89 86 def close(self, data=None):
90 87 """Close the frontend-side version of this comm"""
91 88 if self._closed:
92 89 # only close once
93 90 return
94 91 if data is None:
95 92 data = self._close_data
96 93 self._publish_msg('comm_close', data)
97 94 self._closed = True
98 95
99 96 def send(self, data=None):
100 """Update the frontend-side version of this comm"""
97 """Send a message to the frontend-side version of this comm"""
101 98 self._publish_msg('comm_msg', data)
102 99
103 100 # registering callbacks
104 101 def on_open(self, callback):
105 102 """Register a callback for comm_open
106 103
107 104 Will be called with the `data` of the open message.
108 105
109 106 Call `on_open(None)` to disable an existing callback.
110 107 """
111 108 self._open_callback = callback
112 109
113 110 def on_close(self, callback):
114 111 """Register a callback for comm_close
115 112
116 113 Will be called with the `data` of the close message.
117 114
118 115 Call `on_close(None)` to disable an existing callback.
119 116 """
120 117 self._close_callback = callback
121 118
122 119 def on_msg(self, callback):
123 120 """Register a callback for comm_msg
124 121
125 122 Will be called with the `data` of any comm_msg messages.
126 123
127 124 Call `on_msg(None)` to disable an existing callback.
128 125 """
129 126 self._msg_callback = callback
130 127
131 128 # handling of incoming messages
132 129
133 def handle_open(self, data):
130 def handle_open(self, msg):
134 131 """Handle a comm_open message"""
135 self.log.debug("handle_open[%s](%s)", self.comm_id, data)
132 self.log.debug("handle_open[%s](%s)", self.comm_id, msg)
136 133 if self._open_callback:
137 self._open_callback(data)
134 self._open_callback(msg)
138 135
139 def handle_close(self, data):
136 def handle_close(self, msg):
140 137 """Handle a comm_close message"""
141 self.log.debug("handle_close[%s](%s)", self.comm_id, data)
138 self.log.debug("handle_close[%s](%s)", self.comm_id, msg)
142 139 if self._close_callback:
143 self._close_callback(data)
140 self._close_callback(msg)
144 141
145 def handle_msg(self, data):
142 def handle_msg(self, msg):
146 143 """Handle a comm_msg message"""
147 self.log.debug("handle_msg[%s](%s)", self.comm_id, data)
144 self.log.debug("handle_msg[%s](%s)", self.comm_id, msg)
148 145 if self._msg_callback:
149 self._msg_callback(data)
146 self._msg_callback(msg)
150 147
151 148
152 149 __all__ = ['Comm']
@@ -1,142 +1,142 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 from IPython.config import LoggingConfigurable
15 15 from IPython.core.prompts import LazyEvaluate
16 16 from IPython.core.getipython import get_ipython
17 17
18 18 from IPython.utils.importstring import import_item
19 19 from IPython.utils.traitlets import Instance, Unicode, Dict, Any
20 20
21 21 from .comm import Comm
22 22
23 23 #-----------------------------------------------------------------------------
24 24 # Code
25 25 #-----------------------------------------------------------------------------
26 26
27 27 def lazy_keys(dikt):
28 28 """Return lazy-evaluated string representation of a dictionary's keys
29 29
30 30 Key list is only constructed if it will actually be used.
31 31 Used for debug-logging.
32 32 """
33 33 return LazyEvaluate(lambda d: list(d.keys()))
34 34
35 35
36 36 class CommManager(LoggingConfigurable):
37 37 """Manager for Comms in the Kernel"""
38 38
39 39 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
40 40 def _shell_default(self):
41 41 return get_ipython()
42 42 iopub_socket = Any()
43 43 def _iopub_socket_default(self):
44 44 return self.shell.parent.iopub_socket
45 45 session = Instance('IPython.kernel.zmq.session.Session')
46 46 def _session_default(self):
47 47 if self.shell is None:
48 48 return
49 49 return self.shell.parent.session
50 50
51 51 comms = Dict()
52 52 targets = Dict()
53 53
54 54 # Public APIs
55 55
56 56 def register_target(self, target, f):
57 57 """Register a callable f for a given target
58 58
59 59 f will be called with a Comm object as its only argument
60 60 when a comm_open message is received with `target`.
61 61
62 62 f can be a Python callable or an import string for one.
63 63 """
64 64 if isinstance(f, basestring):
65 65 f = import_item(f)
66 66
67 67 self.targets[target] = f
68 68
69 69 def register_comm(self, comm):
70 70 """Register a new comm"""
71 71 comm_id = comm.comm_id
72 72 comm.shell = self.shell
73 73 comm.iopub_socket = self.iopub_socket
74 74 self.comms[comm_id] = comm
75 75 return comm_id
76 76
77 77 def unregister_comm(self, comm_id):
78 78 """Unregister a comm, and close its counterpart"""
79 79 # unlike get_comm, this should raise a KeyError
80 80 comm = self.comms.pop(comm_id)
81 81 comm.close()
82 82
83 83 def get_comm(self, comm_id):
84 84 """Get a comm with a particular id
85 85
86 86 Returns the comm if found, otherwise None.
87 87
88 88 This will not raise an error,
89 89 it will log messages if the comm cannot be found.
90 90 """
91 91 if comm_id not in self.comms:
92 92 self.log.error("No such comm: %s", comm_id)
93 93 self.log.debug("Current comms: %s", lazy_keys(self.comms))
94 94 return
95 95 # call, because we store weakrefs
96 96 comm = self.comms[comm_id]
97 97 return comm
98 98
99 99 # Message handlers
100 100
101 101 def comm_open(self, stream, ident, msg):
102 102 """Handler for comm_open messages"""
103 103 content = msg['content']
104 104 comm_id = content['comm_id']
105 105 target = content['target']
106 106 callback = self.targets.get(target, None)
107 107 comm = Comm(comm_id=comm_id,
108 108 shell=self.shell,
109 109 iopub_socket=self.iopub_socket,
110 _open_data=content['data'],
111 110 primary=False,
112 111 )
113 112 if callback is None:
114 113 self.log.error("No such comm target registered: %s", target)
115 114 comm.close()
116 115 return
117 116 callback(comm)
117 comm.handle_open(msg)
118 118 self.register_comm(comm)
119 119
120 120 def comm_msg(self, stream, ident, msg):
121 121 """Handler for comm_msg messages"""
122 122 content = msg['content']
123 123 comm_id = content['comm_id']
124 124 comm = self.get_comm(comm_id)
125 125 if comm is None:
126 126 # no such comm
127 127 return
128 comm.handle_msg(content['data'])
128 comm.handle_msg(msg)
129 129
130 130 def comm_close(self, stream, ident, msg):
131 131 """Handler for comm_close messages"""
132 132 content = msg['content']
133 133 comm_id = content['comm_id']
134 134 comm = self.get_comm(comm_id)
135 135 if comm is None:
136 136 # no such comm
137 137 return
138 comm.handle_close(content['data'])
139 138 del self.comms[comm_id]
139 comm.handle_close(msg)
140 140
141 141
142 142 __all__ = ['CommManager']
General Comments 0
You need to be logged in to leave comments. Login now