* wrap/python/tests/ipnbdoctest.py: Skip if IPython is missing.
This commit is contained in:
parent
3bf3d2c8a1
commit
72c7ad9fcd
1 changed files with 32 additions and 20 deletions
|
|
@ -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(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue