* wrap/python/tests/ipnbdoctest.py: Skip if IPython is missing.

This commit is contained in:
Alexandre Duret-Lutz 2015-03-08 10:50:17 +01:00
parent 3bf3d2c8a1
commit 72c7ad9fcd

View file

@ -4,7 +4,8 @@ simple example script for running and testing notebooks.
Usage: `ipnbdoctest.py foo.ipynb [bar.ipynb [...]]` Usage: `ipnbdoctest.py foo.ipynb [bar.ipynb [...]]`
Each cell is submitted to the kernel, and the outputs are compared with those stored in the notebook. Each cell is submitted to the kernel, and the outputs are compared
with those stored in the notebook.
""" """
from __future__ import print_function from __future__ import print_function
@ -21,6 +22,13 @@ except ImportError:
print('Python 3.x is needed to run this script.') print('Python 3.x is needed to run this script.')
sys.exit(77) sys.exit(77)
import imp
try:
imp.find_module('IPython')
except:
print('IPython is needed to run this script.')
sys.exit(77)
try: try:
from IPython.kernel import KernelManager from IPython.kernel import KernelManager
except ImportError: except ImportError:
@ -41,23 +49,24 @@ def compare_png(a64, b64):
def sanitize(s): def sanitize(s):
"""sanitize a string for comparison. """sanitize a string for comparison.
fix universal newlines, strip trailing newlines, and normalize likely random values (memory addresses and UUIDs) fix universal newlines, strip trailing newlines, and normalize likely
random values (memory addresses and UUIDs)
""" """
if not isinstance(s, str): if not isinstance(s, str):
return s return s
# normalize newline: # normalize newline:
s = s.replace('\r\n', '\n') s = s.replace('\r\n', '\n')
# ignore trailing newlines (but not space) # ignore trailing newlines (but not space)
s = s.rstrip('\n') s = s.rstrip('\n')
# normalize hex addresses: # normalize hex addresses:
s = re.sub(r'0x[a-f0-9]+', '0xFFFFFFFF', s) s = re.sub(r'0x[a-f0-9]+', '0xFFFFFFFF', s)
# normalize UUIDs: # normalize UUIDs:
s = re.sub(r'[a-f0-9]{8}(\-[a-f0-9]{4}){3}\-[a-f0-9]{12}', 'U-U-I-D', s) s = re.sub(r'[a-f0-9]{8}(\-[a-f0-9]{4}){3}\-[a-f0-9]{12}', 'U-U-I-D', s)
return s return s
@ -66,25 +75,27 @@ def consolidate_outputs(outputs):
data = defaultdict(list) data = defaultdict(list)
data['stdout'] = '' data['stdout'] = ''
data['stderr'] = '' data['stderr'] = ''
for out in outputs: for out in outputs:
if out.type == 'stream': if out.type == 'stream':
data[out.stream] += out.text data[out.stream] += out.text
elif out.type == 'pyerr': elif out.type == 'pyerr':
data['pyerr'] = dict(ename=out.ename, evalue=out.evalue) data['pyerr'] = dict(ename=out.ename, evalue=out.evalue)
else: else:
for key in ('png', 'svg', 'latex', 'html', 'javascript', 'text', 'jpeg',): for key in ('png', 'svg', 'latex', 'html',
'javascript', 'text', 'jpeg',):
if key in out: if key in out:
data[key].append(out[key]) data[key].append(out[key])
return data return data
def compare_outputs(test, ref, skip_compare=('png', 'traceback', 'latex', 'prompt_number')): def compare_outputs(test, ref, skip_cmp=('png', 'traceback',
'latex', 'prompt_number')):
for key in ref: for key in ref:
if key not in test: if key not in test:
print("missing key: %s != %s" % (test.keys(), ref.keys())) print("missing key: %s != %s" % (test.keys(), ref.keys()))
return False return False
elif key not in skip_compare and sanitize(test[key]) != sanitize(ref[key]): elif key not in skip_cmp and sanitize(test[key]) != sanitize(ref[key]):
print("mismatch %s:" % key) print("mismatch %s:" % key)
exp = ref[key] exp = ref[key]
eff = test[key] eff = test[key]
@ -104,7 +115,7 @@ def run_cell(shell, iopub, cell):
# wait for finish, maximum 20s # wait for finish, maximum 20s
shell.get_msg(timeout=20) shell.get_msg(timeout=20)
outs = [] outs = []
while True: while True:
try: try:
msg = iopub.get_msg(timeout=0.2) msg = iopub.get_msg(timeout=0.2)
@ -116,11 +127,11 @@ def run_cell(shell, iopub, cell):
elif msg_type == 'clear_output': elif msg_type == 'clear_output':
outs = [] outs = []
continue continue
content = msg['content'] content = msg['content']
# print msg_type, content # print msg_type, content
out = NotebookNode(output_type=msg_type) out = NotebookNode(output_type=msg_type)
if msg_type == 'stream': if msg_type == 'stream':
out.stream = content['name'] out.stream = content['name']
out.text = content['data'] out.text = content['data']
@ -139,14 +150,15 @@ def run_cell(shell, iopub, cell):
out.traceback = content['traceback'] out.traceback = content['traceback']
else: else:
print("unhandled iopub msg:", msg_type) print("unhandled iopub msg:", msg_type)
outs.append(out) outs.append(out)
return outs return outs
def test_notebook(nb): def test_notebook(nb):
km = KernelManager() km = KernelManager()
km.start_kernel(extra_arguments=['--pylab=inline'], stderr=open(os.devnull, 'w')) km.start_kernel(extra_arguments=['--pylab=inline'],
stderr=open(os.devnull, 'w'))
try: try:
kc = km.client() kc = km.client()
kc.start_channels() kc.start_channels()
@ -157,7 +169,7 @@ def test_notebook(nb):
kc.start_channels() kc.start_channels()
iopub = kc.sub_channel iopub = kc.sub_channel
shell = kc.shell_channel shell = kc.shell_channel
# run %pylab inline, because some notebooks assume this # run %pylab inline, because some notebooks assume this
# even though they shouldn't # even though they shouldn't
shell.execute("pass") shell.execute("pass")
@ -167,7 +179,7 @@ def test_notebook(nb):
iopub.get_msg(timeout=1) iopub.get_msg(timeout=1)
except Empty: except Empty:
break break
successes = 0 successes = 0
failures = 0 failures = 0
errors = 0 errors = 0
@ -182,7 +194,7 @@ def test_notebook(nb):
print(cell.input) print(cell.input)
errors += 1 errors += 1
continue continue
failed = False failed = False
if len(outs) != len(cell.outputs): if len(outs) != len(cell.outputs):
print("output length mismatch (expected {}, got {})".format( print("output length mismatch (expected {}, got {})".format(