diff --git a/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js b/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js index 49dee21f4..31e0aa3e7 100644 --- a/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js +++ b/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js @@ -1,14 +1,11 @@ // Restrict output in a codecell to a maximum length define([ - 'base/js/namespace', - 'jquery', 'notebook/js/outputarea', - 'base/js/dialog', 'notebook/js/codecell', 'services/config', 'base/js/utils' -], function(IPython, $, oa, dialog, cc, configmod, utils) { +], function(oa, cc, configmod, utils) { "use strict"; var base_url = utils.get_body_data("baseUrl"); @@ -32,51 +29,88 @@ define([ } }; + function is_finite_number (n) { + n = parseFloat(n); + return !isNaN(n) && isFinite(n); + } + config.loaded.then(function() { update_params(); - var MAX_CHARACTERS = params.limit_output; + // sometimes limit_output metadata val can get stored as a string + params.limit_output = parseFloat(params.limit_output); - oa.OutputArea.prototype._handle_output = oa.OutputArea.prototype.handle_output; + var old_handle_output = oa.OutputArea.prototype.handle_output; oa.OutputArea.prototype.handle_output = function (msg) { - if (msg.header.msg_type.match("stream|execute_result|display_data")) { - var count = 0; + 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 + 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") { - count = String(msg.content.text).length; - } else { - count = Math.max( + count += String(msg.content.text).length; + } + else { + 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 ) + (msg.content.data['text/html'] === undefined) ? 0 : String(msg.content.data['text/html']).length + ); } - if (count > MAX_CHARACTERS) { - console.log("limit_output: output", count, "exceeded", MAX_CHARACTERS, "characters. Further output muted."); + // 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, MAX_CHARACTERS) - } else { + 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, MAX_CHARACTERS); + 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, MAX_CHARACTERS); + msg.content.data['text/html'] = msg.content.data['text/html'].substr(0, to_add); } } - var limitmsg = {}; - limitmsg.data = []; + 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 - limitmsg.data['text/html'] = params.limit_output_message.replace("{limit_output_length}", MAX_CHARACTERS) - .replace("{output_length}", count); - this._handle_output(msg); - return this.append_display_data(limitmsg); + var limitmsg = params.limit_output_message.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}, + }); } } - return this._handle_output(msg); }; - cc.CodeCell.prototype._execute = cc.CodeCell.prototype.execute; - cc.CodeCell.prototype.execute = function() { + var old_clear_output = oa.OutputArea.prototype.clear_output; + oa.OutputArea.prototype.clear_output = function () { // reset counter on execution. - this.output_area.count = 0; - this.output_area.drop = false; - return this._execute(); + this.element.data('limit_output_count', 0); + return old_clear_output.apply(this, arguments); }; }); diff --git a/src/jupyter_contrib_nbextensions/nbextensions/limit_output/readme.md b/src/jupyter_contrib_nbextensions/nbextensions/limit_output/readme.md index 461bcb9a4..97764ce38 100644 --- a/src/jupyter_contrib_nbextensions/nbextensions/limit_output/readme.md +++ b/src/jupyter_contrib_nbextensions/nbextensions/limit_output/readme.md @@ -1,30 +1,39 @@ Limit Output ============ + Description ----------- -This extension limits the number of characters a codecell will output as text or HTML. -This also allows to interrupt endless loops of print commands. + +This extension limits the number of characters a codecell will output as text +or HTML. +This also allows the interruption of endless loops of print commands. [![Demo Video](http://img.youtube.com/vi/U26ujuPXf00/0.jpg)](https://youtu.be/U26ujuPXf00) You can set the number of characters using the ConfigManager: -```Python +```python from notebook.services.config import ConfigManager cm = ConfigManager().update('notebook', {'limit_output': 1000}) ``` or by using the [jupyter_nbextensions_configurator](https://github.com/Jupyter-contrib/jupyter_nbextensions_configurator) +The limit can also be set for an individual cell, using the cell's +`cell.metadata.limit_output`. + + Internals --------- -Three types of messages are intercepted: `stream`, `execute_result`, `display_data` - -For `stream`- type messages, the text string length is limited to `limit_output` number of characters -For other message types, `text/plain` and `text/html` content length is counted` and if either -exceeds `limit_output` charaters, will be truncated. +Three types of messages are intercepted: `stream`, `execute_result`, and +`display_data`. For `stream`-type messages, the text string length is limited +to `limit_output` number of characters. +For other message types, `text/plain` and `text/html` content length is +counted, and if either exceeds `limit_output` characters will be truncated to +`limit_output` number of characters. -The `limit_output_message` can be formatted to display the `limit_output` length and the current `output_length`, -respectively using the replacement fields `{limit_output_length}` and `{output_length}`. +The `limit_output_message` parameter can be formatted to display the +`limit_output` length and the current `output_length`, using the respective +replacement fields `{limit_output_length}` and `{output_length}`.