diff --git a/ipython_doctester.py b/ipython_doctester.py index eb3f24c..092dc33 100644 --- a/ipython_doctester.py +++ b/ipython_doctester.py @@ -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 @@ -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.""" @@ -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 @@ -58,7 +62,7 @@ def __init__(self): """ success_template = """

Success!

- """ + """ def trap_txt(self, txt): self.txt += txt def publish(self): @@ -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 += """ @@ -80,7 +84,7 @@ def _repr_html_(self): """ return result - + reporter = Reporter() @@ -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) @@ -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 '
\n%s\n
' % 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 @@ -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):