Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 32 additions & 26 deletions ipython_doctester.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
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
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:

Developed for the Dayton Python Workshop:
https://openhatch.org/wiki/Dayton_Python_Workshop
catherine.devlin@gmail.com

Expand All @@ -24,11 +24,15 @@
import cgi
import inspect
import sys
import requests

try:
import requests
except ImportError:
requests = None

import IPython.zmq.displayhook

__version__ = '0.2.2'
finder = doctest.DocTestFinder()
docent_url = 'http://ipython-docent.appspot.com'

"""Set these per session, as desired."""
Expand All @@ -37,7 +41,7 @@
# even for successes

"""Set these if desired to track student progress
at http://ipython-docent.appspot.com/.
at http://ipython-docent.appspot.com/.
See that page for more instructions."""
student_name = None
workshop_name = None
Expand All @@ -58,7 +62,7 @@ def __init__(self):
"""
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):
Expand All @@ -69,8 +73,8 @@ def publish(self):
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),
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 += """
Expand All @@ -80,7 +84,7 @@ def _repr_html_(self):
"""
return result


reporter = Reporter()


Expand All @@ -103,7 +107,7 @@ def report_success(self, out, test, example, 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)
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)
Expand All @@ -114,45 +118,45 @@ def report_unexpected_exception(self, out, test, example, exc_info):
example.color = 'red'
reporter.examples.append(example)
return result


runner = Runner()
finder = doctest.DocTestFinder()

class IPythonDoctesterException(StandardError):
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 -
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?
and the doctest?
"""

class NoStudentNameException(IPythonDoctesterException):
txt = """
OOPS! We need you to set the ipython_doctester.student_name variable;
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 = {} # TODO: get the ipython globals?
globs = func.__globals__
reporter.__init__()
globs[func.__name__] = func
globs['reporter'] = reporter
Expand All @@ -161,13 +165,15 @@ def testobj(func):
runner.run(t, out=reporter.trap_txt)
reporter.publish()
if workshop_name:
payload = dict(function_name = func.__name__,
failure=reporter.failed,
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):
Expand Down