##// END OF EJS Templates
Reset the interactive namespace __warningregistry__ before executing code...
Reset the interactive namespace __warningregistry__ before executing code Fixes #6611. Idea: Right now, people often don't see important warnings when running code in IPython, because (to a first approximation) any given warning will only issue once per session. Blink and you'll miss it! This is a very common contributor to confused emails to numpy-discussion. E.g.: In [5]: 1 / my_array_with_random_contents /home/njs/.user-python2.7-64bit-3/bin/ipython:1: RuntimeWarning: divide by zero encountered in divide #!/home/njs/.user-python2.7-64bit-3/bin/python Out[5]: array([ 1.77073316, -2.29765021, -2.01800811, ..., 1.13871243, -1.08302964, -8.6185091 ]) Oo, right, guess I gotta be careful of those zeros -- thanks, numpy, for giving me that warning! A few days later: In [592]: 1 / some_other_array Out[592]: array([ 3.07735763, 0.50769289, 0.83984078, ..., -0.67563917, -0.85736257, -1.36511271]) Oops, it turns out that this array had a zero in it too, and that's going to bite me later. But no warning this time! The effect of this commit is to make it so that warnings triggered by the code in cell 5 do *not* suppress warnings triggered by the code in cell 592. Note that this only applies to warnings triggered *directly* by code entered interactively -- if somepkg.foo() calls anotherpkg.bad_func() which issues a warning, then this warning will still only be displayed once, even if multiple cells call somepkg.foo(). But if cell 5 and cell 592 both call anotherpkg.bad_func() directly, then both will get warnings. (Important exception: if foo() is defined *interactively*, and calls anotherpkg.bad_func(), then every cell that calls foo() will display the warning again. This is unavoidable without fixes to CPython upstream.) Explanation: Python's warning system has some weird quirks. By default, it tries to suppress duplicate warnings, where "duplicate" means the same warning message triggered twice by the same line of code. This requires determining which line of code is responsible for triggering a warning, and this is controlled by the stacklevel= argument to warnings.warn. Basically, though, the idea is that if foo() calls bar() which calls baz() which calls some_deprecated_api(), then baz() will get counted as being "responsible", and the warning system will make a note that the usage of some_deprecated_api() inside baz() has already been warned about and doesn't need to be warned about again. So far so good. To accomplish this, obviously, there has to be a record of somewhere which line this was. You might think that this would be done by recording the filename:linenumber pair in a dict inside the warnings module, or something like that. You would be wrong. What actually happens is that the warnings module will use stack introspection to reach into baz()'s execution environment, create a global (module-level) variable there named __warningregistry__, and then, inside this dictionary, record just the line number. Basically, it assumes that any given module contains only one line 1, only one line 2, etc., so storing the filename is irrelevant. Obviously for interactive code this is totally wrong -- all cells share the same execution environment and global namespace, and they all contain a new line 1. Currently the warnings module treats these as if they were all the same line. In fact they are not the same line; once we have executed a given chunk of code, we will never see those particular lines again. As soon as a given chunk of code finishes executing, its line number labels become meaningless, and the corresponding warning registry entries become meaningless as well. Therefore, with this patch we delete the __warningregistry__ each time we execute a new block of code.

File last commit:

