UNPKG

14.5 kBJavaScriptView Raw
1"use strict";
2// Copyright (c) Jupyter Development Team.
3// Distributed under the terms of the Modified BSD License.
4var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5 if (k2 === undefined) k2 = k;
6 Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
7}) : (function(o, m, k, k2) {
8 if (k2 === undefined) k2 = k;
9 o[k2] = m[k];
10}));
11var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
12 Object.defineProperty(o, "default", { enumerable: true, value: v });
13}) : function(o, v) {
14 o["default"] = v;
15});
16var __importStar = (this && this.__importStar) || function (mod) {
17 if (mod && mod.__esModule) return mod;
18 var result = {};
19 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
20 __setModuleDefault(result, mod);
21 return result;
22};
23Object.defineProperty(exports, "__esModule", { value: true });
24exports.KernelShellFutureHandler = exports.KernelControlFutureHandler = exports.KernelFutureHandler = void 0;
25const coreutils_1 = require("@lumino/coreutils");
26const disposable_1 = require("@lumino/disposable");
27const KernelMessage = __importStar(require("./messages"));
28/**
29 * Implementation of a kernel future.
30 *
31 * If a reply is expected, the Future is considered done when both a `reply`
32 * message and an `idle` iopub status message have been received. Otherwise, it
33 * is considered done when the `idle` status is received.
34 *
35 */
36class KernelFutureHandler extends disposable_1.DisposableDelegate {
37 /**
38 * Construct a new KernelFutureHandler.
39 */
40 constructor(cb, msg, expectReply, disposeOnDone, kernel) {
41 super(cb);
42 this._status = 0;
43 this._stdin = Private.noOp;
44 this._iopub = Private.noOp;
45 this._reply = Private.noOp;
46 this._done = new coreutils_1.PromiseDelegate();
47 this._hooks = new Private.HookList();
48 this._disposeOnDone = true;
49 this._msg = msg;
50 if (!expectReply) {
51 this._setFlag(Private.KernelFutureFlag.GotReply);
52 }
53 this._disposeOnDone = disposeOnDone;
54 this._kernel = kernel;
55 }
56 /**
57 * Get the original outgoing message.
58 */
59 get msg() {
60 return this._msg;
61 }
62 /**
63 * A promise that resolves when the future is done.
64 */
65 get done() {
66 return this._done.promise;
67 }
68 /**
69 * Get the reply handler.
70 */
71 get onReply() {
72 return this._reply;
73 }
74 /**
75 * Set the reply handler.
76 */
77 set onReply(cb) {
78 this._reply = cb;
79 }
80 /**
81 * Get the iopub handler.
82 */
83 get onIOPub() {
84 return this._iopub;
85 }
86 /**
87 * Set the iopub handler.
88 */
89 set onIOPub(cb) {
90 this._iopub = cb;
91 }
92 /**
93 * Get the stdin handler.
94 */
95 get onStdin() {
96 return this._stdin;
97 }
98 /**
99 * Set the stdin handler.
100 */
101 set onStdin(cb) {
102 this._stdin = cb;
103 }
104 /**
105 * Register hook for IOPub messages.
106 *
107 * @param hook - The callback invoked for an IOPub message.
108 *
109 * #### Notes
110 * The IOPub hook system allows you to preempt the handlers for IOPub
111 * messages handled by the future.
112 *
113 * The most recently registered hook is run first. A hook can return a
114 * boolean or a promise to a boolean, in which case all kernel message
115 * processing pauses until the promise is fulfilled. If a hook return value
116 * resolves to false, any later hooks will not run and the function will
117 * return a promise resolving to false. If a hook throws an error, the error
118 * is logged to the console and the next hook is run. If a hook is
119 * registered during the hook processing, it will not run until the next
120 * message. If a hook is removed during the hook processing, it will be
121 * deactivated immediately.
122 */
123 registerMessageHook(hook) {
124 if (this.isDisposed) {
125 throw new Error('Kernel future is disposed');
126 }
127 this._hooks.add(hook);
128 }
129 /**
130 * Remove a hook for IOPub messages.
131 *
132 * @param hook - The hook to remove.
133 *
134 * #### Notes
135 * If a hook is removed during the hook processing, it will be deactivated immediately.
136 */
137 removeMessageHook(hook) {
138 if (this.isDisposed) {
139 return;
140 }
141 this._hooks.remove(hook);
142 }
143 /**
144 * Send an `input_reply` message.
145 */
146 sendInputReply(content, parent_header) {
147 this._kernel.sendInputReply(content, parent_header);
148 }
149 /**
150 * Dispose and unregister the future.
151 */
152 dispose() {
153 this._stdin = Private.noOp;
154 this._iopub = Private.noOp;
155 this._reply = Private.noOp;
156 this._hooks = null;
157 if (!this._testFlag(Private.KernelFutureFlag.IsDone)) {
158 // TODO: Uncomment the following logging code, and check for any tests that trigger it.
159 // let status = [];
160 // if (!this._testFlag(Private.KernelFutureFlag.GotIdle)) {
161 // status.push('idle');
162 // }
163 // if (!this._testFlag(Private.KernelFutureFlag.GotReply)) {
164 // status.push('reply');
165 // }
166 // console.warn(
167 // `*************** DISPOSED BEFORE DONE: K${this._kernel.id.slice(
168 // 0,
169 // 6
170 // )} M${this._msg.header.msg_id.slice(0, 6)} missing ${status.join(' ')}`
171 // );
172 // Reject the `done` promise, but catch its error here in case no one else
173 // is waiting for the promise to resolve. This prevents the error from
174 // being displayed in the console, but does not prevent it from being
175 // caught by a client who is waiting for it.
176 this._done.promise.catch(() => {
177 /* no-op */
178 });
179 this._done.reject(new Error(`Canceled future for ${this.msg.header.msg_type} message before replies were done`));
180 }
181 super.dispose();
182 }
183 /**
184 * Handle an incoming kernel message.
185 */
186 async handleMsg(msg) {
187 switch (msg.channel) {
188 case 'control':
189 case 'shell':
190 if (msg.channel === this.msg.channel &&
191 msg.parent_header.msg_id === this.msg.header.msg_id) {
192 await this._handleReply(msg);
193 }
194 break;
195 case 'stdin':
196 await this._handleStdin(msg);
197 break;
198 case 'iopub':
199 await this._handleIOPub(msg);
200 break;
201 default:
202 break;
203 }
204 }
205 async _handleReply(msg) {
206 const reply = this._reply;
207 if (reply) {
208 // tslint:disable-next-line:await-promise
209 await reply(msg);
210 }
211 this._replyMsg = msg;
212 this._setFlag(Private.KernelFutureFlag.GotReply);
213 if (this._testFlag(Private.KernelFutureFlag.GotIdle)) {
214 this._handleDone();
215 }
216 }
217 async _handleStdin(msg) {
218 this._kernel.hasPendingInput = true;
219 const stdin = this._stdin;
220 if (stdin) {
221 // tslint:disable-next-line:await-promise
222 await stdin(msg);
223 }
224 }
225 async _handleIOPub(msg) {
226 const process = await this._hooks.process(msg);
227 const iopub = this._iopub;
228 if (process && iopub) {
229 // tslint:disable-next-line:await-promise
230 await iopub(msg);
231 }
232 if (KernelMessage.isStatusMsg(msg) &&
233 msg.content.execution_state === 'idle') {
234 this._setFlag(Private.KernelFutureFlag.GotIdle);
235 if (this._testFlag(Private.KernelFutureFlag.GotReply)) {
236 this._handleDone();
237 }
238 }
239 }
240 _handleDone() {
241 if (this._testFlag(Private.KernelFutureFlag.IsDone)) {
242 return;
243 }
244 this._setFlag(Private.KernelFutureFlag.IsDone);
245 this._done.resolve(this._replyMsg);
246 if (this._disposeOnDone) {
247 this.dispose();
248 }
249 }
250 /**
251 * Test whether the given future flag is set.
252 */
253 _testFlag(flag) {
254 // tslint:disable-next-line
255 return (this._status & flag) !== 0;
256 }
257 /**
258 * Set the given future flag.
259 */
260 _setFlag(flag) {
261 // tslint:disable-next-line
262 this._status |= flag;
263 }
264}
265exports.KernelFutureHandler = KernelFutureHandler;
266class KernelControlFutureHandler extends KernelFutureHandler {
267}
268exports.KernelControlFutureHandler = KernelControlFutureHandler;
269class KernelShellFutureHandler extends KernelFutureHandler {
270}
271exports.KernelShellFutureHandler = KernelShellFutureHandler;
272var Private;
273(function (Private) {
274 /**
275 * A no-op function.
276 */
277 Private.noOp = () => {
278 /* no-op */
279 };
280 /**
281 * Defer a computation.
282 *
283 * #### NOTES
284 * We can't just use requestAnimationFrame since it is not available in node.
285 * This implementation is from Phosphor:
286 * https://github.com/phosphorjs/phosphor/blob/e88e4321289bb1198f3098e7bda40736501f2ed8/tests/test-messaging/src/index.spec.ts#L63
287 */
288 const defer = (() => {
289 const ok = typeof requestAnimationFrame === 'function';
290 return ok ? requestAnimationFrame : setImmediate;
291 })();
292 class HookList {
293 constructor() {
294 this._hooks = [];
295 }
296 /**
297 * Register a hook.
298 *
299 * @param hook - The callback to register.
300 */
301 add(hook) {
302 this.remove(hook);
303 this._hooks.push(hook);
304 }
305 /**
306 * Remove a hook, if it exists in the hook list.
307 *
308 * @param hook - The callback to remove.
309 */
310 remove(hook) {
311 const index = this._hooks.indexOf(hook);
312 if (index >= 0) {
313 this._hooks[index] = null;
314 this._scheduleCompact();
315 }
316 }
317 /**
318 * Process a message through the hooks.
319 *
320 * @returns a promise resolving to false if any hook resolved as false,
321 * otherwise true
322 *
323 * #### Notes
324 * The most recently registered hook is run first. A hook can return a
325 * boolean or a promise to a boolean, in which case processing pauses until
326 * the promise is fulfilled. If a hook return value resolves to false, any
327 * later hooks will not run and the function will return a promise resolving
328 * to false. If a hook throws an error, the error is logged to the console
329 * and the next hook is run. If a hook is registered during the hook
330 * processing, it will not run until the next message. If a hook is removed
331 * during the hook processing, it will be deactivated immediately.
332 */
333 async process(msg) {
334 // Wait until we can start a new process run.
335 await this._processing;
336 // Start the next process run.
337 const processing = new coreutils_1.PromiseDelegate();
338 this._processing = processing.promise;
339 let continueHandling;
340 // Call the end hook (most recently-added) first. Starting at the end also
341 // guarantees that hooks added during the processing will not be run in
342 // this process run.
343 for (let i = this._hooks.length - 1; i >= 0; i--) {
344 const hook = this._hooks[i];
345 // If the hook has been removed, continue to the next one.
346 if (hook === null) {
347 continue;
348 }
349 // Execute the hook and log any errors.
350 try {
351 // tslint:disable-next-line:await-promise
352 continueHandling = await hook(msg);
353 }
354 catch (err) {
355 continueHandling = true;
356 console.error(err);
357 }
358 // If the hook resolved to false, stop processing and return.
359 if (continueHandling === false) {
360 processing.resolve(undefined);
361 return false;
362 }
363 }
364 // All hooks returned true (or errored out), so return true.
365 processing.resolve(undefined);
366 return true;
367 }
368 /**
369 * Schedule a cleanup of the list, removing any hooks that have been nulled out.
370 */
371 _scheduleCompact() {
372 if (!this._compactScheduled) {
373 this._compactScheduled = true;
374 // Schedule a compaction in between processing runs. We do the
375 // scheduling in an animation frame to rate-limit our compactions. If we
376 // need to compact more frequently, we can change this to directly
377 // schedule the compaction.
378 defer(() => {
379 this._processing = this._processing.then(() => {
380 this._compactScheduled = false;
381 this._compact();
382 });
383 });
384 }
385 }
386 /**
387 * Compact the list, removing any nulls.
388 */
389 _compact() {
390 let numNulls = 0;
391 for (let i = 0, len = this._hooks.length; i < len; i++) {
392 const hook = this._hooks[i];
393 if (this._hooks[i] === null) {
394 numNulls++;
395 }
396 else {
397 this._hooks[i - numNulls] = hook;
398 }
399 }
400 this._hooks.length -= numNulls;
401 }
402 }
403 Private.HookList = HookList;
404 /**
405 * Bit flags for the kernel future state.
406 */
407 let KernelFutureFlag;
408 (function (KernelFutureFlag) {
409 KernelFutureFlag[KernelFutureFlag["GotReply"] = 1] = "GotReply";
410 KernelFutureFlag[KernelFutureFlag["GotIdle"] = 2] = "GotIdle";
411 KernelFutureFlag[KernelFutureFlag["IsDone"] = 4] = "IsDone";
412 KernelFutureFlag[KernelFutureFlag["DisposeOnDone"] = 8] = "DisposeOnDone";
413 })(KernelFutureFlag = Private.KernelFutureFlag || (Private.KernelFutureFlag = {}));
414})(Private || (Private = {}));
415//# sourceMappingURL=future.js.map
\No newline at end of file