Skip to content


Subversion checkout URL

You can clone with
Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

230 lines (183 sloc) 7.354 kB
"""Run doctests on a single class or function, and report for IPython Notebook.
Decorate each function or class to be tested with ``ipython_doctester.test``.
If you want to turn off automatic testing but don't want to take the @test
decorators off, set ipython_doctester.run_tests = False.
Note: It's easy to cheat by simply deleting or changing the doctest. That's
OK, cheating is learning, too.
If you want to track students' progress through a notebook in a
classroom setting, you can; see
for instructions.
Developed for the Dayton Python Workshop:
import IPython
import doctest
import cgi
import inspect
import sys
import os
import os.path
import requests
from IPython.core.displaypub import publish_display_data
from IPython.zmq import displayhook as zmq_displayhook
except ImportError:
# zmq in kernel in Ipython 1.x serie
from IPython.kernel.zmq import displayhook as zmq_displayhook
__version__ = '0.3.0'
finder = doctest.DocTestFinder()
docent_url = ''
doctest_path = './doctests'
"""Set these per session, as desired."""
run_tests = True
verbose = False # ``True`` causes the result table to print,
# even for successes
"""Set these if desired to track student progress
See that page for more instructions."""
student_name = None
workshop_name = None
def running_from_notebook():
return isinstance(sys.displayhook, zmq_displayhook.ZMQShellDisplayHook)
class Reporter(object):
html = running_from_notebook()
def __init__(self):
self.failed = False
self.examples = []
self.txt = ''
example_template = ('<tr><td><code><pre>%s</pre></code></td>'
'<td><pre style="color:%s">%s</pre></td></tr>')
fail_template = """
<p><span style="color:red;">Oops!</span> Not quite there yet...</p>
success_template = """
<p style="color:green;font-size:250%;font-weight=bold">Success!</p>
def trap_txt(self, txt):
self.txt += txt
def publish(self):
# if self.html:
# IPython.core.displaypub.publish_html(self._repr_html_())
# else:
# IPython.core.displaypub.publish_pretty(self.txt)
if self.html:
{'text/html': self._repr_html_()})
{'text/plain': self.txt})
def _repr_html_(self):
result = self.fail_template if self.failed else self.success_template
if verbose or self.failed:
examples = '\n '.join(self.example_template %
e.color, cgi.escape(
)for e in self.examples)
result += ("""
+ examples + """
return result
reporter = Reporter()
class Runner(doctest.DocTestRunner):
def _or_nothing(self, x):
if x in (None, ''):
return 'Nothing'
elif hasattr(x, 'strip') and x.strip() == '':
return '<BLANKLINE>'
return x
def report_failure(self, out, test, example, got): = self._or_nothing(got)
example.want = self._or_nothing(example.want)
example.color = 'red'
reporter.failed = True
return doctest.DocTestRunner.report_failure(self, out, test, example,
def report_success(self, out, test, example, got): = self._or_nothing(got)
example.want = self._or_nothing(example.want)
example.color = 'green'
return doctest.DocTestRunner.report_success(self, out, test, example, got)
def report_unexpected_exception(self, out, test, example, exc_info):
reporter.failed = True
trim = len(reporter.txt)
result = doctest.DocTestRunner.report_unexpected_exception(
self, out, test, example, exc_info) = reporter.txt[trim:].split('Exception raised:')[1]
example.want = self._or_nothing(example.want)
example.color = 'red'
return result
runner = Runner()
finder = doctest.DocTestFinder()
class IPythonDoctesterException(Exception):
def _repr_html_(self):
return '<pre>\n%s\n</pre>' % self.txt
class NoTestsException(IPythonDoctesterException):
txt = """
OOPS! We expected to find a doctest -
a string immediately after the function definition, looking something like
def do_something():
>>> do_something()
'did something'
... but it wasn't there. Did you insert code between the function definition
and the doctest?
class NoStudentNameException(IPythonDoctesterException):
txt = """
OOPS! We need you to set the ipython_doctester.student_name variable;
please look for it (probably in the first cell in this worksheet) and
enter your name, like
ipython_doctester.student_name = 'Catherine'
... then hit Shift+Enter to execute that cell, then come back here to
execute this one.
def testobj(func):
tests = finder.find(func)
if (not tests) or (not tests[0].examples):
doctest_filename = os.path.join(os.curdir, doctest_path, func.__name__
) + '.txt'
with open(doctest_filename) as infile:
func.__doc__ = (func.__doc__ or "") + "\n" +
except IOError:
raise NoTestsException
tests = finder.find(func)
if not tests:
raise NoTestsException
if workshop_name and not student_name:
raise NoStudentNameException()
globs = {} # TODO: get the ipython globals?
globs[func.__name__] = func
globs['reporter'] = reporter
for t in tests:
t.globs = globs.copy(), out=reporter.trap_txt)
if workshop_name:
payload = dict(function_name=func.__name__,
student_name=student_name) + '/record', data=payload)
return reporter
def test(func):
if run_tests:
result = testobj(func)
except (NoStudentNameException, NoTestsException) as e:
return func
Jump to Line
Something went wrong with that request. Please try again.