r18238:4ab90e82
r18548:61431d7d
Show More
util.js
704 lines | 23.2 KiB | application/javascript | JavascriptLexer
//
// Utility functions for the HTML notebook's CasperJS tests.
//
casper.get_notebook_server = function () {
// Get the URL of a notebook server on which to run tests.
port = casper.cli.get("port");
port = (typeof port === 'undefined') ? '8888' : port;
return 'http://127.0.0.1:' + port;
};
casper.open_new_notebook = function () {
// Create and open a new notebook.
var baseUrl = this.get_notebook_server();
this.start(baseUrl);
this.waitFor(this.page_loaded);
this.thenClick('button#new_notebook');
this.waitForPopup('');
this.withPopup('', function () {this.waitForSelector('.CodeMirror-code');});
this.then(function () {
this.open(this.popups[0].url);
});
this.waitFor(this.page_loaded);
// Make sure the kernel has started
this.waitFor(this.kernel_running);
// track the IPython busy/idle state
this.thenEvaluate(function () {
require(['base/js/namespace', 'base/js/events'], function (IPython, events) {
events.on('kernel_idle.Kernel',function () {
IPython._status = 'idle';
});
events.on('kernel_busy.Kernel',function () {
IPython._status = 'busy';
});
});
});
// Because of the asynchronous nature of SlimerJS (Gecko), we need to make
// sure the notebook has actually been loaded into the IPython namespace
// before running any tests.
this.waitFor(function() {
return this.evaluate(function () {
return IPython.notebook;
});
});
};
casper.page_loaded = function() {
// Return whether or not the kernel is running.
return this.evaluate(function() {
return typeof IPython !== "undefined" &&
IPython.page !== undefined;
});
};
casper.kernel_running = function() {
// Return whether or not the kernel is running.
return this.evaluate(function() {
return IPython.notebook.kernel.is_connected();
});
};
casper.kernel_disconnected = function() {
return this.evaluate(function() {
return IPython.notebook.kernel.is_fully_disconnected();
});
};
casper.wait_for_kernel_ready = function () {
this.waitFor(this.kernel_running);
this.thenEvaluate(function () {
IPython._kernel_ready = false;
IPython.notebook.kernel.kernel_info(
function () {
IPython._kernel_ready = true;
});
});
this.waitFor(function () {
return this.evaluate(function () {
return IPython._kernel_ready;
});
});
};
casper.shutdown_current_kernel = function () {
// Shut down the current notebook's kernel.
this.thenEvaluate(function() {
IPython.notebook.session.delete();
});
// We close the page right after this so we need to give it time to complete.
this.wait(1000);
};
casper.delete_current_notebook = function () {
// Delete created notebook.
// For some unknown reason, this doesn't work?!?
this.thenEvaluate(function() {
IPython.notebook.delete();
});
};
casper.wait_for_busy = function () {
// Waits for the notebook to enter a busy state.
this.waitFor(function () {
return this.evaluate(function () {
return IPython._status == 'busy';
});
});
};
casper.wait_for_idle = function () {
// Waits for the notebook to idle.
this.waitFor(function () {
return this.evaluate(function () {
return IPython._status == 'idle';
});
});
};
casper.wait_for_output = function (cell_num, out_num) {
// wait for the nth output in a given cell
this.wait_for_idle();
out_num = out_num || 0;
this.then(function() {
this.waitFor(function (c, o) {
return this.evaluate(function get_output(c, o) {
var cell = IPython.notebook.get_cell(c);
return cell.output_area.outputs.length > o;
},
// pass parameter from the test suite js to the browser code js
{c : cell_num, o : out_num});
});
},
function then() { },
function timeout() {
this.echo("wait_for_output timed out!");
});
};
casper.wait_for_widget = function (widget_info) {
// wait for a widget msg que to reach 0
//
// Parameters
// ----------
// widget_info : object
// Object which contains info related to the widget. The model_id property
// is used to identify the widget.
this.waitFor(function () {
var pending = this.evaluate(function (m) {
return IPython.notebook.kernel.widget_manager.get_model(m).pending_msgs;
}, {m: widget_info.model_id});
if (pending === 0) {
return true;
} else {
return false;
}
});
};
casper.get_output_cell = function (cell_num, out_num) {
// return an output of a given cell
out_num = out_num || 0;
var result = casper.evaluate(function (c, o) {
var cell = IPython.notebook.get_cell(c);
return cell.output_area.outputs[o];
},
{c : cell_num, o : out_num});
if (!result) {
var num_outputs = casper.evaluate(function (c) {
var cell = IPython.notebook.get_cell(c);
return cell.output_area.outputs.length;
},
{c : cell_num});
this.test.assertTrue(false,
"Cell " + cell_num + " has no output #" + out_num + " (" + num_outputs + " total)"
);
} else {
return result;
}
};
casper.get_cells_length = function () {
// return the number of cells in the notebook
var result = casper.evaluate(function () {
return IPython.notebook.get_cells().length;
});
return result;
};
casper.set_cell_text = function(index, text){
// Set the text content of a cell.
this.evaluate(function (index, text) {
var cell = IPython.notebook.get_cell(index);
cell.set_text(text);
}, index, text);
};
casper.get_cell_text = function(index){
// Get the text content of a cell.
return this.evaluate(function (index) {
var cell = IPython.notebook.get_cell(index);
return cell.get_text();
}, index);
};
casper.insert_cell_at_bottom = function(cell_type){
// Inserts a cell at the bottom of the notebook
// Returns the new cell's index.
return this.evaluate(function (cell_type) {
var cell = IPython.notebook.insert_cell_at_bottom(cell_type);
return IPython.notebook.find_cell_index(cell);
}, cell_type);
};
casper.append_cell = function(text, cell_type) {
// Insert a cell at the bottom of the notebook and set the cells text.
// Returns the new cell's index.
var index = this.insert_cell_at_bottom(cell_type);
if (text !== undefined) {
this.set_cell_text(index, text);
}
return index;
};
casper.execute_cell = function(index, expect_failure){
// Asynchronously executes a cell by index.
// Returns the cell's index.
if (expect_failure === undefined) expect_failure = false;
var that = this;
this.then(function(){
that.evaluate(function (index) {
var cell = IPython.notebook.get_cell(index);
cell.execute();
}, index);
});
this.wait_for_idle();
this.then(function () {
var error = that.evaluate(function (index) {
var cell = IPython.notebook.get_cell(index);
var outputs = cell.output_area.outputs;
for (var i = 0; i < outputs.length; i++) {
if (outputs[i].output_type == 'error') {
return outputs[i];
}
}
return false;
}, index);
if (error === null) {
this.test.fail("Failed to check for error output");
}
if (expect_failure && error === false) {
this.test.fail("Expected error while running cell");
} else if (!expect_failure && error !== false) {
this.test.fail("Error running cell:\n" + error.traceback.join('\n'));
}
});
return index;
};
casper.execute_cell_then = function(index, then_callback, expect_failure) {
// Synchronously executes a cell by index.
// Optionally accepts a then_callback parameter. then_callback will get called
// when the cell has finished executing.
// Returns the cell's index.
var return_val = this.execute_cell(index, expect_failure);
this.wait_for_idle();
var that = this;
this.then(function(){
if (then_callback!==undefined) {
then_callback.apply(that, [index]);
}
});
return return_val;
};
casper.cell_element_exists = function(index, selector){
// Utility function that allows us to easily check if an element exists
// within a cell. Uses JQuery selector to look for the element.
return casper.evaluate(function (index, selector) {
var $cell = IPython.notebook.get_cell(index).element;
return $cell.find(selector).length > 0;
}, index, selector);
};
casper.cell_element_function = function(index, selector, function_name, function_args){
// Utility function that allows us to execute a jQuery function on an
// element within a cell.
return casper.evaluate(function (index, selector, function_name, function_args) {
var $cell = IPython.notebook.get_cell(index).element;
var $el = $cell.find(selector);
return $el[function_name].apply($el, function_args);
}, index, selector, function_name, function_args);
};
casper.validate_notebook_state = function(message, mode, cell_index) {
// Validate the entire dual mode state of the notebook. Make sure no more than
// one cell is selected, focused, in edit mode, etc...
// General tests.
this.test.assertEquals(this.get_keyboard_mode(), this.get_notebook_mode(),
message + '; keyboard and notebook modes match');
// Is the selected cell the only cell that is selected?
if (cell_index!==undefined) {
this.test.assert(this.is_only_cell_selected(cell_index),
message + '; cell ' + cell_index + ' is the only cell selected');
}
// Mode specific tests.
if (mode==='command') {
// Are the notebook and keyboard manager in command mode?
this.test.assertEquals(this.get_keyboard_mode(), 'command',
message + '; in command mode');
// Make sure there isn't a single cell in edit mode.
this.test.assert(this.is_only_cell_edit(null),
message + '; all cells in command mode');
this.test.assert(this.is_cell_editor_focused(null),
message + '; no cell editors are focused while in command mode');
} else if (mode==='edit') {
// Are the notebook and keyboard manager in edit mode?
this.test.assertEquals(this.get_keyboard_mode(), 'edit',
message + '; in edit mode');
if (cell_index!==undefined) {
// Is the specified cell the only cell in edit mode?
this.test.assert(this.is_only_cell_edit(cell_index),
message + '; cell ' + cell_index + ' is the only cell in edit mode');
// Is the specified cell the only cell with a focused code mirror?
this.test.assert(this.is_cell_editor_focused(cell_index),
message + '; cell ' + cell_index + '\'s editor is appropriately focused');
}
} else {
this.test.assert(false, message + '; ' + mode + ' is an unknown mode');
}
};
casper.select_cell = function(index) {
// Select a cell in the notebook.
this.evaluate(function (i) {
IPython.notebook.select(i);
}, {i: index});
};
casper.click_cell_editor = function(index) {
// Emulate a click on a cell's editor.
// Code Mirror does not play nicely with emulated brower events.
// Instead of trying to emulate a click, here we run code similar to
// the code used in Code Mirror that handles the mousedown event on a
// region of codemirror that the user can focus.
this.evaluate(function (i) {
var cm = IPython.notebook.get_cell(i).code_mirror;
if (cm.options.readOnly != "nocursor" && (document.activeElement != cm.display.input))
cm.display.input.focus();
}, {i: index});
};
casper.set_cell_editor_cursor = function(index, line_index, char_index) {
// Set the Code Mirror instance cursor's location.
this.evaluate(function (i, l, c) {
IPython.notebook.get_cell(i).code_mirror.setCursor(l, c);
}, {i: index, l: line_index, c: char_index});
};
casper.focus_notebook = function() {
// Focus the notebook div.
this.evaluate(function (){
$('#notebook').focus();
}, {});
};
casper.trigger_keydown = function() {
// Emulate a keydown in the notebook.
for (var i = 0; i < arguments.length; i++) {
this.evaluate(function (k) {
var element = $(document);
var event = IPython.keyboard.shortcut_to_event(k, 'keydown');
element.trigger(event);
}, {k: arguments[i]});
}
};
casper.get_keyboard_mode = function() {
// Get the mode of the keyboard manager.
return this.evaluate(function() {
return IPython.keyboard_manager.mode;
}, {});
};
casper.get_notebook_mode = function() {
// Get the mode of the notebook.
return this.evaluate(function() {
return IPython.notebook.mode;
}, {});
};
casper.get_cell = function(index) {
// Get a single cell.
//
// Note: Handles to DOM elements stored in the cell will be useless once in
// CasperJS context.
return this.evaluate(function(i) {
var cell = IPython.notebook.get_cell(i);
if (cell) {
return cell;
}
return null;
}, {i : index});
};
casper.is_cell_editor_focused = function(index) {
// Make sure a cell's editor is the only editor focused on the page.
return this.evaluate(function(i) {
var focused_textarea = $('#notebook .CodeMirror-focused textarea');
if (focused_textarea.length > 1) { throw 'More than one Code Mirror editor is focused at once!'; }
if (i === null) {
return focused_textarea.length === 0;
} else {
var cell = IPython.notebook.get_cell(i);
if (cell) {
return cell.code_mirror.getInputField() == focused_textarea[0];
}
}
return false;
}, {i : index});
};
casper.is_only_cell_selected = function(index) {
// Check if a cell is the only cell selected.
// Pass null as the index to check if no cells are selected.
return this.is_only_cell_on(index, 'selected', 'unselected');
};
casper.is_only_cell_edit = function(index) {
// Check if a cell is the only cell in edit mode.
// Pass null as the index to check if all of the cells are in command mode.
return this.is_only_cell_on(index, 'edit_mode', 'command_mode');
};
casper.is_only_cell_on = function(i, on_class, off_class) {
// Check if a cell is the only cell with the `on_class` DOM class applied to it.
// All of the other cells are checked for the `off_class` DOM class.
// Pass null as the index to check if all of the cells have the `off_class`.
var cells_length = this.get_cells_length();
for (var j = 0; j < cells_length; j++) {
if (j === i) {
if (this.cell_has_class(j, off_class) || !this.cell_has_class(j, on_class)) {
return false;
}
} else {
if (!this.cell_has_class(j, off_class) || this.cell_has_class(j, on_class)) {
return false;
}
}
}
return true;
};
casper.cell_has_class = function(index, classes) {
// Check if a cell has a class.
return this.evaluate(function(i, c) {
var cell = IPython.notebook.get_cell(i);
if (cell) {
return cell.element.hasClass(c);
}
return false;
}, {i : index, c: classes});
};
casper.is_cell_rendered = function (index) {
return this.evaluate(function(i) {
return !!IPython.notebook.get_cell(i).rendered;
}, {i:index});
};
casper.assert_colors_equal = function (hex_color, local_color, msg) {
// Tests to see if two colors are equal.
//
// Parameters
// hex_color: string
// Hexadecimal color code, with or without preceeding hash character.
// local_color: string
// Local color representation. Can either be hexadecimal (default for
// phantom) or rgb (default for slimer).
// Remove parentheses, hashes, semi-colons, and space characters.
hex_color = hex_color.replace(/[\(\); #]/, '');
local_color = local_color.replace(/[\(\); #]/, '');
// If the local color is rgb, clean it up and replace
if (local_color.substr(0,3).toLowerCase() == 'rgb') {
components = local_color.substr(3).split(',');
local_color = '';
for (var i = 0; i < components.length; i++) {
var part = parseInt(components[i]).toString(16);
while (part.length < 2) part = '0' + part;
local_color += part;
}
}
this.test.assertEquals(hex_color.toUpperCase(), local_color.toUpperCase(), msg);
};
casper.notebook_test = function(test) {
// Wrap a notebook test to reduce boilerplate.
this.open_new_notebook();
// Echo whether or not we are running this test using SlimerJS
if (this.evaluate(function(){
return typeof InstallTrigger !== 'undefined'; // Firefox 1.0+
})) {
console.log('This test is running in SlimerJS.');
this.slimerjs = true;
}
// Make sure to remove the onbeforeunload callback. This callback is
// responsible for the "Are you sure you want to quit?" type messages.
// PhantomJS ignores these prompts, SlimerJS does not which causes hangs.
this.then(function(){
this.evaluate(function(){
window.onbeforeunload = function(){};
});
});
this.then(test);
// Kill the kernel and delete the notebook.
this.shutdown_current_kernel();
// This is still broken but shouldn't be a problem for now.
// this.delete_current_notebook();
// This is required to clean up the page we just finished with. If we don't call this
// casperjs will leak file descriptors of all the open WebSockets in that page. We
// have to set this.page=null so that next time casper.start runs, it will create a
// new page from scratch.
this.then(function () {
this.page.close();
this.page = null;
});
// Run the browser automation.
this.run(function() {
this.test.done();
});
};
casper.wait_for_dashboard = function () {
// Wait for the dashboard list to load.
casper.waitForSelector('.list_item');
};
casper.open_dashboard = function () {
// Start casper by opening the dashboard page.
var baseUrl = this.get_notebook_server();
this.start(baseUrl);
this.waitFor(this.page_loaded);
this.wait_for_dashboard();
};
casper.dashboard_test = function (test) {
// Open the dashboard page and run a test.
this.open_dashboard();
this.then(test);
this.then(function () {
this.page.close();
this.page = null;
});
// Run the browser automation.
this.run(function() {
this.test.done();
});
};
// note that this will only work for UNIQUE events -- if you want to
// listen for the same event twice, this will not work!
casper.event_test = function (name, events, action, timeout) {
// set up handlers to listen for each of the events
this.thenEvaluate(function (events) {
var make_handler = function (event) {
return function () {
IPython._events_triggered.push(event);
IPython.notebook.events.off(event, null, IPython._event_handlers[event]);
delete IPython._event_handlers[event];
};
};
IPython._event_handlers = {};
IPython._events_triggered = [];
for (var i=0; i < events.length; i++) {
IPython._event_handlers[events[i]] = make_handler(events[i]);
IPython.notebook.events.on(events[i], IPython._event_handlers[events[i]]);
}
}, [events]);
// execute the requested action
this.then(action);
// wait for all the events to be triggered
this.waitFor(function () {
return this.evaluate(function (events) {
return IPython._events_triggered.length >= events.length;
}, [events]);
}, undefined, undefined, timeout);
// test that the events were triggered in the proper order
this.then(function () {
var triggered = this.evaluate(function () {
return IPython._events_triggered;
});
var handlers = this.evaluate(function () {
return Object.keys(IPython._event_handlers);
});
this.test.assertEquals(triggered.length, events.length, name + ': ' + events.length + ' events were triggered');
this.test.assertEquals(handlers.length, 0, name + ': all handlers triggered');
for (var i=0; i < events.length; i++) {
this.test.assertEquals(triggered[i], events[i], name + ': ' + events[i] + ' was triggered');
}
});
// turn off any remaining event listeners
this.thenEvaluate(function () {
for (var event in IPython._event_handlers) {
IPython.notebook.events.off(event, null, IPython._event_handlers[event]);
delete IPython._event_handlers[event];
}
});
};
casper.options.waitTimeout=10000;
casper.on('waitFor.timeout', function onWaitForTimeout(timeout) {
this.echo("Timeout for " + casper.get_notebook_server());
this.echo("Is the notebook server running?");
});
casper.print_log = function () {
// Pass `console.log` calls from page JS to casper.
this.on('remote.message', function(msg) {
this.echo('Remote message caught: ' + msg);
});
};
casper.on("page.error", function onError(msg, trace) {
// show errors in the browser
this.echo("Page Error!");
for (var i = 0; i < trace.length; i++) {
var frame = trace[i];
var file = frame.file;
// shorten common phantomjs evaluate url
// this will have a different value on slimerjs
if (file === "phantomjs://webpage.evaluate()") {
file = "evaluate";
}
this.echo("line " + frame.line + " of " + file);
if (frame.function.length > 0) {
this.echo("in " + frame.function);
}
}
this.echo(msg);
});
casper.capture_log = function () {
// show captured errors
var captured_log = [];
var seen_errors = 0;
this.on('remote.message', function(msg) {
captured_log.push(msg);
});
this.test.on("test.done", function (result) {
// test.done runs per-file,
// but suiteResults is per-suite (directory)
var current_errors;
if (this.suiteResults) {
// casper 1.1 has suiteResults
current_errors = this.suiteResults.countErrors() + this.suiteResults.countFailed();
} else {
// casper 1.0 has testResults instead
current_errors = this.testResults.failed;
}
if (current_errors > seen_errors && captured_log.length > 0) {
casper.echo("\nCaptured console.log:");
for (var i = 0; i < captured_log.length; i++) {
casper.echo(" " + captured_log[i]);
}
}
seen_errors = current_errors;
captured_log = [];
});
};
casper.capture_log();