File size: 5,885 Bytes
fdaf774
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Restrict output in a codecell to a maximum length

define([
    'base/js/namespace',
    'notebook/js/outputarea',
    'notebook/js/codecell',
], function(
    Jupyter,
    oa,
    cc
) {
    "use strict";

    // define default values for config parameters
    var params = {
        // maximum number of characters the output area is allowed to print
        limit_output : 10000,
        limit_stream : true,
        limit_execute_result : true,
        limit_display_data : false,
        // message to print when output is limited
        limit_output_message : '<b>limit_output extension: Maximum message size of {limit_output_length} exceeded with {output_length} characters</b>'
    };

    // to be called once config is loaded, this updates default config vals
    // with the ones specified by the server's config file
    var update_params = function() {
        var config = Jupyter.notebook.config;
        for (var key in params) {
            if (config.data.hasOwnProperty(key) ){
                params[key] = config.data[key];
            }
        }
    };

    function is_finite_number (n) {
        n = parseFloat(n);
        return !isNaN(n) && isFinite(n);
    }

    var initialize = function () {
        update_params();
        // sometimes limit_output metadata val can get stored as a string
        params.limit_output = parseFloat(params.limit_output);
        var old_handle_output = oa.OutputArea.prototype.handle_output;
        oa.OutputArea.prototype.handle_output = function (msg) {
            var handled_msg_types = ['stream', 'execute_result', 'display_data'];
            if (handled_msg_types.indexOf(msg.header.msg_type) < 0) {
                return old_handle_output.apply(this, arguments);
            }
            else {
                // get MAX_CHARACTERS from cell metadata if present, otherwise param
                //msg.header.msg_type
                var MAX_CHARACTERS = params.limit_output;
                var cell_metadata = this.element.closest('.cell').data('cell').metadata;
                if (is_finite_number(cell_metadata.limit_output)) {
                    MAX_CHARACTERS = parseFloat(cell_metadata.limit_output);
                }

                // read the length of already-appended outputs from our data
                var count = this.element.data('limit_output_count') || 0;
                // update count with the length of this message
                var old_count = count;
                if (msg.header.msg_type === "stream" && params.limit_stream) {
                    count += String(msg.content.text).length;
                }
                else {
                    if ((msg.header.msg_type === "execute_result" && params.limit_execute_result) ||
                        (msg.header.msg_type === "display_data" && params.limit_display_data)) {
                        count += Math.max(
                            (msg.content.data['text/plain'] === undefined) ? 0 : String(msg.content.data['text/plain']).length,
                            (msg.content.data['text/html'] === undefined) ? 0 : String(msg.content.data['text/html']).length
                        );
                    }

                }
                // save updated count
                this.element.data('limit_output_count', count);

                if (count <= MAX_CHARACTERS) {
                    return old_handle_output.apply(this, arguments);
                }
                // if here, we'd exceed MAX_CHARACTERS with addition of this message.
                if (old_count <= MAX_CHARACTERS) {
                    // Apply truncated portion of this message
                    var to_add = MAX_CHARACTERS - old_count;
                    if (msg.header.msg_type === "stream") {
                        msg.content.text = msg.content.text.substr(0, to_add);
                    }
                    else {
                        if (msg.content.data['text/plain'] !== undefined) {
                            msg.content.data['text/plain'] = msg.content.data['text/plain'].substr(0, to_add);
                        }
                        if (msg.content.data['text/html'] !== undefined) {
                            msg.content.data['text/html'] = msg.content.data['text/html'].substr(0, to_add);
                        }
                    }
                    old_handle_output.apply(this, arguments);

                    // display limit notification messages
                    console.log(
                        "limit_output: Maximum message size of", MAX_CHARACTERS,
                        "exceeded with",  count, "characters. Further output muted."
                    );
                    // allow simple substitutions for output length for quick debugging
                    var limitmsg = params.limit_output_message.replace("{message_type}", msg.header.msg_type)
                                                              .replace("{limit_output_length}", MAX_CHARACTERS)
                                                              .replace("{output_length}", count);
                    this.append_output({
                        "output_type": "display_data",
                        "metadata": {}, // included to avoid warning
                        "data": {"text/html": limitmsg}
                    });
                }
            }
        };

        var old_clear_output = oa.OutputArea.prototype.clear_output;
        oa.OutputArea.prototype.clear_output = function () {
            // reset counter on execution.
            this.element.data('limit_output_count', 0);
            return old_clear_output.apply(this, arguments);
        };
    };

    var load_ipython_extension = function() {
        return Jupyter.notebook.config.loaded.then(initialize);
    };

    return {
        load_ipython_extension : load_ipython_extension
    };
});