Source: handlers_v4.js

/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2015, Nicolas Riesco and others as credited in the AUTHORS file
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

module.exports = {
    execute_request: execute_request,
    complete_request: complete_request,
    history_request: history_request,
    kernel_info_request: kernel_info_request,
    object_info_request: object_info_request,
    shutdown_request: shutdown_request,
};

var DEBUG = global.DEBUG;

var log;
if (DEBUG) {
    var console = require("console");
    log = function log() {
        process.stderr.write("KERNEL: ");
        console.error.apply(this, arguments);
    };
} else {
    try {
        log = require("debug")("KERNEL:");
    } catch (err) {
        log = function noop() {};
    }
}

/**
 * Send `status_busy` message
 *
 * @param {module:jmp~Message} request Request message
 * @this Kernel
 */
function status_busy(request) {
    request.respond(this.iopubSocket, "status", {
        execution_state: "busy"
    });
}

/**
 * Send for `status_idle` message
 *
 * @param {module:jmp~Message} request Request message
 * @this Kernel
 */
function status_idle(request) {
    request.respond(this.iopubSocket, "status", {
        execution_state: "idle"
    });
}

/**
 * Handler for `kernel_info_request` messages
 *
 * @param {module:jmp~Message} request Request message
 * @this Kernel
 */
function kernel_info_request(request) {
    status_busy.call(this, request);

    request.respond(
        this.shellSocket,
        "kernel_info_reply",
        this.kernelInfoReply,
        {},
        this.protocolVersion
    );

    status_idle.call(this, request);
}

/**
 * Handler for `execute_request` messages
 *
 * @param {module:jmp~Message} request Request message
 * @this Kernel
 */
function execute_request(request) {
    this.session.execute(request.content.code, {
        onSuccess: onSuccess.bind(this),
        onError: onError.bind(this),
        beforeRun: beforeRun.bind(this),
        afterRun: afterRun.bind(this),
        onStdout: onStdout.bind(this),
        onStderr: onStderr.bind(this),
        onDisplay: onDisplay.bind(this),
        onRequest: onRequest.bind(this),
    });

    function beforeRun() {
        status_busy.call(this, request);

        this.executionCount++;

        request.respond(
            this.iopubSocket,
            "pyin", {
                execution_count: this.executionCount,
                code: request.content.code,
            }
        );
    }

    function afterRun() {
        status_idle.call(this, request);
    }

    function onSuccess(result) {
        request.respond(
            this.shellSocket,
            "execute_reply", {
                status: "ok",
                execution_count: this.executionCount,
                payload: [], // TODO(NR)
                user_variables: {}, // TODO(NR)
                user_expressions: {}, // TODO(NR)
            }
        );

        if (!result.mime) {
            return;
        }

        if (this.hideExecutionResult) {
            return;
        }

        if (this.hideUndefined &&
            result.mime["text/plain"] === "undefined") {
            return;
        }

        request.respond(
            this.iopubSocket,
            "pyout", {
                execution_count: this.executionCount,
                data: result.mime,
                metadata: {},
            }
        );
    }

    function onError(result) {
        request.respond(
            this.shellSocket,
            "execute_reply", {
                status: "error",
                execution_count: this.executionCount,
                ename: result.error.ename,
                evalue: result.error.evalue,
                traceback: result.error.traceback,
            }
        );

        request.respond(
            this.iopubSocket,
            "pyerr", {
                execution_count: this.executionCount,
                ename: result.error.ename,
                evalue: result.error.evalue,
                traceback: result.error.traceback,
            }
        );
    }

    function onStdout(data) {
        request.respond(
            this.iopubSocket,
            "stream", {
                name: "stdout",
                data: data,
            }
        );
    }

    function onStderr(data) {
        request.respond(
            this.iopubSocket,
            "stream", {
                name: "stderr",
                data: data,
            }
        );
    }

    function onDisplay(update) {
        request.respond(this.iopubSocket,
            "display_data", {
                data: update.mime,
                metadata: {},
            }
        );
    }

    function onRequest(message, onReply) {
        if (!message) {
            log("REQUEST: Empty request");
            return;
        }

        if (message.clear) {
            var clearOutput = request.respond(
                this.iopubSocket, "clear_output", message.clear
            );

            log("REQUEST: CLEAR_OUTPUT:", clearOutput);
            return;
        }

        if (typeof onReply !== "function") {
            log("REQUEST: Missing onReply callback");
            return;
        }

        if (message && message.input) {
            if (!request.content.allow_stdin) {
                log("REQUEST: STDIN: Frontend does not support stdin requests");
                onReply(new Error("Frontend does not support stdin requests"));
                return;
            }

            var response = request.respond(
                this.stdinSocket, "input_request", message.input
            );

            log("REQUEST: STDIN:", response);
            this.onReplies[response.header.msg_id] = onReply;
            this.lastActiveOnReply = onReply;
        }
    }
}

