From 65eb671ed82f89e450ba2df7ee91864bad2fec14 Mon Sep 17 00:00:00 2001 From: Josh Barnes Date: Fri, 2 Dec 2016 00:03:40 +0000 Subject: [PATCH 1/8] [limit_output] remove unused requirements --- .../nbextensions/limit_output/main.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js b/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js index 49dee21f4..e123defa5 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"); From d64cc5bc672c6c0e479e310209142822a3b5304f Mon Sep 17 00:00:00 2001 From: Josh Barnes Date: Fri, 2 Dec 2016 00:04:07 +0000 Subject: [PATCH 2/8] [limit_output] add missing semicolons --- .../nbextensions/limit_output/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js b/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js index e123defa5..4d460cfe1 100644 --- a/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js +++ b/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js @@ -42,12 +42,12 @@ define([ } 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."); if (msg.header.msg_type === "stream") { - msg.content.text = msg.content.text.substr(0, MAX_CHARACTERS) + msg.content.text = msg.content.text.substr(0, MAX_CHARACTERS); } else { if (msg.content.data['text/plain'] !== undefined) { msg.content.data['text/plain'] = msg.content.data['text/plain'].substr(0, MAX_CHARACTERS); From 4d5ec1570d173ede850b06a9f83668a4004bac18 Mon Sep 17 00:00:00 2001 From: Josh Barnes Date: Fri, 2 Dec 2016 00:09:09 +0000 Subject: [PATCH 3/8] [limit_output] store old methods in closure variables rather than object parameters, which may get overwritten --- .../nbextensions/limit_output/main.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js b/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js index 4d460cfe1..bc4ec7ec0 100644 --- a/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js +++ b/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js @@ -33,7 +33,7 @@ define([ update_params(); var MAX_CHARACTERS = 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; @@ -61,19 +61,20 @@ define([ // 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 ret = old_handle_output.apply(this, arguments); + this.append_display_data(limitmsg); + return ret; } } - return this._handle_output(msg); + return old_handle_output.apply(this, arguments); }; - cc.CodeCell.prototype._execute = cc.CodeCell.prototype.execute; + var old_execute = cc.CodeCell.prototype.execute; cc.CodeCell.prototype.execute = function() { // reset counter on execution. this.output_area.count = 0; this.output_area.drop = false; - return this._execute(); + return old_execute.apply(this, arguments); }; }); From 973ad52c3f9b274cce4c67e85aeef0829ce6da73 Mon Sep 17 00:00:00 2001 From: Josh Barnes Date: Fri, 2 Dec 2016 00:39:47 +0000 Subject: [PATCH 4/8] [limit_output] allow cell-specific override of output length via metadata value --- .../nbextensions/limit_output/main.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js b/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js index bc4ec7ec0..1554b3942 100644 --- a/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js +++ b/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js @@ -29,9 +29,15 @@ 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); var old_handle_output = oa.OutputArea.prototype.handle_output; oa.OutputArea.prototype.handle_output = function (msg) { @@ -44,6 +50,11 @@ define([ (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 ); } + var MAX_CHARACTERS = params.limit_output; + var cell = this.element.closest('.cell').data('cell'); + if (is_finite_number(cell.metadata.limit_output)) { + MAX_CHARACTERS = parseFloat(cell.metadata.limit_output); + } if (count > MAX_CHARACTERS) { console.log("limit_output: output", count, "exceeded", MAX_CHARACTERS, "characters. Further output muted."); if (msg.header.msg_type === "stream") { From 854f16e40af8b21f19c513fdf5c43ea8d6bc72da Mon Sep 17 00:00:00 2001 From: Josh Barnes Date: Fri, 2 Dec 2016 01:52:04 +0000 Subject: [PATCH 5/8] [limit_ouput] patch output_area.clear_output rather than cell.execute --- .../nbextensions/limit_output/main.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js b/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js index 1554b3942..1b9befcf7 100644 --- a/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js +++ b/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js @@ -80,12 +80,11 @@ define([ return old_handle_output.apply(this, arguments); }; - var old_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 old_execute.apply(this, arguments); + this.data('limit_output_count', 0); + return old_clear_output.apply(this, arguments); }; }); From 6746e9f8d9177f8573877ebfee32d01fe136ec72 Mon Sep 17 00:00:00 2001 From: Josh Barnes Date: Fri, 2 Dec 2016 01:54:01 +0000 Subject: [PATCH 6/8] [limit_output] make limit_output notes persist through notebook reload by adding them as actual output, rather than just calling append_display_data --- .../nbextensions/limit_output/main.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js b/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js index 1b9befcf7..f91f963a2 100644 --- a/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js +++ b/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js @@ -67,14 +67,15 @@ define([ msg.content.data['text/html'] = msg.content.data['text/html'].substr(0, MAX_CHARACTERS); } } - var limitmsg = {}; - limitmsg.data = []; + // 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); - var ret = old_handle_output.apply(this, arguments); - this.append_display_data(limitmsg); - return ret; + 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 old_handle_output.apply(this, arguments); From 0d4fdcb4397f1d86ea11958362390ee5059542fc Mon Sep 17 00:00:00 2001 From: Josh Barnes Date: Fri, 2 Dec 2016 01:56:54 +0000 Subject: [PATCH 7/8] [limit_output] limit outputs even over several distinct outputs --- .../nbextensions/limit_output/main.js | 61 +++++++++++++------ 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js b/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js index f91f963a2..31e0aa3e7 100644 --- a/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js +++ b/src/jupyter_contrib_nbextensions/nbextensions/limit_output/main.js @@ -41,33 +41,59 @@ define([ 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 + ); } - var MAX_CHARACTERS = params.limit_output; - var cell = this.element.closest('.cell').data('cell'); - if (is_finite_number(cell.metadata.limit_output)) { - MAX_CHARACTERS = parseFloat(cell.metadata.limit_output); + // save updated count + this.element.data('limit_output_count', count); + + if (count <= MAX_CHARACTERS) { + return old_handle_output.apply(this, arguments); } - if (count > MAX_CHARACTERS) { - console.log("limit_output: output", count, "exceeded", MAX_CHARACTERS, "characters. Further output muted."); + // 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); } } + 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("{limit_output_length}", MAX_CHARACTERS) .replace("{output_length}", count); @@ -78,13 +104,12 @@ define([ }); } } - return old_handle_output.apply(this, arguments); }; var old_clear_output = oa.OutputArea.prototype.clear_output; oa.OutputArea.prototype.clear_output = function () { // reset counter on execution. - this.data('limit_output_count', 0); + this.element.data('limit_output_count', 0); return old_clear_output.apply(this, arguments); }; }); From 7dc0e69a9c9e4d541b389429ce0b0f7ce8f955b6 Mon Sep 17 00:00:00 2001 From: Josh Barnes Date: Fri, 2 Dec 2016 02:11:23 +0000 Subject: [PATCH 8/8] [limit_output] update readme --- .../nbextensions/limit_output/readme.md | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) 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}`.