##// END OF EJS Templates
Place customized doctests in a separate module.
chebee7i -
Show More
@@ -0,0 +1,92 b''
1 """
2 Module containing custom doctests.
3
4 """
5
6 def str_to_array(s):
7 """
8 Simplistic converter of strings from repr to float NumPy arrays.
9
10 If the repr representation has ellipsis in it, then this will fail.
11
12 Parameters
13 ----------
14 s : str
15 The repr version of a NumPy array.
16
17 Examples
18 --------
19 >>> s = "array([ 0.3, inf, nan])"
20 >>> a = str_to_array(s)
21
22 """
23 import numpy as np
24
25 # Need to make sure eval() knows about inf and nan.
26 # This also assumes default printoptions for NumPy.
27 from numpy import inf, nan
28
29 if s.startswith(u'array'):
30 # Remove array( and )
31 s = s[6:-1]
32
33 if s.startswith(u'['):
34 a = np.array(eval(s), dtype=float)
35 else:
36 # Assume its a regular float. Force 1D so we can index into it.
37 a = np.atleast_1d(float(s))
38 return a
39
40 def float_doctest(sphinx_shell, args, input_lines, found, submitted):
41 """
42 Doctest which allow the submitted output to vary slightly from the input.
43
44 Here is how it might appear in an rst file:
45
46 .. code-block:: rst
47
48 .. ipython::
49
50 @doctest float
51 In [1]: 0.1 + 0.2
52 Out[1]: 0.3
53
54 """
55 import numpy as np
56
57 if len(args) == 2:
58 rtol = 1e-05
59 atol = 1e-08
60 else:
61 # Both must be specified if any are specified.
62 try:
63 rtol = float(args[2])
64 atol = float(args[3])
65 except IndexError:
66 e = ("Both `rtol` and `atol` must be specified "
67 "if either are specified: {0}".format(args))
68 raise IndexError(e)
69
70 try:
71 submitted = str_to_array(submitted)
72 found = str_to_array(found)
73 except:
74 # For example, if the array is huge and there are ellipsis in it.
75 error = True
76 else:
77 found_isnan = np.isnan(found)
78 submitted_isnan = np.isnan(submitted)
79 error = not np.allclose(found_isnan, submitted_isnan)
80 error |= not np.allclose(found[~found_isnan],
81 submitted[~submitted_isnan],
82 rtol=rtol, atol=atol)
83
84 if error:
85 e = ('doctest float comparison failure for input_lines={0} with '
86 'found_output={1} and submitted '
87 'output="{2}"'.format(input_lines, repr(found), repr(submitted)) )
88 raise RuntimeError(e)
89
90 doctests = {
91 'float': float_doctest,
92 }
@@ -136,27 +136,6 b' COMMENT, INPUT, OUTPUT = range(3)'
136 #-----------------------------------------------------------------------------
136 #-----------------------------------------------------------------------------
137 # Functions and class declarations
137 # Functions and class declarations
138 #-----------------------------------------------------------------------------
138 #-----------------------------------------------------------------------------
139 def str_to_array(s):
140 """
141 Simplistic converter of strings from repr to float NumPy arrays.
142
143 """
144 import numpy as np
145
146 # Handle infs (assumes default printoptions for NumPy)
147 s = s.replace(u'inf', u'np.inf')
148 s = s.replace(u'nan', u'np.nan')
149
150 if s.startswith(u'array'):
151 # Remove array( and )
152 s = s[6:-1]
153
154 if s.startswith(u'['):
155 a = np.array(eval(s), dtype=float)
156 else:
157 # Assume its a regular float. Force 1D so we can index into it.
158 a = np.atleast_1d(float(s))
159 return a
160
139
161 def block_parser(part, rgxin, rgxout, fmtin, fmtout):
140 def block_parser(part, rgxin, rgxout, fmtin, fmtout):
162 """
141 """
@@ -252,6 +231,7 b' def block_parser(part, rgxin, rgxout, fmtin, fmtout):'
252
231
253 return block
232 return block
254
233
234
255 class EmbeddedSphinxShell(object):
235 class EmbeddedSphinxShell(object):
256 """An embedded IPython instance to run inside Sphinx"""
236 """An embedded IPython instance to run inside Sphinx"""
257
237
@@ -355,7 +335,6 b' class EmbeddedSphinxShell(object):'
355 image_directive = '\n'.join(imagerows)
335 image_directive = '\n'.join(imagerows)
356 return image_file, image_directive
336 return image_file, image_directive
357
337
358
359 # Callbacks for each type of token
338 # Callbacks for each type of token
360 def process_input(self, data, input_prompt, lineno):
339 def process_input(self, data, input_prompt, lineno):
361 """Process data block for INPUT token."""
340 """Process data block for INPUT token."""
@@ -452,8 +431,7 b' class EmbeddedSphinxShell(object):'
452 'output="{2}"'.format(input_lines, found, submitted) )
431 'output="{2}"'.format(input_lines, found, submitted) )
453 raise RuntimeError(e)
432 raise RuntimeError(e)
454 else:
433 else:
455 self.specialized_doctest(decorator, input_lines,
434 self.custom_doctest(decorator, input_lines, found, submitted)
456 found, submitted)
457
435
458 def process_comment(self, data):
436 def process_comment(self, data):
459 """Process data fPblock for COMMENT token."""
437 """Process data fPblock for COMMENT token."""
@@ -594,56 +572,21 b' class EmbeddedSphinxShell(object):'
594
572
595 return output
573 return output
596
574
597 def specialized_doctest(self, decorator, input_lines, found, submitted):
575 def custom_doctest(self, decorator, input_lines, found, submitted):
598 """
576 """
599 Perform a specialized doctest.
577 Perform a specialized doctest.
600
578
601 """
579 """
602 # Requires NumPy
580 from .custom_doctests import doctests
603 import numpy as np
604
605 valid_types = set(['float'])
606
581
607 args = decorator.split()
582 args = decorator.split()
608
609 doctest_type = args[1]
583 doctest_type = args[1]
610 if doctest_type not in valid_types:
584 if doctest_type in doctests:
585 doctests[doctest_type](self, args, input_lines, found, submitted)
586 else:
611 e = "Invalid option to @doctest: {0}".format(doctest_type)
587 e = "Invalid option to @doctest: {0}".format(doctest_type)
612 raise Exception(e)
588 raise Exception(e)
613
589
614 if len(args) == 2:
615 rtol = 1e-05
616 atol = 1e-08
617 else:
618 # Both must be specified if any are specified.
619 try:
620 rtol = float(args[2])
621 atol = float(args[3])
622 except IndexError:
623 e = ("Both `rtol` and `atol` must be specified "
624 "if either are specified: {0}".format(args))
625 raise IndexError(e)
626
627 try:
628 submitted = str_to_array(submitted)
629 found = str_to_array(found)
630 except:
631 # For example, if the array is huge and there are ellipsis in it.
632 error = True
633 else:
634 found_isnan = np.isnan(found)
635 submitted_isnan = np.isnan(submitted)
636 error = not np.allclose(found_isnan, submitted_isnan)
637 error |= not np.allclose(found[~found_isnan],
638 submitted[~submitted_isnan],
639 rtol=rtol, atol=atol)
640
641 if error:
642 e = ('doctest float comparison failure for input_lines="{0}" with '
643 'found_output="{1}" and submitted '
644 'output="{2}"'.format(input_lines, found, submitted) )
645 raise RuntimeError(e)
646
647
590
648 class IPythonDirective(Directive):
591 class IPythonDirective(Directive):
649
592
General Comments 0
You need to be logged in to leave comments. Login now