/**
 * Handler for `complete_request` messages
 *
 * @param {module:jmp~Message} request Request message
 * @this Kernel
 */
function complete_request(request) {
    this.session.complete(request.content.line, request.content.cursor_pos, {
        beforeRun: beforeRun.bind(this),
        onSuccess: onSuccess.bind(this),
        onError: onError.bind(this),
        afterRun: afterRun.bind(this),
    });
    return;

    function beforeRun() {
        status_busy.call(this, request);
    }

    function afterRun() {
        status_idle.call(this, request);
    }

    function onSuccess(result) {
        var content = {
            "matches": result.completion.list,
            "matched_text": result.completion.matchedText,
            "status": "ok",
        };
        request.respond(this.shellSocket, "complete_reply", content);
    }

    function onError(result) {
        var content = {
            "matches": [],
            "matched_text": request.content.text,
            "status": "ok",
        };
        request.respond(this.shellSocket, "complete_reply", content);
    }
}

/**
 * Handler for `history_request` messages
 *
 * @param {module:jmp~Message} request Request message
 * @this Kernel
 */
function history_request(request) {
    status_busy.call(this, request);

    request.respond(
        this.shellSocket,
        "history_reply", {
            "history": [] // TODO
        }, {},
        this.protocolVersion
    );

    status_idle.call(this, request);
}

/**
 * Handler for `object_info_request` messages
 *
 * @param {module:jmp~Message} request Request message
 * @this Kernel
 */
function object_info_request(request) {
    this.session.inspect(request.content.oname, request.content.oname.length, {
        beforeRun: beforeRun.bind(this),
        onSuccess: onSuccess.bind(this),
        onError: onError.bind(this),
        afterRun: afterRun.bind(this),
    });
    return;

    function beforeRun() {
        status_busy.call(this, request);
    }

    function afterRun() {
        status_idle.call(this, request);
    }

    function onSuccess(result) {
        var content = {
            oname: request.content.oname,
            found: true,
            ismagic: false,
            isalias: false,
        };

        if (result.inspection) {
            content.string_name = result.inspection.string;

            if (result.inspection.list) {
                if (result.inspection.list.length >= 1) {
                    content.type_name = result.inspection.list[0];
                } else {
                    content.type_name = result.inspection.type;
                }

                if (result.inspection.list.length >= 2) {
                    content.base_name = result.inspection.list[1];
                }
            }

            if (result.inspection.length) {
                content.length = result.inspection.length;
            }

            content.docstring = (
                result.inspection.type + ": " + result.inspection.string
            );
        }

        if (result.doc) {
            content.namespace = "builtin";
            content.docstring = result.doc.description;
            if (result.doc.usage) {
                content.definition = result.doc.usage;
            }
        }

        request.respond(this.shellSocket, "object_info_reply", content);
    }

    function onError(result) {
        var content = {
            oname: request.content.oname,
            found: false,
        };
        request.respond(this.shellSocket, "object_info_reply", content);
    }
}

/**
 * Handler for `shutdown_request` messages
 *
 * @param {module:jmp~Message} request Request message
 * @this Kernel
 */
function shutdown_request(request) {
    status_busy.call(this, request);

    function respond(code, signal) {
        request.respond(
            this.controlSocket, "shutdown_reply", request.content
        );
    }

    if (request.content.restart) {
        this.restart(respond.bind(this));
    } else {
        this.destroy(respond.bind(this));
    }

    status_idle.call(this, request);
}