ktongue's picture
download
raw
6.32 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
http://ipython-docent.appspot.com/
for instructions.
Developed for the Dayton Python Workshop:
https://openhatch.org/wiki/Dayton_Python_Workshop
catherine.devlin@gmail.com
"""
import doctest
import cgi
import inspect
import sys
try:
import requests
except ImportError:
requests = None
import IPython.zmq.displayhook
__version__ = '0.2.2'
docent_url = 'http://ipython-docent.appspot.com'
"""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
at http://ipython-docent.appspot.com/.
See that page for more instructions."""
student_name = None
workshop_name = None
def running_from_notebook():
return isinstance(sys.displayhook, IPython.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>%s</pre></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)
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 %
(cgi.escape(e.source), cgi.escape(e.want),
e.color, cgi.escape(e.got)
)for e in self.examples)
result += """
<table>
<tr><th>Tried</th><th>Expected</th><th>Got</th></tr>""" + examples + """
</table>
"""
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):
example.got = self._or_nothing(got)
example.want = self._or_nothing(example.want)
example.color = 'red'
reporter.examples.append(example)
reporter.failed = True
return doctest.DocTestRunner.report_failure(self, out, test, example, got)
def report_success(self, out, test, example, got):
example.got = self._or_nothing(got)
example.want = self._or_nothing(example.want)
example.color = 'green'
reporter.examples.append(example)
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)
example.got = reporter.txt[trim:].split('Exception raised:')[1]
example.want = self._or_nothing(example.want)
example.color = 'red'
reporter.examples.append(example)
return result
runner = Runner()
finder = doctest.DocTestFinder()
class IPythonDoctesterException(StandardError):
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:
raise NoTestsException
if workshop_name and not student_name:
raise NoStudentNameException()
globs = func.__globals__
reporter.__init__()
globs[func.__name__] = func
globs['reporter'] = reporter
for t in tests:
t.globs = globs.copy()
runner.run(t, out=reporter.trap_txt)
reporter.publish()
if workshop_name:
if not requests:
raise ImportError("The requests module is required to upload results.")
payload = dict(function_name = func.__name__,
failure=reporter.failed,
source=inspect.getsource(func),
workshop_name = workshop_name,
student_name = student_name)
requests.post(docent_url+'/record', data=payload)
return reporter
def report_error(e):
if running_from_notebook():
IPython.core.displaypub.publish_html(e._repr_html_())
else:
IPython.core.displaypub.publish_pretty(e.txt)
def test(func):
if run_tests:
try:
result = testobj(func)
except (NoStudentNameException, NoTestsException) as e:
report_error(e)
return func

Xet Storage Details

Size:
6.32 kB
·
Xet hash:
3654dd17e3b492a0668d071921a80110a764051a1f8f44d7d9f5d6a6aa1a528e

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.