Show More
@@ -74,28 +74,36 b' def ipfunc():' | |||||
74 | def ranfunc(): |
|
74 | def ranfunc(): | |
75 | """A function with some random output. |
|
75 | """A function with some random output. | |
76 |
|
76 | |||
|
77 | Normal inputs are verified as usual: | |||
77 | >>> 1+3 |
|
78 | >>> 1+3 | |
78 | junk goes here... #random |
|
79 | 4 | |
79 |
|
80 | |||
|
81 | But if you put '# random' in the output, it is ignored: | |||
80 | >>> 1+3 |
|
82 | >>> 1+3 | |
81 | 4 |
|
83 | junk goes here... # random | |
82 |
|
84 | |||
83 | >>> 1+2 |
|
85 | >>> 1+2 | |
84 | again, anything goes #random |
|
86 | again, anything goes #random | |
85 |
if multiline, the random mark is only needed |
|
87 | if multiline, the random mark is only needed once. | |
86 |
|
88 | |||
87 |
>>> 1+ |
|
89 | >>> 1+2 | |
88 | 4 |
|
90 | You can also put the random marker at the end: | |
|
91 | # random | |||
|
92 | ||||
|
93 | >>> 1+2 | |||
|
94 | # random | |||
|
95 | .. or at the beginning. | |||
89 |
|
96 | |||
|
97 | More correct input is properly verified: | |||
90 | >>> ranfunc() |
|
98 | >>> ranfunc() | |
91 | 'ranfunc' |
|
99 | 'ranfunc' | |
92 | """ |
|
100 | """ | |
93 | return 'ranfunc' |
|
101 | return 'ranfunc' | |
94 |
|
102 | |||
95 |
|
103 | |||
96 |
if |
|
104 | if 0: | |
97 | def ranf2(): |
|
105 | def ranf2(): | |
98 |
"""A function whose examples'output are |
|
106 | """A function whose examples' output are completely ignored. | |
99 |
|
107 | |||
100 | Examples: |
|
108 | Examples: | |
101 |
|
109 |
@@ -184,33 +184,24 b' class DocTestFinder(doctest.DocTestFinder):' | |||||
184 | module. |
|
184 | module. | |
185 | """ |
|
185 | """ | |
186 | if module is None: |
|
186 | if module is None: | |
187 | #print '_fm C1' # dbg |
|
|||
188 | return True |
|
187 | return True | |
189 | elif inspect.isfunction(object): |
|
188 | elif inspect.isfunction(object): | |
190 | #print '_fm C2' # dbg |
|
|||
191 | return module.__dict__ is object.func_globals |
|
189 | return module.__dict__ is object.func_globals | |
192 | elif inspect.isbuiltin(object): |
|
190 | elif inspect.isbuiltin(object): | |
193 | #print '_fm C2-1' # dbg |
|
|||
194 | return module.__name__ == object.__module__ |
|
191 | return module.__name__ == object.__module__ | |
195 | elif inspect.isclass(object): |
|
192 | elif inspect.isclass(object): | |
196 | #print '_fm C3' # dbg |
|
|||
197 | return module.__name__ == object.__module__ |
|
193 | return module.__name__ == object.__module__ | |
198 | elif inspect.ismethod(object): |
|
194 | elif inspect.ismethod(object): | |
199 | # This one may be a bug in cython that fails to correctly set the |
|
195 | # This one may be a bug in cython that fails to correctly set the | |
200 | # __module__ attribute of methods, but since the same error is easy |
|
196 | # __module__ attribute of methods, but since the same error is easy | |
201 | # to make by extension code writers, having this safety in place |
|
197 | # to make by extension code writers, having this safety in place | |
202 | # isn't such a bad idea |
|
198 | # isn't such a bad idea | |
203 | #print '_fm C3-1' # dbg |
|
|||
204 | return module.__name__ == object.im_class.__module__ |
|
199 | return module.__name__ == object.im_class.__module__ | |
205 | elif inspect.getmodule(object) is not None: |
|
200 | elif inspect.getmodule(object) is not None: | |
206 | #print '_fm C4' # dbg |
|
|||
207 | #print 'C4 mod',module,'obj',object # dbg |
|
|||
208 | return module is inspect.getmodule(object) |
|
201 | return module is inspect.getmodule(object) | |
209 | elif hasattr(object, '__module__'): |
|
202 | elif hasattr(object, '__module__'): | |
210 | #print '_fm C5' # dbg |
|
|||
211 | return module.__name__ == object.__module__ |
|
203 | return module.__name__ == object.__module__ | |
212 | elif isinstance(object, property): |
|
204 | elif isinstance(object, property): | |
213 | #print '_fm C6' # dbg |
|
|||
214 | return True # [XX] no way not be sure. |
|
205 | return True # [XX] no way not be sure. | |
215 | else: |
|
206 | else: | |
216 | raise ValueError("object must be a class or function") |
|
207 | raise ValueError("object must be a class or function") | |
@@ -263,18 +254,28 b' class DocTestFinder(doctest.DocTestFinder):' | |||||
263 | globs, seen) |
|
254 | globs, seen) | |
264 |
|
255 | |||
265 |
|
256 | |||
266 | # second-chance checker; if the default comparison doesn't |
|
|||
267 | # pass, then see if the expected output string contains flags that |
|
|||
268 | # tell us to ignore the output |
|
|||
269 | class IPDoctestOutputChecker(doctest.OutputChecker): |
|
257 | class IPDoctestOutputChecker(doctest.OutputChecker): | |
|
258 | """Second-chance checker with support for random tests. | |||
|
259 | ||||
|
260 | If the default comparison doesn't pass, this checker looks in the expected | |||
|
261 | output string for flags that tell us to ignore the output. | |||
|
262 | """ | |||
|
263 | ||||
|
264 | random_re = re.compile(r'#\s*random') | |||
|
265 | ||||
270 | def check_output(self, want, got, optionflags): |
|
266 | def check_output(self, want, got, optionflags): | |
271 | #print '*** My Checker!' # dbg |
|
267 | """Check output, accepting special markers embedded in the output. | |
272 |
|
|
268 | ||
|
269 | If the output didn't pass the default validation but the special string | |||
|
270 | '#random' is included, we accept it.""" | |||
|
271 | ||||
|
272 | # Let the original tester verify first, in case people have valid tests | |||
|
273 | # that happen to have a comment saying '#random' embedded in. | |||
273 | ret = doctest.OutputChecker.check_output(self, want, got, |
|
274 | ret = doctest.OutputChecker.check_output(self, want, got, | |
274 | optionflags) |
|
275 | optionflags) | |
275 | if not ret: |
|
276 | if not ret and self.random_re.search(want): | |
276 | if "#random" in want: |
|
277 | #print >> sys.stderr, 'RANDOM OK:',want # dbg | |
277 |
|
|
278 | return True | |
278 |
|
279 | |||
279 | return ret |
|
280 | return ret | |
280 |
|
281 | |||
@@ -405,6 +406,8 b' class IPDocTestParser(doctest.DocTestParser):' | |||||
405 | _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP), |
|
406 | _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP), | |
406 | re.MULTILINE | re.VERBOSE) |
|
407 | re.MULTILINE | re.VERBOSE) | |
407 |
|
408 | |||
|
409 | _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL') | |||
|
410 | ||||
408 | def ip2py(self,source): |
|
411 | def ip2py(self,source): | |
409 | """Convert input IPython source into valid Python.""" |
|
412 | """Convert input IPython source into valid Python.""" | |
410 | out = [] |
|
413 | out = [] | |
@@ -452,7 +455,7 b' class IPDocTestParser(doctest.DocTestParser):' | |||||
452 | # IPExternalExamples are run out-of-process (via pexpect) so they |
|
455 | # IPExternalExamples are run out-of-process (via pexpect) so they | |
453 | # don't need any filtering (a real ipython will be executing them). |
|
456 | # don't need any filtering (a real ipython will be executing them). | |
454 | terms = list(self._EXAMPLE_RE_IP.finditer(string)) |
|
457 | terms = list(self._EXAMPLE_RE_IP.finditer(string)) | |
455 | if re.search(r'#\s*ipdoctest:\s*EXTERNAL',string): |
|
458 | if self._EXTERNAL_IP.search(string): | |
456 | #print '-'*70 # dbg |
|
459 | #print '-'*70 # dbg | |
457 | #print 'IPExternalExample, Source:\n',string # dbg |
|
460 | #print 'IPExternalExample, Source:\n',string # dbg | |
458 | #print '-'*70 # dbg |
|
461 | #print '-'*70 # dbg |
General Comments 0
You need to be logged in to leave comments.
Login now