mirror of
https://github.com/jupyter/notebook.git
synced 2025-01-06 11:35:24 +08:00
Merge remote-tracking branch 'upstream/master' into mono_cursor_offset
This commit is contained in:
commit
5a070dcd1a
@ -193,7 +193,7 @@ class MultiKernelManager(LoggingConfigurable):
|
||||
def create_shell_stream(self, kernel_id):
|
||||
ip = self.get_kernel_ip(kernel_id)
|
||||
ports = self.get_kernel_ports(kernel_id)
|
||||
shell_stream = self.create_connected_stream(ip, ports['shell_port'], zmq.XREQ)
|
||||
shell_stream = self.create_connected_stream(ip, ports['shell_port'], zmq.DEALER)
|
||||
return shell_stream
|
||||
|
||||
def create_hb_stream(self, kernel_id):
|
||||
|
@ -228,6 +228,7 @@ class NotebookManager(LoggingConfigurable):
|
||||
os.unlink(old_pypath)
|
||||
self.mapping[notebook_id] = new_name
|
||||
self.rev_mapping[new_name] = notebook_id
|
||||
del self.rev_mapping[old_name]
|
||||
|
||||
def delete_notebook(self, notebook_id):
|
||||
"""Delete notebook by notebook_id."""
|
||||
|
@ -39,6 +39,10 @@ span#notebook_name {
|
||||
padding: 2px 1.6em;
|
||||
}
|
||||
|
||||
.ui-menu .ui-menu-item a.ui-state-focus {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ui-menu hr {
|
||||
margin: 0.3em 0;
|
||||
}
|
||||
@ -281,7 +285,7 @@ div.text_cell_render {
|
||||
.completions {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
overflow: auto;
|
||||
overflow: hidden;
|
||||
border: 1px solid grey;
|
||||
}
|
||||
|
||||
@ -291,6 +295,7 @@ div.text_cell_render {
|
||||
border: none;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
overflow: auto;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
|
@ -61,11 +61,11 @@ var IPython = (function (IPython) {
|
||||
// handlers and is used to provide custom key handling. Its return
|
||||
// value is used to determine if CodeMirror should ignore the event:
|
||||
// true = ignore, false = don't ignore.
|
||||
|
||||
|
||||
if (this.read_only){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
var that = this;
|
||||
// whatever key is pressed, first, cancel the tooltip request before
|
||||
// they are sent, and remove tooltip if any, except for tab again
|
||||
@ -90,7 +90,7 @@ var IPython = (function (IPython) {
|
||||
event.stop();
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
return true;
|
||||
};
|
||||
} else if (event.which === key.ESC) {
|
||||
IPython.tooltip.remove_and_cancel_tooltip(true);
|
||||
@ -102,7 +102,7 @@ var IPython = (function (IPython) {
|
||||
event.stop();
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
return true;
|
||||
};
|
||||
} else if (event.keyCode === key.TAB && event.type == 'keydown') {
|
||||
// Tab completion.
|
||||
@ -267,6 +267,9 @@ var IPython = (function (IPython) {
|
||||
if (data.cell_type === 'code') {
|
||||
if (data.input !== undefined) {
|
||||
this.set_text(data.input);
|
||||
// make this value the starting point, so that we can only undo
|
||||
// to this state, instead of a blank cell
|
||||
this.code_mirror.clearHistory();
|
||||
}
|
||||
if (data.prompt_number !== undefined) {
|
||||
this.set_input_prompt(data.prompt_number);
|
||||
|
@ -8,17 +8,41 @@ var IPython = (function (IPython) {
|
||||
// easyier key mapping
|
||||
var key = IPython.utils.keycodes;
|
||||
|
||||
function prepend_n_prc(str, n) {
|
||||
for( var i =0 ; i< n ; i++)
|
||||
{ str = '%'+str }
|
||||
return str;
|
||||
}
|
||||
|
||||
function _existing_completion(item, completion_array){
|
||||
for( var c in completion_array ) {
|
||||
if(completion_array[c].substr(-item.length) == item)
|
||||
{ return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// what is the common start of all completions
|
||||
|
||||
|
||||
function shared_start(B) {
|
||||
function shared_start(B, drop_prct) {
|
||||
if (B.length == 1) {
|
||||
return B[0];
|
||||
}
|
||||
var A = new Array();
|
||||
var common;
|
||||
var min_lead_prct = 10;
|
||||
for (var i = 0; i < B.length; i++) {
|
||||
A.push(B[i].str);
|
||||
var str = B[i].str;
|
||||
var localmin = 0;
|
||||
if(drop_prct == true){
|
||||
while ( str.substr(0, 1) == '%') {
|
||||
localmin = localmin+1;
|
||||
str = str.substring(1);
|
||||
}
|
||||
}
|
||||
min_lead_prct = Math.min(min_lead_prct, localmin);
|
||||
A.push(str);
|
||||
}
|
||||
|
||||
if (A.length > 1) {
|
||||
var tem1, tem2, s;
|
||||
A = A.slice(0).sort();
|
||||
@ -29,10 +53,10 @@ var IPython = (function (IPython) {
|
||||
tem1 = tem1.substring(0, --s);
|
||||
}
|
||||
if (tem1 == "" || tem2.indexOf(tem1) != 0) {
|
||||
return null;
|
||||
return prepend_n_prc('', min_lead_prct);
|
||||
}
|
||||
return {
|
||||
str: tem1,
|
||||
str: prepend_n_prc(tem1, min_lead_prct),
|
||||
type: "computed",
|
||||
from: B[0].from,
|
||||
to: B[0].to
|
||||
@ -116,12 +140,19 @@ var IPython = (function (IPython) {
|
||||
|
||||
var cur = this.editor.getCursor();
|
||||
var results = CodeMirror.contextHint(this.editor);
|
||||
var filterd_results = Array();
|
||||
//remove results from context completion
|
||||
//that are already in kernel completion
|
||||
for(var elm in results) {
|
||||
if(_existing_completion(results[elm]['str'], matches) == false)
|
||||
{ filterd_results.push(results[elm]); }
|
||||
}
|
||||
|
||||
// append the introspection result, in order, at at the beginning of
|
||||
// the table and compute the replacement range from current cursor
|
||||
// positon and matched_text length.
|
||||
for (var i = matches.length - 1; i >= 0; --i) {
|
||||
results.unshift({
|
||||
filterd_results.unshift({
|
||||
str: matches[i],
|
||||
type: "introspection",
|
||||
from: {
|
||||
@ -136,7 +167,7 @@ var IPython = (function (IPython) {
|
||||
}
|
||||
|
||||
// one the 2 sources results have been merge, deal with it
|
||||
this.raw_result = results;
|
||||
this.raw_result = filterd_results;
|
||||
|
||||
// if empty result return
|
||||
if (!this.raw_result || !this.raw_result.length) return;
|
||||
@ -244,7 +275,7 @@ var IPython = (function (IPython) {
|
||||
//Check that shared start is not null which can append with prefixed completion
|
||||
// like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
|
||||
// to erase py
|
||||
var sh = shared_start(this.raw_result);
|
||||
var sh = shared_start(this.raw_result, true);
|
||||
if (sh) {
|
||||
this.insert(sh);
|
||||
}
|
||||
|
@ -98,8 +98,9 @@ var IPython = (function (IPython) {
|
||||
" or if the url does not look right, there could be an error in the" +
|
||||
" server's configuration.";
|
||||
} else {
|
||||
msg = "Websocket connection closed unexpectedly." +
|
||||
" The kernel will no longer be responsive.";
|
||||
IPython.notification_widget.set_message('Reconnecting Websockets', 1000);
|
||||
this.start_channels();
|
||||
return;
|
||||
}
|
||||
var dialog = $('<div/>');
|
||||
dialog.html(msg);
|
||||
|
@ -28,7 +28,7 @@ var IPython = (function (IPython) {
|
||||
this.control_key_active = false;
|
||||
this.notebook_id = null;
|
||||
this.notebook_name = null;
|
||||
this.notebook_name_blacklist_re = /[\/\\]/;
|
||||
this.notebook_name_blacklist_re = /[\/\\:]/;
|
||||
this.nbformat = 3 // Increment this when changing the nbformat
|
||||
this.style();
|
||||
this.create_elements();
|
||||
@ -602,6 +602,9 @@ var IPython = (function (IPython) {
|
||||
text = '';
|
||||
}
|
||||
target_cell.set_text(text);
|
||||
// make this value the starting point, so that we can only undo
|
||||
// to this state, instead of a blank cell
|
||||
target_cell.code_mirror.clearHistory();
|
||||
source_element.remove();
|
||||
this.dirty = true;
|
||||
};
|
||||
@ -623,6 +626,9 @@ var IPython = (function (IPython) {
|
||||
// The edit must come before the set_text.
|
||||
target_cell.edit();
|
||||
target_cell.set_text(text);
|
||||
// make this value the starting point, so that we can only undo
|
||||
// to this state, instead of a blank cell
|
||||
target_cell.code_mirror.clearHistory();
|
||||
source_element.remove();
|
||||
this.dirty = true;
|
||||
};
|
||||
@ -645,6 +651,9 @@ var IPython = (function (IPython) {
|
||||
// The edit must come before the set_text.
|
||||
target_cell.edit();
|
||||
target_cell.set_text(text);
|
||||
// make this value the starting point, so that we can only undo
|
||||
// to this state, instead of a blank cell
|
||||
target_cell.code_mirror.clearHistory();
|
||||
source_element.remove();
|
||||
this.dirty = true;
|
||||
};
|
||||
@ -667,6 +676,9 @@ var IPython = (function (IPython) {
|
||||
// The edit must come before the set_text.
|
||||
target_cell.edit();
|
||||
target_cell.set_text(text);
|
||||
// make this value the starting point, so that we can only undo
|
||||
// to this state, instead of a blank cell
|
||||
target_cell.code_mirror.clearHistory();
|
||||
source_element.remove();
|
||||
this.dirty = true;
|
||||
};
|
||||
@ -693,6 +705,9 @@ var IPython = (function (IPython) {
|
||||
target_cell.set_level(level);
|
||||
target_cell.edit();
|
||||
target_cell.set_text(text);
|
||||
// make this value the starting point, so that we can only undo
|
||||
// to this state, instead of a blank cell
|
||||
target_cell.code_mirror.clearHistory();
|
||||
source_element.remove();
|
||||
this.dirty = true;
|
||||
};
|
||||
|
@ -85,10 +85,10 @@ var IPython = (function (IPython) {
|
||||
that.set_message("Saving notebook",500);
|
||||
});
|
||||
$([IPython.events]).on('notebook_saved.Notebook', function () {
|
||||
that.set_message("Notebook saved",500);
|
||||
that.set_message("Notebook saved",2000);
|
||||
});
|
||||
$([IPython.events]).on('notebook_save_failed.Notebook', function () {
|
||||
that.set_message("Notebook save failed",500);
|
||||
that.set_message("Notebook save failed",2000);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -181,11 +181,7 @@ var IPython = (function (IPython) {
|
||||
if (json.stream == undefined){
|
||||
json.stream = 'stdout';
|
||||
}
|
||||
if (!utils.fixConsole(json.text)){
|
||||
// fixConsole gives nothing (empty string, \r, etc.)
|
||||
// so don't append any elements, which might add undesirable space
|
||||
return;
|
||||
}
|
||||
var text = json.text;
|
||||
var subclass = "output_"+json.stream;
|
||||
if (this.outputs.length > 0){
|
||||
// have at least one output to consider
|
||||
@ -194,15 +190,23 @@ var IPython = (function (IPython) {
|
||||
// latest output was in the same stream,
|
||||
// so append directly into its pre tag
|
||||
// escape ANSI & HTML specials:
|
||||
var text = utils.fixConsole(json.text);
|
||||
this.element.find('div.'+subclass).last().find('pre').append(text);
|
||||
var pre = this.element.find('div.'+subclass).last().find('pre');
|
||||
var html = utils.fixCarriageReturn(
|
||||
pre.html() + utils.fixConsole(text));
|
||||
pre.html(html);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!text.replace("\r", "")) {
|
||||
// text is nothing (empty string, \r, etc.)
|
||||
// so don't append any elements, which might add undesirable space
|
||||
return;
|
||||
}
|
||||
|
||||
// If we got here, attach a new div
|
||||
var toinsert = this.create_output_area();
|
||||
this.append_text(json.text, toinsert, "output_stream "+subclass);
|
||||
this.append_text(text, toinsert, "output_stream "+subclass);
|
||||
this.element.append(toinsert);
|
||||
};
|
||||
|
||||
@ -260,6 +264,7 @@ var IPython = (function (IPython) {
|
||||
var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_text");
|
||||
// escape ANSI & HTML specials in plaintext:
|
||||
data = utils.fixConsole(data);
|
||||
data = utils.fixCarriageReturn(data);
|
||||
if (extra_class){
|
||||
toinsert.addClass(extra_class);
|
||||
}
|
||||
@ -277,14 +282,22 @@ var IPython = (function (IPython) {
|
||||
|
||||
OutputArea.prototype.append_png = function (png, element) {
|
||||
var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_png");
|
||||
toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
|
||||
var img = $("<img/>").attr('src','data:image/png;base64,'+png);
|
||||
img.load(function () {
|
||||
$(this).resizable({'aspectRatio': true, 'autoHide': true})
|
||||
});
|
||||
toinsert.append(img);
|
||||
element.append(toinsert);
|
||||
};
|
||||
|
||||
|
||||
OutputArea.prototype.append_jpeg = function (jpeg, element) {
|
||||
var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_jpeg");
|
||||
toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
|
||||
var img = $("<img/>").attr('src','data:image/jpeg;base64,'+jpeg);
|
||||
img.load(function () {
|
||||
$(this).resizable({'aspectRatio': true, 'autoHide': true})
|
||||
});
|
||||
toinsert.append(img);
|
||||
element.append(toinsert);
|
||||
};
|
||||
|
||||
@ -328,7 +341,7 @@ var IPython = (function (IPython) {
|
||||
|
||||
OutputArea.prototype.clear_output_callback = function (stdout, stderr, other) {
|
||||
var output_div = this.element;
|
||||
|
||||
|
||||
if (stdout && stderr && other){
|
||||
// clear all, no need for logic
|
||||
output_div.html("");
|
||||
@ -347,7 +360,7 @@ var IPython = (function (IPython) {
|
||||
if (other) {
|
||||
output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
|
||||
}
|
||||
|
||||
|
||||
// remove cleared outputs from JSON list:
|
||||
for (var i = this.outputs.length - 1; i >= 0; i--) {
|
||||
var out = this.outputs[i];
|
||||
|
@ -117,7 +117,7 @@ var IPython = (function (IPython) {
|
||||
|
||||
Pager.prototype.append_text = function (text) {
|
||||
var toinsert = $("<div/>").addClass("output_area output_stream");
|
||||
toinsert.append($('<pre/>').html(utils.fixConsole(text)));
|
||||
toinsert.append($('<pre/>').html(utils.fixCarriageReturn(utils.fixConsole(text))));
|
||||
this.pager_element.append(toinsert);
|
||||
};
|
||||
|
||||
|
@ -52,7 +52,7 @@ var IPython = (function (IPython) {
|
||||
that.update_document_title();
|
||||
});
|
||||
$([IPython.events]).on('notebook_save_failed.Notebook', function () {
|
||||
that.set_save_status('');
|
||||
that.set_save_status('Last Save Failed!');
|
||||
});
|
||||
};
|
||||
|
||||
@ -83,7 +83,7 @@ var IPython = (function (IPython) {
|
||||
$(this).find('h3').html(
|
||||
"Invalid notebook name. Notebook names must "+
|
||||
"have 1 or more characters and can contain any characters " +
|
||||
"except / and \\. Please enter a new notebook name:"
|
||||
"except :/\\. Please enter a new notebook name:"
|
||||
);
|
||||
} else {
|
||||
IPython.notebook.set_notebook_name(new_name);
|
||||
|
@ -158,6 +158,9 @@ var IPython = (function (IPython) {
|
||||
if (data.cell_type === this.cell_type) {
|
||||
if (data.source !== undefined) {
|
||||
this.set_text(data.source);
|
||||
// make this value the starting point, so that we can only undo
|
||||
// to this state, instead of a blank cell
|
||||
this.code_mirror.clearHistory();
|
||||
this.set_rendered(data.rendered || '');
|
||||
this.rendered = false;
|
||||
this.render();
|
||||
|
@ -43,11 +43,11 @@ IPython.utils = (function (IPython) {
|
||||
ansi_colormap = {
|
||||
"30":"ansiblack", "31":"ansired",
|
||||
"32":"ansigreen", "33":"ansiyellow",
|
||||
"34":"ansiblue", "35":"ansipurple","36":"ansicyan",
|
||||
"34":"ansiblue", "35":"ansipurple","36":"ansicyan",
|
||||
"37":"ansigrey", "01":"ansibold"
|
||||
};
|
||||
|
||||
// Transform ANI color escape codes into HTML <span> tags with css
|
||||
// Transform ANSI color escape codes into HTML <span> tags with css
|
||||
// classes listed in the above ansi_colormap object. The actual color used
|
||||
// are set in the css file.
|
||||
function fixConsole(txt) {
|
||||
@ -57,8 +57,6 @@ IPython.utils = (function (IPython) {
|
||||
var cmds = [];
|
||||
var opener = "";
|
||||
var closer = "";
|
||||
// \r does nothing, so shouldn't be included
|
||||
txt = txt.replace('\r', '');
|
||||
while (re.test(txt)) {
|
||||
var cmds = txt.match(re)[1].split(";");
|
||||
closer = opened?"</span>":"";
|
||||
@ -74,6 +72,16 @@ IPython.utils = (function (IPython) {
|
||||
return txt;
|
||||
}
|
||||
|
||||
// Remove chunks that should be overridden by the effect of
|
||||
// carriage return characters
|
||||
function fixCarriageReturn(txt) {
|
||||
tmp = txt;
|
||||
do {
|
||||
txt = tmp;
|
||||
tmp = txt.replace(/^.*\r(?!\n)/gm, '');
|
||||
} while (tmp.length < txt.length);
|
||||
return txt;
|
||||
}
|
||||
|
||||
grow = function(element) {
|
||||
// Grow the cell by hand. This is used upon reloading from JSON, when the
|
||||
@ -118,12 +126,24 @@ IPython.utils = (function (IPython) {
|
||||
DOWN : 40,
|
||||
};
|
||||
|
||||
|
||||
points_to_pixels = function (points) {
|
||||
// A reasonably good way of converting between points and pixels.
|
||||
var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
|
||||
$(body).append(test);
|
||||
var pixel_per_point = test.width()/10000;
|
||||
test.remove();
|
||||
return Math.floor(points*pixel_per_point);
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
uuid : uuid,
|
||||
fixConsole : fixConsole,
|
||||
keycodes : keycodes,
|
||||
grow : grow,
|
||||
fixCarriageReturn : fixCarriageReturn,
|
||||
points_to_pixels : points_to_pixels
|
||||
};
|
||||
|
||||
}(IPython));
|
||||
|
||||
|
@ -26,6 +26,7 @@ itself from the command line. There are two ways of running this script:
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# Stdlib
|
||||
import glob
|
||||
import os
|
||||
import os.path as path
|
||||
import signal
|
||||
@ -148,16 +149,18 @@ have = {}
|
||||
|
||||
have['curses'] = test_for('_curses')
|
||||
have['matplotlib'] = test_for('matplotlib')
|
||||
have['numpy'] = test_for('numpy')
|
||||
have['pexpect'] = test_for('IPython.external.pexpect')
|
||||
have['pymongo'] = test_for('pymongo')
|
||||
have['pygments'] = test_for('pygments')
|
||||
have['wx'] = test_for('wx')
|
||||
have['wx.aui'] = test_for('wx.aui')
|
||||
have['qt'] = test_for('IPython.external.qt')
|
||||
have['rpy2'] = test_for('rpy2')
|
||||
have['sqlite3'] = test_for('sqlite3')
|
||||
have['cython'] = test_for('Cython')
|
||||
|
||||
have['oct2py'] = test_for('oct2py')
|
||||
have['tornado'] = test_for('tornado.version_info', (2,1,0), callback=None)
|
||||
have['wx'] = test_for('wx')
|
||||
have['wx.aui'] = test_for('wx.aui')
|
||||
|
||||
if os.name == 'nt':
|
||||
min_zmq = (2,1,7)
|
||||
@ -234,6 +237,11 @@ def make_exclude():
|
||||
exclusions.append(ipjoin('core', 'history'))
|
||||
if not have['wx']:
|
||||
exclusions.append(ipjoin('lib', 'inputhookwx'))
|
||||
|
||||
# FIXME: temporarily disable autoreload tests, as they can produce
|
||||
# spurious failures in subsequent tests (cythonmagic).
|
||||
exclusions.append(ipjoin('extensions', 'autoreload'))
|
||||
exclusions.append(ipjoin('extensions', 'tests', 'test_autoreload'))
|
||||
|
||||
# We do this unconditionally, so that the test suite doesn't import
|
||||
# gtk, changing the default encoding and masking some unicode bugs.
|
||||
@ -277,9 +285,17 @@ def make_exclude():
|
||||
exclusions.extend([ipjoin('extensions', 'cythonmagic')])
|
||||
exclusions.extend([ipjoin('extensions', 'tests', 'test_cythonmagic')])
|
||||
|
||||
if not have['oct2py']:
|
||||
exclusions.extend([ipjoin('extensions', 'octavemagic')])
|
||||
exclusions.extend([ipjoin('extensions', 'tests', 'test_octavemagic')])
|
||||
|
||||
if not have['tornado']:
|
||||
exclusions.append(ipjoin('frontend', 'html'))
|
||||
|
||||
if not have['rpy2'] or not have['numpy']:
|
||||
exclusions.append(ipjoin('extensions', 'rmagic'))
|
||||
exclusions.append(ipjoin('extensions', 'tests', 'test_rmagic'))
|
||||
|
||||
# This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
|
||||
if sys.platform == 'win32':
|
||||
exclusions = [s.replace('\\','\\\\') for s in exclusions]
|
||||
@ -287,8 +303,11 @@ def make_exclude():
|
||||
# check for any exclusions that don't seem to exist:
|
||||
parent, _ = os.path.split(get_ipython_package_dir())
|
||||
for exclusion in exclusions:
|
||||
if exclusion.endswith(('deathrow', 'quarantine')):
|
||||
# ignore deathrow/quarantine, which exist in dev, but not install
|
||||
continue
|
||||
fullpath = pjoin(parent, exclusion)
|
||||
if not os.path.exists(fullpath) and not os.path.exists(fullpath + '.py'):
|
||||
if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
|
||||
warn("Excluding nonexistent file: %r\n" % exclusion)
|
||||
|
||||
return exclusions
|
||||
|
@ -20,7 +20,7 @@ from subprocess import Popen, PIPE
|
||||
import nose.tools as nt
|
||||
|
||||
from IPython.testing import decorators as dec
|
||||
from IPython.utils.io import Tee
|
||||
from IPython.utils.io import Tee, capture_output
|
||||
from IPython.utils.py3compat import doctest_refactor_print
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
@ -73,3 +73,13 @@ def test_io_init():
|
||||
# __class__ is a reference to the class object in Python 3, so we can't
|
||||
# just test for string equality.
|
||||
assert 'IPython.utils.io.IOStream' in classname, classname
|
||||
|
||||
def test_capture_output():
|
||||
"""capture_output() context works"""
|
||||
|
||||
with capture_output() as io:
|
||||
print 'hi, stdout'
|
||||
print >> sys.stderr, 'hi, stderr'
|
||||
|
||||
nt.assert_equals(io.stdout, 'hi, stdout\n')
|
||||
nt.assert_equals(io.stderr, 'hi, stderr\n')
|
||||
|
Loading…
Reference in New Issue
Block a user