##// END OF EJS Templates
Promise logic is infectious like a disease
Jonathan Frederic -
Show More
@@ -1,202 +1,211
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 'base/js/namespace',
6 6 'jquery',
7 7 'base/js/utils',
8 8 ], function(IPython, $, utils) {
9 9 "use strict";
10 10
11 11 //-----------------------------------------------------------------------
12 12 // CommManager class
13 13 //-----------------------------------------------------------------------
14 14
15 15 var CommManager = function (kernel) {
16 16 this.comms = {};
17 17 this.targets = {};
18 18 if (kernel !== undefined) {
19 19 this.init_kernel(kernel);
20 20 }
21 21 };
22 22
23 23 CommManager.prototype.init_kernel = function (kernel) {
24 24 // connect the kernel, and register message handlers
25 25 this.kernel = kernel;
26 26 var msg_types = ['comm_open', 'comm_msg', 'comm_close'];
27 27 for (var i = 0; i < msg_types.length; i++) {
28 28 var msg_type = msg_types[i];
29 29 kernel.register_iopub_handler(msg_type, $.proxy(this[msg_type], this));
30 30 }
31 31 };
32 32
33 33 CommManager.prototype.new_comm = function (target_name, data, callbacks, metadata) {
34 34 // Create a new Comm, register it, and open its Kernel-side counterpart
35 35 // Mimics the auto-registration in `Comm.__init__` in the IPython Comm
36 36 var comm = new Comm(target_name);
37 37 this.register_comm(comm);
38 38 comm.open(data, callbacks, metadata);
39 39 return comm;
40 40 };
41 41
42 42 CommManager.prototype.register_target = function (target_name, f) {
43 43 // Register a target function for a given target name
44 44 this.targets[target_name] = f;
45 45 };
46 46
47 47 CommManager.prototype.unregister_target = function (target_name, f) {
48 48 // Unregister a target function for a given target name
49 49 delete this.targets[target_name];
50 50 };
51 51
52 52 CommManager.prototype.register_comm = function (comm) {
53 53 // Register a comm in the mapping
54 this.comms[comm.comm_id] = comm;
54 this.comms[comm.comm_id] = new Promise(function(resolve) {resolve(comm);});
55 55 comm.kernel = this.kernel;
56 56 return comm.comm_id;
57 57 };
58 58
59 59 CommManager.prototype.unregister_comm = function (comm) {
60 60 // Remove a comm from the mapping
61 61 delete this.comms[comm.comm_id];
62 62 };
63 63
64 64 // comm message handlers
65 65
66 66 CommManager.prototype.comm_open = function (msg) {
67 67 var content = msg.content;
68 68 var that = this;
69
70 return utils.load_class(content.target_name, content.target_module,
69 var comm_id = content.comm_id;
70
71 this.comms[comm_id] = utils.load_class(content.target_name, content.target_module,
71 72 this.targets).then(function(target) {
72 73
73 var comm = new Comm(content.target_name, content.comm_id);
74 that.register_comm(comm);
74 var comm = new Comm(content.target_name, comm_id);
75 comm.kernel = that.kernel;
75 76 try {
76 77 target(comm, msg);
77 78 } catch (e) {
78 79 comm.close();
79 80 that.unregister_comm(comm);
80 81 var wrapped_error = new utils.WrappedError("Exception opening new comm", e);
81 82 console.error(wrapped_error);
82 83 return Promise.reject(wrapped_error);
83 84 }
84 85 return comm;
85 86 }, utils.reject('Could not open comm', true));
87 return this.comms[comm_id];
86 88 };
87 89
88 CommManager.prototype.comm_close = function (msg) {
90 CommManager.prototype.comm_close = function(msg) {
89 91 var content = msg.content;
90 var comm = this.comms[content.comm_id];
91 if (comm === undefined) {
92 if (!this.comms[content.comm_id]) {
93 console.error('Comm promise not found for comm id ' + content.comm_id);
92 94 return;
93 95 }
94 this.unregister_comm(comm);
95 try {
96 comm.handle_close(msg);
97 } catch (e) {
98 console.log("Exception closing comm: ", e, e.stack, msg);
99 }
96
97 this.comms[content.comm_id].then(function(comm) {
98 this.unregister_comm(comm);
99 try {
100 comm.handle_close(msg);
101 } catch (e) {
102 console.log("Exception closing comm: ", e, e.stack, msg);
103 }
104 });
100 105 };
101 106
102 CommManager.prototype.comm_msg = function (msg) {
107 CommManager.prototype.comm_msg = function(msg) {
103 108 var content = msg.content;
104 var comm = this.comms[content.comm_id];
105 if (comm === undefined) {
109 if (!this.comms[content.comm_id]) {
110 console.error('Comm promise not found for comm id ' + content.comm_id);
106 111 return;
107 112 }
108 try {
109 comm.handle_msg(msg);
110 } catch (e) {
111 console.log("Exception handling comm msg: ", e, e.stack, msg);
112 }
113
114 this.comms[content.comm_id].then(function(comm) {
115 try {
116 comm.handle_msg(msg);
117 } catch (e) {
118 console.log("Exception handling comm msg: ", e, e.stack, msg);
119 }
120 });
113 121 };
114 122
115 123 //-----------------------------------------------------------------------
116 124 // Comm base class
117 125 //-----------------------------------------------------------------------
118 126
119 127 var Comm = function (target_name, comm_id) {
120 128 this.target_name = target_name;
121 129 this.comm_id = comm_id || utils.uuid();
122 130 this._msg_callback = this._close_callback = null;
123 131
124 132 var that = this;
125 133 this.msg_promise = new Promise(function(resolve, reject) {
126 134 that.resolve_msg_promise = resolve;
127 135 });
128 136 };
129 137
130 138 // methods for sending messages
131 139 Comm.prototype.open = function (data, callbacks, metadata) {
132 140 var content = {
133 141 comm_id : this.comm_id,
134 142 target_name : this.target_name,
135 143 data : data || {},
136 144 };
137 145 return this.kernel.send_shell_message("comm_open", content, callbacks, metadata);
138 146 };
139 147
140 148 Comm.prototype.send = function (data, callbacks, metadata, buffers) {
141 149 var content = {
142 150 comm_id : this.comm_id,
143 151 data : data || {},
144 152 };
145 153 return this.kernel.send_shell_message("comm_msg", content, callbacks, metadata, buffers);
146 154 };
147 155
148 156 Comm.prototype.close = function (data, callbacks, metadata) {
149 157 var content = {
150 158 comm_id : this.comm_id,
151 159 data : data || {},
152 160 };
153 161 return this.kernel.send_shell_message("comm_close", content, callbacks, metadata);
154 162 };
155 163
156 164 // methods for registering callbacks for incoming messages
157 165 Comm.prototype._register_callback = function (key, callback) {
158 166 this['_' + key + '_callback'] = callback;
159 167 };
160 168
161 169 Comm.prototype.on_msg = function (callback) {
162 170 this._register_callback('msg', callback);
163 171 this.resolve_msg_promise();
164 172 };
165 173
166 174 Comm.prototype.on_close = function (callback) {
167 175 this._register_callback('close', callback);
168 176 };
169 177
170 178 // methods for handling incoming messages
171 179
172 180 Comm.prototype._maybe_callback = function (key, msg) {
173 181 var callback = this['_' + key + '_callback'];
174 182 if (callback) {
175 183 try {
176 184 callback(msg);
177 185 } catch (e) {
178 186 console.log("Exception in Comm callback", e, e.stack, msg);
179 187 }
180 188 }
181 189 };
182 190
183 191 Comm.prototype.handle_msg = function (msg) {
184 192 var that = this;
185 this.msg_promise.then(function() {
193 this.msg_promise = this.msg_promise.then(function() {
186 194 that._maybe_callback('msg', msg);
195 return Promise.resolve();
187 196 });
188 197 };
189 198
190 199 Comm.prototype.handle_close = function (msg) {
191 200 this._maybe_callback('close', msg);
192 201 };
193 202
194 203 // For backwards compatability.
195 204 IPython.CommManager = CommManager;
196 205 IPython.Comm = Comm;
197 206
198 207 return {
199 208 'CommManager': CommManager,
200 209 'Comm': Comm
201 210 };
202 211 });
@@ -1,109 +1,107
1 1 // Test widget float class
2 2 casper.notebook_test(function () {
3 3 var float_text = {};
4 4 float_text.query = '.widget-area .widget-subarea .my-second-float-text input';
5 5 float_text.index = this.append_cell(
6 6 'from IPython.html import widgets\n' +
7 7 'from IPython.display import display, clear_output\n' +
8 8 'float_widget = widgets.FloatText()\n' +
9 9 'display(float_widget)\n' +
10 10 'float_widget._dom_classes = ["my-second-float-text"]\n' +
11 11 'print(float_widget.model_id)\n');
12 12 this.execute_cell_then(float_text.index, function(index){
13 13 float_text.model_id = this.get_output_cell(index).text.trim();
14 14 });
15 15
16 16 // Wait for the widget to actually display.
17 17 this.wait_for_element(float_text.index, float_text.query);
18 18
19 19 // Continue with the tests
20 20 this.then(function(){
21 21 this.test.assert(this.cell_element_exists(float_text.index,
22 22 '.widget-area .widget-subarea'),
23 23 'Widget subarea exists.');
24 24
25 25 this.test.assert(this.cell_element_exists(float_text.index, float_text.query),
26 26 'Widget float textbox exists.');
27 27
28 28 this.cell_element_function(float_text.index, float_text.query, 'val', ['']);
29 console.log('send keys');
30 29 this.sendKeys(float_text.query, '1.05');
31 console.log('send keys done');
32 30 });
33 31
34 32 this.wait_for_widget(float_text);
35 33
36 34 index = this.append_cell('print(float_widget.value)\n');
37 35 this.execute_cell_then(index, function(index){
38 36 this.test.assertEquals(this.get_output_cell(index).text, '1.05\n',
39 37 'Float textbox value set.');
40 38 this.cell_element_function(float_text.index, float_text.query, 'val', ['']);
41 39 this.sendKeys(float_text.query, '123456789.0');
42 40 });
43 41
44 42 this.wait_for_widget(float_text);
45 43
46 44 index = this.append_cell('print(float_widget.value)\n');
47 45 this.execute_cell_then(index, function(index){
48 46 this.test.assertEquals(this.get_output_cell(index).text, '123456789.0\n',
49 47 'Long float textbox value set (probably triggers throttling).');
50 48 this.cell_element_function(float_text.index, float_text.query, 'val', ['']);
51 49 this.sendKeys(float_text.query, '12hello');
52 50 });
53 51
54 52 this.wait_for_widget(float_text);
55 53
56 54 index = this.append_cell('print(float_widget.value)\n');
57 55 this.execute_cell_then(index, function(index){
58 56 this.test.assertEquals(this.get_output_cell(index).text, '12.0\n',
59 57 'Invald float textbox value caught and filtered.');
60 58 });
61 59
62 60 var float_text_query = '.widget-area .widget-subarea .widget-numeric-text';
63 61 var slider = {};
64 62 slider.query = '.widget-area .widget-subarea .slider';
65 63 slider.index = this.append_cell(
66 64 'floatrange = [widgets.BoundedFloatText(), \n' +
67 65 ' widgets.FloatSlider()]\n' +
68 66 '[display(floatrange[i]) for i in range(2)]\n' +
69 67 'print("Success")\n');
70 68 this.execute_cell_then(slider.index, function(index){
71 69 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
72 70 'Create float range cell executed with correct output.');
73 71 });
74 72
75 73 // Wait for the widgets to actually display.
76 74 this.wait_for_element(slider.index, slider.query);
77 75 this.wait_for_element(slider.index, float_text_query);
78 76
79 77 this.then(function(){
80 78 this.test.assert(this.cell_element_exists(slider.index,
81 79 '.widget-area .widget-subarea'),
82 80 'Widget subarea exists.');
83 81
84 82 this.test.assert(this.cell_element_exists(slider.index, slider.query),
85 83 'Widget slider exists.');
86 84
87 85 this.test.assert(this.cell_element_exists(slider.index, float_text_query),
88 86 'Widget float textbox exists.');
89 87 });
90 88
91 89 index = this.append_cell(
92 90 'for widget in floatrange:\n' +
93 91 ' widget.max = 50.0\n' +
94 92 ' widget.min = -50.0\n' +
95 93 ' widget.value = 25.0\n' +
96 94 'print("Success")\n');
97 95 this.execute_cell_then(index, function(index){
98 96
99 97 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
100 98 'Float range properties cell executed with correct output.');
101 99
102 100 this.test.assert(this.cell_element_exists(slider.index, slider.query),
103 101 'Widget slider exists.');
104 102
105 103 this.test.assert(this.cell_element_function(slider.index, slider.query,
106 104 'slider', ['value']) == 25.0,
107 105 'Slider set to Python value.');
108 106 });
109 107 }); No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now