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