##// END OF EJS Templates
Provide helpful error messages on doctest failures.
chebee7i -
Show More
@@ -81,10 +81,26 b' def float_doctest(sphinx_shell, args, input_lines, found, submitted):'
81 submitted[~submitted_isnan],
81 submitted[~submitted_isnan],
82 rtol=rtol, atol=atol)
82 rtol=rtol, atol=atol)
83
83
84 TAB = ' ' * 4
85 directive = sphinx_shell.directive
86 if directive is None:
87 source = 'Unavailable'
88 content = 'Unavailable'
89 else:
90 source = directive.state.document.current_source
91 # Add tabs and make into a single string.
92 content = '\n'.join([TAB + line for line in directive.content])
93
84 if error:
94 if error:
85 e = ('doctest float comparison failure for input_lines={0} with '
95
86 'found_output={1} and submitted '
96 e = ('doctest float comparison failure\n\n'
87 'output="{2}"'.format(input_lines, repr(found), repr(submitted)) )
97 'Document source: {0}\n\n'
98 'Raw content: \n{1}\n\n'
99 'On input line(s):\n{TAB}{2}\n\n'
100 'we found output:\n{TAB}{3}\n\n'
101 'instead of the expected:\n{TAB}{4}\n\n')
102 e = e.format(source, content, '\n'.join(input_lines), repr(found),
103 repr(submitted), TAB=TAB)
88 raise RuntimeError(e)
104 raise RuntimeError(e)
89
105
90 doctests = {
106 doctests = {
@@ -155,7 +155,6 b' def block_parser(part, rgxin, rgxout, fmtin, fmtout):'
155 INPUT_LINE: the input as string (possibly multi-line)
155 INPUT_LINE: the input as string (possibly multi-line)
156 REST : any stdout generated by the input line (not OUTPUT)
156 REST : any stdout generated by the input line (not OUTPUT)
157
157
158
159 OUTPUT: the output string, possibly multi-line
158 OUTPUT: the output string, possibly multi-line
160
159
161 """
160 """
@@ -279,6 +278,9 b' class EmbeddedSphinxShell(object):'
279 self.is_doctest = False
278 self.is_doctest = False
280 self.is_suppress = False
279 self.is_suppress = False
281
280
281 # Optionally, provide more detailed information to shell.
282 self.directive = None
283
282 # on the first call to the savefig decorator, we'll import
284 # on the first call to the savefig decorator, we'll import
283 # pyplot as plt so we can make a call to the plt.gcf().savefig
285 # pyplot as plt so we can make a call to the plt.gcf().savefig
284 self._pyplot_imported = False
286 self._pyplot_imported = False
@@ -293,7 +295,7 b' class EmbeddedSphinxShell(object):'
293
295
294 def process_input_line(self, line, store_history=True):
296 def process_input_line(self, line, store_history=True):
295 """process the input, capturing stdout"""
297 """process the input, capturing stdout"""
296 #print "input='%s'"%self.input
298
297 stdout = sys.stdout
299 stdout = sys.stdout
298 splitter = self.IP.input_splitter
300 splitter = self.IP.input_splitter
299 try:
301 try:
@@ -337,11 +339,14 b' class EmbeddedSphinxShell(object):'
337
339
338 # Callbacks for each type of token
340 # Callbacks for each type of token
339 def process_input(self, data, input_prompt, lineno):
341 def process_input(self, data, input_prompt, lineno):
340 """Process data block for INPUT token."""
342 """
343 Process data block for INPUT token.
344
345 """
341 decorator, input, rest = data
346 decorator, input, rest = data
342 image_file = None
347 image_file = None
343 image_directive = None
348 image_directive = None
344 #print 'INPUT:', data # dbg
349
345 is_verbatim = decorator=='@verbatim' or self.is_verbatim
350 is_verbatim = decorator=='@verbatim' or self.is_verbatim
346 is_doctest = (decorator is not None and \
351 is_doctest = (decorator is not None and \
347 decorator.startswith('@doctest')) or self.is_doctest
352 decorator.startswith('@doctest')) or self.is_doctest
@@ -403,22 +408,41 b' class EmbeddedSphinxShell(object):'
403 self.cout.truncate(0)
408 self.cout.truncate(0)
404 return (ret, input_lines, output, is_doctest, decorator, image_file,
409 return (ret, input_lines, output, is_doctest, decorator, image_file,
405 image_directive)
410 image_directive)
406 #print 'OUTPUT', output # dbg
411
407
412
408 def process_output(self, data, output_prompt,
413 def process_output(self, data, output_prompt,
409 input_lines, output, is_doctest, decorator, image_file):
414 input_lines, output, is_doctest, decorator, image_file):
410 """Process data block for OUTPUT token."""
415 """
416 Process data block for OUTPUT token.
417
418 """
419 TAB = ' ' * 4
420
411 if is_doctest and output is not None:
421 if is_doctest and output is not None:
412
422
413 found = output
423 found = output
414 found = found.strip()
424 found = found.strip()
415 submitted = data.strip()
425 submitted = data.strip()
416
426
427 if self.directive is None:
428 source = 'Unavailable'
429 content = 'Unavailable'
430 else:
431 source = self.directive.state.document.current_source
432 content = self.directive.content
433 # Add tabs and join into a single string.
434 content = '\n'.join([TAB + line for line in content])
435
417 # Make sure the output contains the output prompt.
436 # Make sure the output contains the output prompt.
418 ind = found.find(output_prompt)
437 ind = found.find(output_prompt)
419 if ind < 0:
438 if ind < 0:
420 e = ('output prompt="{0}" does '
439 e = ('output does not contain output prompt\n\n'
421 'not match out line={1}'.format(output_prompt, found))
440 'Document source: {0}\n\n'
441 'Raw content: \n{1}\n\n'
442 'Input line(s):\n{TAB}{2}\n\n'
443 'Output line(s):\n{TAB}{3}\n\n')
444 e = e.format(source, content, '\n'.join(input_lines),
445 repr(found), TAB=TAB)
422 raise RuntimeError(e)
446 raise RuntimeError(e)
423 found = found[len(output_prompt):].strip()
447 found = found[len(output_prompt):].strip()
424
448
@@ -426,9 +450,14 b' class EmbeddedSphinxShell(object):'
426 if decorator.strip() == '@doctest':
450 if decorator.strip() == '@doctest':
427 # Standard doctest
451 # Standard doctest
428 if found != submitted:
452 if found != submitted:
429 e = ('doctest failure for input_lines="{0}" with '
453 e = ('doctest failure\n\n'
430 'found_output="{1}" and submitted '
454 'Document source: {0}\n\n'
431 'output="{2}"'.format(input_lines, found, submitted) )
455 'Raw content: \n{1}\n\n'
456 'On input line(s):\n{TAB}{2}\n\n'
457 'we found output:\n{TAB}{3}\n\n'
458 'instead of the expected:\n{TAB}{4}\n\n')
459 e = e.format(source, content, '\n'.join(input_lines),
460 repr(found), repr(submitted), TAB=TAB)
432 raise RuntimeError(e)
461 raise RuntimeError(e)
433 else:
462 else:
434 self.custom_doctest(decorator, input_lines, found, submitted)
463 self.custom_doctest(decorator, input_lines, found, submitted)
@@ -653,6 +682,9 b' class IPythonDirective(Directive):'
653 # setting its backend since exec_lines might import pylab.
682 # setting its backend since exec_lines might import pylab.
654 self.shell = EmbeddedSphinxShell(exec_lines)
683 self.shell = EmbeddedSphinxShell(exec_lines)
655
684
685 # Store IPython directive to enable better error messages
686 self.shell.directive = self
687
656 # reset the execution count if we haven't processed this doc
688 # reset the execution count if we haven't processed this doc
657 #NOTE: this may be borked if there are multiple seen_doc tmp files
689 #NOTE: this may be borked if there are multiple seen_doc tmp files
658 #check time stamp?
690 #check time stamp?
@@ -676,7 +708,6 b' class IPythonDirective(Directive):'
676
708
677 return rgxin, rgxout, promptin, promptout
709 return rgxin, rgxout, promptin, promptout
678
710
679
680 def teardown(self):
711 def teardown(self):
681 # delete last bookmark
712 # delete last bookmark
682 self.shell.process_input_line('bookmark -d ipy_savedir',
713 self.shell.process_input_line('bookmark -d ipy_savedir',
@@ -702,7 +733,7 b' class IPythonDirective(Directive):'
702
733
703 parts = '\n'.join(self.content).split('\n\n')
734 parts = '\n'.join(self.content).split('\n\n')
704
735
705 lines = ['.. code-block:: ipython','']
736 lines = ['.. code-block:: ipython', '']
706 figures = []
737 figures = []
707
738
708 for part in parts:
739 for part in parts:
@@ -724,7 +755,9 b' class IPythonDirective(Directive):'
724 if debug:
755 if debug:
725 print('\n'.join(lines))
756 print('\n'.join(lines))
726 else:
757 else:
727 # This is what makes the lines appear in the final output.
758 # This has to do with input, not output. But if we comment
759 # these lines out, then no IPython code will appear in the
760 # final output.
728 self.state_machine.insert_input(
761 self.state_machine.insert_input(
729 lines, self.state_machine.input_lines.source(0))
762 lines, self.state_machine.input_lines.source(0))
730
763
General Comments 0
You need to be logged in to leave comments. Login now