##// END OF EJS Templates
Merge pull request #6949 from minrk/kernel_created...
Thomas Kluyver -
r18920:0afda776 merge
parent child Browse files
Show More
@@ -1,91 +1,91 b''
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 var KernelSelector = function(selector, notebook) {
11 var KernelSelector = function(selector, notebook) {
12 this.selector = selector;
12 this.selector = selector;
13 this.notebook = notebook;
13 this.notebook = notebook;
14 this.events = notebook.events;
14 this.events = notebook.events;
15 this.current_selection = null;
15 this.current_selection = null;
16 this.kernelspecs = {};
16 this.kernelspecs = {};
17 if (this.selector !== undefined) {
17 if (this.selector !== undefined) {
18 this.element = $(selector);
18 this.element = $(selector);
19 this.request_kernelspecs();
19 this.request_kernelspecs();
20 }
20 }
21 this.bind_events();
21 this.bind_events();
22 // Make the object globally available for user convenience & inspection
22 // Make the object globally available for user convenience & inspection
23 IPython.kernelselector = this;
23 IPython.kernelselector = this;
24 };
24 };
25
25
26 KernelSelector.prototype.request_kernelspecs = function() {
26 KernelSelector.prototype.request_kernelspecs = function() {
27 var url = utils.url_join_encode(this.notebook.base_url, 'api/kernelspecs');
27 var url = utils.url_join_encode(this.notebook.base_url, 'api/kernelspecs');
28 $.ajax(url, {success: $.proxy(this._got_kernelspecs, this)});
28 $.ajax(url, {success: $.proxy(this._got_kernelspecs, this)});
29 };
29 };
30
30
31 KernelSelector.prototype._got_kernelspecs = function(data, status, xhr) {
31 KernelSelector.prototype._got_kernelspecs = function(data, status, xhr) {
32 this.kernelspecs = {};
32 this.kernelspecs = {};
33 var menu = this.element.find("#kernel_selector");
33 var menu = this.element.find("#kernel_selector");
34 var change_kernel_submenu = $("#menu-change-kernel-submenu");
34 var change_kernel_submenu = $("#menu-change-kernel-submenu");
35 for (var i = 0; i < data.length; i++) {
35 for (var i = 0; i < data.length; i++) {
36 var ks = data[i];
36 var ks = data[i];
37 this.kernelspecs[ks.name] = ks;
37 this.kernelspecs[ks.name] = ks;
38 var ksentry = $("<li>").attr("id", "kernel-" +ks.name).append($('<a>')
38 var ksentry = $("<li>").attr("id", "kernel-" +ks.name).append($('<a>')
39 .attr('href', '#')
39 .attr('href', '#')
40 .click($.proxy(this.change_kernel, this, ks.name))
40 .click($.proxy(this.change_kernel, this, ks.name))
41 .text(ks.display_name));
41 .text(ks.display_name));
42 menu.append(ksentry);
42 menu.append(ksentry);
43
43
44 var ks_submenu_entry = $("<li>").attr("id", "kernel-submenu-"+ks.name).append($('<a>')
44 var ks_submenu_entry = $("<li>").attr("id", "kernel-submenu-"+ks.name).append($('<a>')
45 .attr('href', '#')
45 .attr('href', '#')
46 .click($.proxy(this.change_kernel, this, ks.name))
46 .click($.proxy(this.change_kernel, this, ks.name))
47 .text(ks.display_name));
47 .text(ks.display_name));
48 change_kernel_submenu.append(ks_submenu_entry);
48 change_kernel_submenu.append(ks_submenu_entry);
49 }
49 }
50 };
50 };
51
51
52 KernelSelector.prototype.change_kernel = function(kernel_name) {
52 KernelSelector.prototype.change_kernel = function(kernel_name) {
53 if (kernel_name === this.current_selection) {
53 if (kernel_name === this.current_selection) {
54 return;
54 return;
55 }
55 }
56 var ks = this.kernelspecs[kernel_name];
56 var ks = this.kernelspecs[kernel_name];
57 try {
57 try {
58 this.notebook.start_session(kernel_name);
58 this.notebook.start_session(kernel_name);
59 } catch (e) {
59 } catch (e) {
60 if (e.name === 'SessionAlreadyStarting') {
60 if (e.name === 'SessionAlreadyStarting') {
61 console.log("Cannot change kernel while waiting for pending session start.");
61 console.log("Cannot change kernel while waiting for pending session start.");
62 } else {
62 } else {
63 // unhandled error
63 // unhandled error
64 throw e;
64 throw e;
65 }
65 }
66 // only trigger spec_changed if change was successful
66 // only trigger spec_changed if change was successful
67 return;
67 return;
68 }
68 }
69 this.events.trigger('spec_changed.Kernel', ks);
69 this.events.trigger('spec_changed.Kernel', ks);
70 };
70 };
71
71
72 KernelSelector.prototype.bind_events = function() {
72 KernelSelector.prototype.bind_events = function() {
73 var that = this;
73 var that = this;
74 this.events.on('spec_changed.Kernel', function(event, data) {
74 this.events.on('spec_changed.Kernel', function(event, data) {
75 that.current_selection = data.name;
75 that.current_selection = data.name;
76 that.element.find("#current_kernel_spec").find('.kernel_name').text(data.display_name);
76 that.element.find("#current_kernel_spec").find('.kernel_name').text(data.display_name);
77 });
77 });
78
78
79 this.events.on('started.Session', function(events, session) {
79 this.events.on('kernel_created.Session', function(event, data) {
80 if (session.kernel_name !== that.current_selection) {
80 if (data.kernel.name !== that.current_selection) {
81 // If we created a 'python' session, we only know if it's Python
81 // If we created a 'python' session, we only know if it's Python
82 // 3 or 2 on the server's reply, so we fire the event again to
82 // 3 or 2 on the server's reply, so we fire the event again to
83 // set things up.
83 // set things up.
84 var ks = that.kernelspecs[session.kernel_name];
84 var ks = that.kernelspecs[data.kernel.name];
85 that.events.trigger('spec_changed.Kernel', ks);
85 that.events.trigger('spec_changed.Kernel', ks);
86 }
86 }
87 });
87 });
88 };
88 };
89
89
90 return {'KernelSelector': KernelSelector};
90 return {'KernelSelector': KernelSelector};
91 });
91 });
@@ -1,317 +1,319 b''
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 'services/kernels/kernel',
8 'services/kernels/kernel',
9 ], function(IPython, $, utils, kernel) {
9 ], function(IPython, $, utils, kernel) {
10 "use strict";
10 "use strict";
11
11
12 /**
12 /**
13 * Session object for accessing the session REST api. The session
13 * Session object for accessing the session REST api. The session
14 * should be used to start kernels and then shut them down -- for
14 * should be used to start kernels and then shut them down -- for
15 * all other operations, the kernel object should be used.
15 * all other operations, the kernel object should be used.
16 *
16 *
17 * Options should include:
17 * Options should include:
18 * - notebook_path: the path (not including name) to the notebook
18 * - notebook_path: the path (not including name) to the notebook
19 * - kernel_name: the type of kernel (e.g. python3)
19 * - kernel_name: the type of kernel (e.g. python3)
20 * - base_url: the root url of the notebook server
20 * - base_url: the root url of the notebook server
21 * - ws_url: the url to access websockets
21 * - ws_url: the url to access websockets
22 * - notebook: Notebook object
22 * - notebook: Notebook object
23 *
23 *
24 * @class Session
24 * @class Session
25 * @param {Object} options
25 * @param {Object} options
26 */
26 */
27 var Session = function (options) {
27 var Session = function (options) {
28 this.id = null;
28 this.id = null;
29 this.notebook_model = {
29 this.notebook_model = {
30 path: options.notebook_path
30 path: options.notebook_path
31 };
31 };
32 this.kernel_model = {
32 this.kernel_model = {
33 id: null,
33 id: null,
34 name: options.kernel_name
34 name: options.kernel_name
35 };
35 };
36
36
37 this.base_url = options.base_url;
37 this.base_url = options.base_url;
38 this.ws_url = options.ws_url;
38 this.ws_url = options.ws_url;
39 this.session_service_url = utils.url_join_encode(this.base_url, 'api/sessions');
39 this.session_service_url = utils.url_join_encode(this.base_url, 'api/sessions');
40 this.session_url = null;
40 this.session_url = null;
41
41
42 this.notebook = options.notebook;
42 this.notebook = options.notebook;
43 this.kernel = null;
43 this.kernel = null;
44 this.events = options.notebook.events;
44 this.events = options.notebook.events;
45
45
46 this.bind_events();
46 this.bind_events();
47 };
47 };
48
48
49 Session.prototype.bind_events = function () {
49 Session.prototype.bind_events = function () {
50 var that = this;
50 var that = this;
51 var record_status = function (evt, info) {
51 var record_status = function (evt, info) {
52 console.log('Session: ' + evt.type + ' (' + info.session.id + ')');
52 console.log('Session: ' + evt.type + ' (' + info.session.id + ')');
53 };
53 };
54
54
55 this.events.on('kernel_created.Session', record_status);
55 this.events.on('kernel_created.Session', record_status);
56 this.events.on('kernel_dead.Session', record_status);
56 this.events.on('kernel_dead.Session', record_status);
57 this.events.on('kernel_killed.Session', record_status);
57 this.events.on('kernel_killed.Session', record_status);
58
58
59 // if the kernel dies, then also remove the session
59 // if the kernel dies, then also remove the session
60 this.events.on('kernel_dead.Kernel', function () {
60 this.events.on('kernel_dead.Kernel', function () {
61 that.delete();
61 that.delete();
62 });
62 });
63 };
63 };
64
64
65
65
66 // Public REST api functions
66 // Public REST api functions
67
67
68 /**
68 /**
69 * GET /api/sessions
69 * GET /api/sessions
70 *
70 *
71 * Get a list of the current sessions.
71 * Get a list of the current sessions.
72 *
72 *
73 * @function list
73 * @function list
74 * @param {function} [success] - function executed on ajax success
74 * @param {function} [success] - function executed on ajax success
75 * @param {function} [error] - functon executed on ajax error
75 * @param {function} [error] - functon executed on ajax error
76 */
76 */
77 Session.prototype.list = function (success, error) {
77 Session.prototype.list = function (success, error) {
78 $.ajax(this.session_service_url, {
78 $.ajax(this.session_service_url, {
79 processData: false,
79 processData: false,
80 cache: false,
80 cache: false,
81 type: "GET",
81 type: "GET",
82 dataType: "json",
82 dataType: "json",
83 success: success,
83 success: success,
84 error: this._on_error(error)
84 error: this._on_error(error)
85 });
85 });
86 };
86 };
87
87
88 /**
88 /**
89 * POST /api/sessions
89 * POST /api/sessions
90 *
90 *
91 * Start a new session. This function can only executed once.
91 * Start a new session. This function can only executed once.
92 *
92 *
93 * @function start
93 * @function start
94 * @param {function} [success] - function executed on ajax success
94 * @param {function} [success] - function executed on ajax success
95 * @param {function} [error] - functon executed on ajax error
95 * @param {function} [error] - functon executed on ajax error
96 */
96 */
97 Session.prototype.start = function (success, error) {
97 Session.prototype.start = function (success, error) {
98 var that = this;
98 var that = this;
99 var on_success = function (data, status, xhr) {
99 var on_success = function (data, status, xhr) {
100 if (!that.kernel) {
100 if (that.kernel) {
101 that.kernel.name = that.kernel_model.name;
102 } else {
101 var kernel_service_url = utils.url_path_join(that.base_url, "api/kernels");
103 var kernel_service_url = utils.url_path_join(that.base_url, "api/kernels");
102 that.kernel = new kernel.Kernel(kernel_service_url, that.ws_url, that.notebook, that.kernel_model.name);
104 that.kernel = new kernel.Kernel(kernel_service_url, that.ws_url, that.notebook, that.kernel_model.name);
103 }
105 }
104 that.events.trigger('kernel_created.Session', {session: that, kernel: that.kernel});
106 that.events.trigger('kernel_created.Session', {session: that, kernel: that.kernel});
105 that.kernel._kernel_created(data.kernel);
107 that.kernel._kernel_created(data.kernel);
106 if (success) {
108 if (success) {
107 success(data, status, xhr);
109 success(data, status, xhr);
108 }
110 }
109 };
111 };
110 var on_error = function (xhr, status, err) {
112 var on_error = function (xhr, status, err) {
111 that.events.trigger('kernel_dead.Session', {session: that, xhr: xhr, status: status, error: err});
113 that.events.trigger('kernel_dead.Session', {session: that, xhr: xhr, status: status, error: err});
112 if (error) {
114 if (error) {
113 error(xhr, status, err);
115 error(xhr, status, err);
114 }
116 }
115 };
117 };
116
118
117 $.ajax(this.session_service_url, {
119 $.ajax(this.session_service_url, {
118 processData: false,
120 processData: false,
119 cache: false,
121 cache: false,
120 type: "POST",
122 type: "POST",
121 data: JSON.stringify(this._get_model()),
123 data: JSON.stringify(this._get_model()),
122 dataType: "json",
124 dataType: "json",
123 success: this._on_success(on_success),
125 success: this._on_success(on_success),
124 error: this._on_error(on_error)
126 error: this._on_error(on_error)
125 });
127 });
126 };
128 };
127
129
128 /**
130 /**
129 * GET /api/sessions/[:session_id]
131 * GET /api/sessions/[:session_id]
130 *
132 *
131 * Get information about a session.
133 * Get information about a session.
132 *
134 *
133 * @function get_info
135 * @function get_info
134 * @param {function} [success] - function executed on ajax success
136 * @param {function} [success] - function executed on ajax success
135 * @param {function} [error] - functon executed on ajax error
137 * @param {function} [error] - functon executed on ajax error
136 */
138 */
137 Session.prototype.get_info = function (success, error) {
139 Session.prototype.get_info = function (success, error) {
138 $.ajax(this.session_url, {
140 $.ajax(this.session_url, {
139 processData: false,
141 processData: false,
140 cache: false,
142 cache: false,
141 type: "GET",
143 type: "GET",
142 dataType: "json",
144 dataType: "json",
143 success: this._on_success(success),
145 success: this._on_success(success),
144 error: this._on_error(error)
146 error: this._on_error(error)
145 });
147 });
146 };
148 };
147
149
148 /**
150 /**
149 * PATCH /api/sessions/[:session_id]
151 * PATCH /api/sessions/[:session_id]
150 *
152 *
151 * Rename or move a notebook. If the given name or path are
153 * Rename or move a notebook. If the given name or path are
152 * undefined, then they will not be changed.
154 * undefined, then they will not be changed.
153 *
155 *
154 * @function rename_notebook
156 * @function rename_notebook
155 * @param {string} [path] - new notebook path
157 * @param {string} [path] - new notebook path
156 * @param {function} [success] - function executed on ajax success
158 * @param {function} [success] - function executed on ajax success
157 * @param {function} [error] - functon executed on ajax error
159 * @param {function} [error] - functon executed on ajax error
158 */
160 */
159 Session.prototype.rename_notebook = function (path, success, error) {
161 Session.prototype.rename_notebook = function (path, success, error) {
160 if (path !== undefined) {
162 if (path !== undefined) {
161 this.notebook_model.path = path;
163 this.notebook_model.path = path;
162 }
164 }
163
165
164 $.ajax(this.session_url, {
166 $.ajax(this.session_url, {
165 processData: false,
167 processData: false,
166 cache: false,
168 cache: false,
167 type: "PATCH",
169 type: "PATCH",
168 data: JSON.stringify(this._get_model()),
170 data: JSON.stringify(this._get_model()),
169 dataType: "json",
171 dataType: "json",
170 success: this._on_success(success),
172 success: this._on_success(success),
171 error: this._on_error(error)
173 error: this._on_error(error)
172 });
174 });
173 };
175 };
174
176
175 /**
177 /**
176 * DELETE /api/sessions/[:session_id]
178 * DELETE /api/sessions/[:session_id]
177 *
179 *
178 * Kill the kernel and shutdown the session.
180 * Kill the kernel and shutdown the session.
179 *
181 *
180 * @function delete
182 * @function delete
181 * @param {function} [success] - function executed on ajax success
183 * @param {function} [success] - function executed on ajax success
182 * @param {function} [error] - functon executed on ajax error
184 * @param {function} [error] - functon executed on ajax error
183 */
185 */
184 Session.prototype.delete = function (success, error) {
186 Session.prototype.delete = function (success, error) {
185 if (this.kernel) {
187 if (this.kernel) {
186 this.events.trigger('kernel_killed.Session', {session: this, kernel: this.kernel});
188 this.events.trigger('kernel_killed.Session', {session: this, kernel: this.kernel});
187 this.kernel._kernel_dead();
189 this.kernel._kernel_dead();
188 }
190 }
189
191
190 $.ajax(this.session_url, {
192 $.ajax(this.session_url, {
191 processData: false,
193 processData: false,
192 cache: false,
194 cache: false,
193 type: "DELETE",
195 type: "DELETE",
194 dataType: "json",
196 dataType: "json",
195 success: this._on_success(success),
197 success: this._on_success(success),
196 error: this._on_error(error)
198 error: this._on_error(error)
197 });
199 });
198 };
200 };
199
201
200 /**
202 /**
201 * Restart the session by deleting it and the starting it
203 * Restart the session by deleting it and the starting it
202 * fresh. If options are given, they can include any of the
204 * fresh. If options are given, they can include any of the
203 * following:
205 * following:
204 *
206 *
205 * - notebook_path - the path to the notebook
207 * - notebook_path - the path to the notebook
206 * - kernel_name - the name (type) of the kernel
208 * - kernel_name - the name (type) of the kernel
207 *
209 *
208 * @function restart
210 * @function restart
209 * @param {Object} [options] - options for the new kernel
211 * @param {Object} [options] - options for the new kernel
210 * @param {function} [success] - function executed on ajax success
212 * @param {function} [success] - function executed on ajax success
211 * @param {function} [error] - functon executed on ajax error
213 * @param {function} [error] - functon executed on ajax error
212 */
214 */
213 Session.prototype.restart = function (options, success, error) {
215 Session.prototype.restart = function (options, success, error) {
214 var that = this;
216 var that = this;
215 var start = function () {
217 var start = function () {
216 if (options && options.notebook_path) {
218 if (options && options.notebook_path) {
217 that.notebook_model.path = options.notebook_path;
219 that.notebook_model.path = options.notebook_path;
218 }
220 }
219 if (options && options.kernel_name) {
221 if (options && options.kernel_name) {
220 that.kernel_model.name = options.kernel_name;
222 that.kernel_model.name = options.kernel_name;
221 }
223 }
222 that.kernel_model.id = null;
224 that.kernel_model.id = null;
223 that.start(success, error);
225 that.start(success, error);
224 };
226 };
225 this.delete(start, start);
227 this.delete(start, start);
226 };
228 };
227
229
228 // Helper functions
230 // Helper functions
229
231
230 /**
232 /**
231 * Get the data model for the session, which includes the notebook path
233 * Get the data model for the session, which includes the notebook path
232 * and kernel (name and id).
234 * and kernel (name and id).
233 *
235 *
234 * @function _get_model
236 * @function _get_model
235 * @returns {Object} - the data model
237 * @returns {Object} - the data model
236 */
238 */
237 Session.prototype._get_model = function () {
239 Session.prototype._get_model = function () {
238 return {
240 return {
239 notebook: this.notebook_model,
241 notebook: this.notebook_model,
240 kernel: this.kernel_model
242 kernel: this.kernel_model
241 };
243 };
242 };
244 };
243
245
244 /**
246 /**
245 * Update the data model from the given JSON object, which should
247 * Update the data model from the given JSON object, which should
246 * have attributes of `id`, `notebook`, and/or `kernel`. If
248 * have attributes of `id`, `notebook`, and/or `kernel`. If
247 * provided, the notebook data must include name and path, and the
249 * provided, the notebook data must include name and path, and the
248 * kernel data must include name and id.
250 * kernel data must include name and id.
249 *
251 *
250 * @function _update_model
252 * @function _update_model
251 * @param {Object} data - updated data model
253 * @param {Object} data - updated data model
252 */
254 */
253 Session.prototype._update_model = function (data) {
255 Session.prototype._update_model = function (data) {
254 if (data && data.id) {
256 if (data && data.id) {
255 this.id = data.id;
257 this.id = data.id;
256 this.session_url = utils.url_join_encode(this.session_service_url, this.id);
258 this.session_url = utils.url_join_encode(this.session_service_url, this.id);
257 }
259 }
258 if (data && data.notebook) {
260 if (data && data.notebook) {
259 this.notebook_model.path = data.notebook.path;
261 this.notebook_model.path = data.notebook.path;
260 }
262 }
261 if (data && data.kernel) {
263 if (data && data.kernel) {
262 this.kernel_model.name = data.kernel.name;
264 this.kernel_model.name = data.kernel.name;
263 this.kernel_model.id = data.kernel.id;
265 this.kernel_model.id = data.kernel.id;
264 }
266 }
265 };
267 };
266
268
267 /**
269 /**
268 * Handle a successful AJAX request by updating the session data
270 * Handle a successful AJAX request by updating the session data
269 * model with the response, and then optionally calling a provided
271 * model with the response, and then optionally calling a provided
270 * callback.
272 * callback.
271 *
273 *
272 * @function _on_success
274 * @function _on_success
273 * @param {function} success - callback
275 * @param {function} success - callback
274 */
276 */
275 Session.prototype._on_success = function (success) {
277 Session.prototype._on_success = function (success) {
276 var that = this;
278 var that = this;
277 return function (data, status, xhr) {
279 return function (data, status, xhr) {
278 that._update_model(data);
280 that._update_model(data);
279 if (success) {
281 if (success) {
280 success(data, status, xhr);
282 success(data, status, xhr);
281 }
283 }
282 };
284 };
283 };
285 };
284
286
285 /**
287 /**
286 * Handle a failed AJAX request by logging the error message, and
288 * Handle a failed AJAX request by logging the error message, and
287 * then optionally calling a provided callback.
289 * then optionally calling a provided callback.
288 *
290 *
289 * @function _on_error
291 * @function _on_error
290 * @param {function} error - callback
292 * @param {function} error - callback
291 */
293 */
292 Session.prototype._on_error = function (error) {
294 Session.prototype._on_error = function (error) {
293 return function (xhr, status, err) {
295 return function (xhr, status, err) {
294 utils.log_ajax_error(xhr, status, err);
296 utils.log_ajax_error(xhr, status, err);
295 if (error) {
297 if (error) {
296 error(xhr, status, err);
298 error(xhr, status, err);
297 }
299 }
298 };
300 };
299 };
301 };
300
302
301 /**
303 /**
302 * Error type indicating that the session is already starting.
304 * Error type indicating that the session is already starting.
303 */
305 */
304 var SessionAlreadyStarting = function (message) {
306 var SessionAlreadyStarting = function (message) {
305 this.name = "SessionAlreadyStarting";
307 this.name = "SessionAlreadyStarting";
306 this.message = (message || "");
308 this.message = (message || "");
307 };
309 };
308 SessionAlreadyStarting.prototype = Error.prototype;
310 SessionAlreadyStarting.prototype = Error.prototype;
309
311
310 // For backwards compatability.
312 // For backwards compatability.
311 IPython.Session = Session;
313 IPython.Session = Session;
312
314
313 return {
315 return {
314 Session: Session,
316 Session: Session,
315 SessionAlreadyStarting: SessionAlreadyStarting
317 SessionAlreadyStarting: SessionAlreadyStarting
316 };
318 };
317 });
319 });
General Comments 0
You need to be logged in to leave comments. Login now