Show More
@@ -3,7 +3,8 b' PREFIX=~/usr/local' | |||||
3 | PREFIX=~/tmp/local |
|
3 | PREFIX=~/tmp/local | |
4 |
|
4 | |||
5 | NOSE0=nosetests -vs --with-doctest --doctest-tests --detailed-errors |
|
5 | NOSE0=nosetests -vs --with-doctest --doctest-tests --detailed-errors | |
6 |
NOSE=nosetests -vvs --with-ipdoctest --doctest-tests --doctest-extension=txt |
|
6 | NOSE=nosetests -vvs --with-ipdoctest --doctest-tests --doctest-extension=txt \ | |
|
7 | --detailed-errors | |||
7 |
|
|
8 | ||
8 | SRC=ipdoctest.py setup.py ../decorators.py |
|
9 | SRC=ipdoctest.py setup.py ../decorators.py | |
9 |
|
10 | |||
@@ -31,30 +32,38 b' deb: plugin dtexample.py' | |||||
31 |
|
32 | |||
32 | # IPython tests |
|
33 | # IPython tests | |
33 | deco: |
|
34 | deco: | |
34 |
$(NOSE0) |
|
35 | $(NOSE0) IPython.testing.decorators | |
35 |
|
36 | |||
36 | magic: plugin |
|
37 | magic: plugin | |
37 |
$(NOSE) |
|
38 | $(NOSE) IPython.Magic | |
38 |
|
39 | |||
39 | ipipe: plugin |
|
40 | ipipe: plugin | |
40 |
$(NOSE) |
|
41 | $(NOSE) IPython.Extensions.ipipe | |
41 |
|
42 | |||
42 | iplib: plugin |
|
43 | iplib: plugin | |
43 |
$(NOSE) |
|
44 | $(NOSE) IPython.iplib | |
44 |
|
45 | |||
45 | strd: plugin |
|
46 | strd: plugin | |
46 | nosetests -vs --with-doctest --doctest-tests IPython.strdispatch |
|
|||
47 | $(NOSE) IPython.strdispatch |
|
47 | $(NOSE) IPython.strdispatch | |
48 |
|
48 | |||
|
49 | engine: plugin | |||
|
50 | $(NOSE) IPython.kernel | |||
|
51 | ||||
|
52 | tf: plugin | |||
|
53 | $(NOSE) IPython.config.traitlets | |||
|
54 | ||||
49 | # All of ipython itself |
|
55 | # All of ipython itself | |
50 | ipython: plugin |
|
56 | ipython: plugin | |
51 | $(NOSE) IPython |
|
57 | $(NOSE) IPython | |
52 |
|
58 | |||
|
59 | ||||
53 | # Combined targets |
|
60 | # Combined targets | |
54 | sr: rtest strd |
|
61 | sr: rtest strd | |
55 |
|
62 | |||
56 | base: dtest rtest test strd deco |
|
63 | base: dtest rtest test strd deco | |
57 |
|
64 | |||
|
65 | quick: base iplib ipipe | |||
|
66 | ||||
58 | all: base ipython |
|
67 | all: base ipython | |
59 |
|
68 | |||
60 | # Main plugin and cleanup |
|
69 | # Main plugin and cleanup |
@@ -13,21 +13,6 b' Limitations:' | |||||
13 | '_34==True', for example). For IPython tests run via an external process the |
|
13 | '_34==True', for example). For IPython tests run via an external process the | |
14 | prompt numbers may be different, and IPython tests run as normal python code |
|
14 | prompt numbers may be different, and IPython tests run as normal python code | |
15 | won't even have these special _NN variables set at all. |
|
15 | won't even have these special _NN variables set at all. | |
16 |
|
||||
17 | - IPython functions that produce output as a side-effect of calling a system |
|
|||
18 | process (e.g. 'ls') can be doc-tested, but they must be handled in an |
|
|||
19 | external IPython process. Such doctests must be tagged with: |
|
|||
20 |
|
||||
21 | # ipdoctest: EXTERNAL |
|
|||
22 |
|
||||
23 | so that the testing machinery handles them differently. Since these are run |
|
|||
24 | via pexpect in an external process, they can't deal with exceptions or other |
|
|||
25 | fancy featurs of regular doctests. You must limit such tests to simple |
|
|||
26 | matching of the output. For this reason, I recommend you limit these kinds |
|
|||
27 | of doctests to features that truly require a separate process, and use the |
|
|||
28 | normal IPython ones (which have all the features of normal doctests) for |
|
|||
29 | everything else. See the examples at the bottom of this file for a |
|
|||
30 | comparison of what can be done with both types. |
|
|||
31 | """ |
|
16 | """ | |
32 |
|
17 | |||
33 |
|
18 | |||
@@ -63,9 +48,6 b' import nose.core' | |||||
63 | from nose.plugins import doctests, Plugin |
|
48 | from nose.plugins import doctests, Plugin | |
64 | from nose.util import anyp, getpackage, test_address, resolve_name, tolist |
|
49 | from nose.util import anyp, getpackage, test_address, resolve_name, tolist | |
65 |
|
50 | |||
66 | # Our own imports |
|
|||
67 | #from extdoctest import ExtensionDoctest, DocTestFinder |
|
|||
68 | #from dttools import DocTestFinder, DocTestCase |
|
|||
69 | #----------------------------------------------------------------------------- |
|
51 | #----------------------------------------------------------------------------- | |
70 | # Module globals and other constants |
|
52 | # Module globals and other constants | |
71 |
|
53 | |||
@@ -79,9 +61,9 b' log = logging.getLogger(__name__)' | |||||
79 | # gets the job done. |
|
61 | # gets the job done. | |
80 |
|
62 | |||
81 |
|
63 | |||
82 |
# |
|
64 | # Hack to modify the %run command so we can sync the user's namespace with the | |
83 |
# |
|
65 | # test globals. Once we move over to a clean magic system, this will be done | |
84 |
# |
|
66 | # with much less ugliness. | |
85 |
|
67 | |||
86 | def _run_ns_sync(self,arg_s,runner=None): |
|
68 | def _run_ns_sync(self,arg_s,runner=None): | |
87 | """Modified version of %run that syncs testing namespaces. |
|
69 | """Modified version of %run that syncs testing namespaces. | |
@@ -94,27 +76,34 b' def _run_ns_sync(self,arg_s,runner=None):' | |||||
94 | return out |
|
76 | return out | |
95 |
|
77 | |||
96 |
|
78 | |||
97 | # XXX1 - namespace handling |
|
79 | class ipnsdict(dict): | |
98 | class ncdict(dict): |
|
80 | """A special subclass of dict for use as an IPython namespace in doctests. | |
|
81 | ||||
|
82 | This subclass adds a simple checkpointing capability so that when testing | |||
|
83 | machinery clears it (we use it as the test execution context), it doesn't | |||
|
84 | get completely destroyed. | |||
|
85 | """ | |||
|
86 | ||||
99 | def __init__(self,*a): |
|
87 | def __init__(self,*a): | |
100 | dict.__init__(self,*a) |
|
88 | dict.__init__(self,*a) | |
101 | self._savedict = {} |
|
89 | self._savedict = {} | |
102 |
|
90 | |||
103 | def copy(self): |
|
|||
104 | return self |
|
|||
105 |
|
||||
106 | def clear(self): |
|
91 | def clear(self): | |
107 | import IPython |
|
|||
108 |
|
||||
109 | print 'NCDICT - clear' # dbg |
|
|||
110 | dict.clear(self) |
|
92 | dict.clear(self) | |
111 | self.update(IPython.ipapi.make_user_ns()) |
|
|||
112 | self.update(self._savedict) |
|
93 | self.update(self._savedict) | |
113 |
|
94 | |||
114 | def remember(self,adict): |
|
95 | def _checkpoint(self): | |
115 |
self._savedict |
|
96 | self._savedict.clear() | |
116 |
|
97 | self._savedict.update(self) | ||
117 | #class ncdict(dict): pass |
|
98 | ||
|
99 | def update(self,other): | |||
|
100 | self._checkpoint() | |||
|
101 | dict.update(self,other) | |||
|
102 | # If '_' is in the namespace, python won't set it when executing code, | |||
|
103 | # and we have examples that test it. So we ensure that the namespace | |||
|
104 | # is always 'clean' of it before it's used for test code execution. | |||
|
105 | self.pop('_',None) | |||
|
106 | ||||
118 |
|
107 | |||
119 | def start_ipython(): |
|
108 | def start_ipython(): | |
120 | """Start a global IPython shell, which we need for IPython-specific syntax. |
|
109 | """Start a global IPython shell, which we need for IPython-specific syntax. | |
@@ -139,7 +128,7 b' def start_ipython():' | |||||
139 | _main = sys.modules.get('__main__') |
|
128 | _main = sys.modules.get('__main__') | |
140 |
|
129 | |||
141 | # Start IPython instance. We customize it to start with minimal frills. |
|
130 | # Start IPython instance. We customize it to start with minimal frills. | |
142 |
user_ns,global_ns = IPython.ipapi.make_user_namespaces( |
|
131 | user_ns,global_ns = IPython.ipapi.make_user_namespaces(ipnsdict(),dict()) | |
143 |
|
132 | |||
144 | IPython.Shell.IPShell(['--classic','--noterm_title'], |
|
133 | IPython.Shell.IPShell(['--classic','--noterm_title'], | |
145 | user_ns,global_ns) |
|
134 | user_ns,global_ns) | |
@@ -275,85 +264,6 b' class DocTestFinder(doctest.DocTestFinder):' | |||||
275 | globs, seen) |
|
264 | globs, seen) | |
276 |
|
265 | |||
277 |
|
266 | |||
278 | # XXX1 - namespace handling |
|
|||
279 | def Xfind(self, obj, name=None, module=None, globs=None, extraglobs=None): |
|
|||
280 | """ |
|
|||
281 | Return a list of the DocTests that are defined by the given |
|
|||
282 | object's docstring, or by any of its contained objects' |
|
|||
283 | docstrings. |
|
|||
284 |
|
||||
285 | The optional parameter `module` is the module that contains |
|
|||
286 | the given object. If the module is not specified or is None, then |
|
|||
287 | the test finder will attempt to automatically determine the |
|
|||
288 | correct module. The object's module is used: |
|
|||
289 |
|
||||
290 | - As a default namespace, if `globs` is not specified. |
|
|||
291 | - To prevent the DocTestFinder from extracting DocTests |
|
|||
292 | from objects that are imported from other modules. |
|
|||
293 | - To find the name of the file containing the object. |
|
|||
294 | - To help find the line number of the object within its |
|
|||
295 | file. |
|
|||
296 |
|
||||
297 | Contained objects whose module does not match `module` are ignored. |
|
|||
298 |
|
||||
299 | If `module` is False, no attempt to find the module will be made. |
|
|||
300 | This is obscure, of use mostly in tests: if `module` is False, or |
|
|||
301 | is None but cannot be found automatically, then all objects are |
|
|||
302 | considered to belong to the (non-existent) module, so all contained |
|
|||
303 | objects will (recursively) be searched for doctests. |
|
|||
304 |
|
||||
305 | The globals for each DocTest is formed by combining `globs` |
|
|||
306 | and `extraglobs` (bindings in `extraglobs` override bindings |
|
|||
307 | in `globs`). A new copy of the globals dictionary is created |
|
|||
308 | for each DocTest. If `globs` is not specified, then it |
|
|||
309 | defaults to the module's `__dict__`, if specified, or {} |
|
|||
310 | otherwise. If `extraglobs` is not specified, then it defaults |
|
|||
311 | to {}. |
|
|||
312 |
|
||||
313 | """ |
|
|||
314 |
|
||||
315 | # Find the module that contains the given object (if obj is |
|
|||
316 | # a module, then module=obj.). Note: this may fail, in which |
|
|||
317 | # case module will be None. |
|
|||
318 | if module is False: |
|
|||
319 | module = None |
|
|||
320 | elif module is None: |
|
|||
321 | module = inspect.getmodule(obj) |
|
|||
322 |
|
||||
323 | # always build our own globals |
|
|||
324 | if globs is None: |
|
|||
325 | if module is None: |
|
|||
326 | globs = {} |
|
|||
327 | else: |
|
|||
328 | globs = module.__dict__.copy() |
|
|||
329 | else: |
|
|||
330 | globs.update(module.__dict__.copy()) |
|
|||
331 |
|
||||
332 | print 'globs is:',globs.keys() |
|
|||
333 |
|
||||
334 | if extraglobs is not None: |
|
|||
335 | globs.update(extraglobs) |
|
|||
336 |
|
||||
337 | try: |
|
|||
338 | globs.remember(module.__dict__) |
|
|||
339 | except: |
|
|||
340 | pass |
|
|||
341 |
|
||||
342 | ## # Initialize globals, and merge in extraglobs. |
|
|||
343 | ## if globs is None: |
|
|||
344 | ## if module is None: |
|
|||
345 | ## globs = {} |
|
|||
346 | ## else: |
|
|||
347 | ## globs = module.__dict__.copy() |
|
|||
348 | ## else: |
|
|||
349 | ## globs = globs.copy() |
|
|||
350 | ## if extraglobs is not None: |
|
|||
351 | ## globs.update(extraglobs) |
|
|||
352 |
|
||||
353 | return doctest.DocTestFinder.find(self,obj,name,module,globs, |
|
|||
354 | extraglobs) |
|
|||
355 |
|
||||
356 |
|
||||
357 | class IPDoctestOutputChecker(doctest.OutputChecker): |
|
267 | class IPDoctestOutputChecker(doctest.OutputChecker): | |
358 | """Second-chance checker with support for random tests. |
|
268 | """Second-chance checker with support for random tests. | |
359 |
|
269 | |||
@@ -411,12 +321,20 b' class DocTestCase(doctests.DocTestCase):' | |||||
411 | self._dt_setUp = setUp |
|
321 | self._dt_setUp = setUp | |
412 | self._dt_tearDown = tearDown |
|
322 | self._dt_tearDown = tearDown | |
413 |
|
323 | |||
|
324 | # XXX - store this runner once in the object! | |||
|
325 | runner = IPDocTestRunner(optionflags=optionflags, | |||
|
326 | checker=checker, verbose=False) | |||
|
327 | self._dt_runner = runner | |||
|
328 | ||||
|
329 | ||||
414 | # Each doctest should remember what directory it was loaded from... |
|
330 | # Each doctest should remember what directory it was loaded from... | |
415 | self._ori_dir = os.getcwd() |
|
331 | self._ori_dir = os.getcwd() | |
416 |
|
332 | |||
417 | # Modified runTest from the default stdlib |
|
333 | # Modified runTest from the default stdlib | |
418 | def runTest(self): |
|
334 | def runTest(self): | |
419 | test = self._dt_test |
|
335 | test = self._dt_test | |
|
336 | runner = self._dt_runner | |||
|
337 | ||||
420 | old = sys.stdout |
|
338 | old = sys.stdout | |
421 | new = StringIO() |
|
339 | new = StringIO() | |
422 | optionflags = self._dt_optionflags |
|
340 | optionflags = self._dt_optionflags | |
@@ -426,9 +344,6 b' class DocTestCase(doctests.DocTestCase):' | |||||
426 | # so add the default reporting flags |
|
344 | # so add the default reporting flags | |
427 | optionflags |= _unittest_reportflags |
|
345 | optionflags |= _unittest_reportflags | |
428 |
|
346 | |||
429 | runner = IPDocTestRunner(optionflags=optionflags, |
|
|||
430 | checker=self._dt_checker, verbose=False) |
|
|||
431 |
|
||||
432 | try: |
|
347 | try: | |
433 | # Save our current directory and switch out to the one where the |
|
348 | # Save our current directory and switch out to the one where the | |
434 | # test was originally created, in case another doctest did a |
|
349 | # test was originally created, in case another doctest did a | |
@@ -437,8 +352,8 b' class DocTestCase(doctests.DocTestCase):' | |||||
437 | os.chdir(self._ori_dir) |
|
352 | os.chdir(self._ori_dir) | |
438 |
|
353 | |||
439 | runner.DIVIDER = "-"*70 |
|
354 | runner.DIVIDER = "-"*70 | |
440 | failures, tries = runner.run( |
|
355 | failures, tries = runner.run(test,out=new.write, | |
441 |
|
|
356 | clear_globs=False) | |
442 | finally: |
|
357 | finally: | |
443 | sys.stdout = old |
|
358 | sys.stdout = old | |
444 | os.chdir(curdir) |
|
359 | os.chdir(curdir) | |
@@ -446,11 +361,18 b' class DocTestCase(doctests.DocTestCase):' | |||||
446 | if failures: |
|
361 | if failures: | |
447 | raise self.failureException(self.format_failure(new.getvalue())) |
|
362 | raise self.failureException(self.format_failure(new.getvalue())) | |
448 |
|
363 | |||
449 | # XXX1 - namespace handling |
|
364 | def setUp(self): | |
450 | def XtearDown(self): |
|
365 | """Modified test setup that syncs with ipython namespace""" | |
451 | print '!! teardown!' # dbg |
|
366 | ||
452 | doctests.DocTestCase.tearDown(self) |
|
367 | if isinstance(self._dt_test.examples[0],IPExample): | |
453 |
|
368 | # for IPython examples *only*, we swap the globals with the ipython | ||
|
369 | # namespace, after updating it with the globals (which doctest | |||
|
370 | # fills with the necessary info from the module being tested). | |||
|
371 | _ip.IP.user_ns.update(self._dt_test.globs) | |||
|
372 | self._dt_test.globs = _ip.IP.user_ns | |||
|
373 | ||||
|
374 | doctests.DocTestCase.setUp(self) | |||
|
375 | ||||
454 |
|
376 | |||
455 |
|
377 | |||
456 | # A simple subclassing of the original with a different class name, so we can |
|
378 | # A simple subclassing of the original with a different class name, so we can | |
@@ -548,6 +470,8 b' class IPDocTestParser(doctest.DocTestParser):' | |||||
548 | output = [] |
|
470 | output = [] | |
549 | charno, lineno = 0, 0 |
|
471 | charno, lineno = 0, 0 | |
550 |
|
472 | |||
|
473 | # We make 'all random' tests by adding the '# random' mark to every | |||
|
474 | # block of output in the test. | |||
551 | if self._RANDOM_TEST.search(string): |
|
475 | if self._RANDOM_TEST.search(string): | |
552 | random_marker = '\n# random' |
|
476 | random_marker = '\n# random' | |
553 | else: |
|
477 | else: | |
@@ -715,12 +639,6 b' class IPDocTestRunner(doctest.DocTestRunner,object):' | |||||
715 | # for all examples, most of which won't be calling %run anyway). |
|
639 | # for all examples, most of which won't be calling %run anyway). | |
716 | _run_ns_sync.test_globs = test.globs |
|
640 | _run_ns_sync.test_globs = test.globs | |
717 |
|
641 | |||
718 | # dbg |
|
|||
719 | ## print >> sys.stderr, "Test:",test |
|
|||
720 | ## for ex in test.examples: |
|
|||
721 | ## print >> sys.stderr, ex.source |
|
|||
722 | ## print >> sys.stderr, 'Want:\n',ex.want,'\n--' |
|
|||
723 |
|
||||
724 | return super(IPDocTestRunner,self).run(test, |
|
642 | return super(IPDocTestRunner,self).run(test, | |
725 | compileflags,out,clear_globs) |
|
643 | compileflags,out,clear_globs) | |
726 |
|
644 | |||
@@ -745,8 +663,10 b' class ExtensionDoctest(doctests.Doctest):' | |||||
745 | Plugin.configure(self, options, config) |
|
663 | Plugin.configure(self, options, config) | |
746 | self.doctest_tests = options.doctest_tests |
|
664 | self.doctest_tests = options.doctest_tests | |
747 | self.extension = tolist(options.doctestExtension) |
|
665 | self.extension = tolist(options.doctestExtension) | |
748 | self.finder = DocTestFinder() |
|
666 | ||
749 | self.parser = doctest.DocTestParser() |
|
667 | self.parser = doctest.DocTestParser() | |
|
668 | self.finder = DocTestFinder() | |||
|
669 | self.checker = IPDoctestOutputChecker() | |||
750 | self.globs = None |
|
670 | self.globs = None | |
751 | self.extraglobs = None |
|
671 | self.extraglobs = None | |
752 |
|
672 | |||
@@ -776,6 +696,9 b' class ExtensionDoctest(doctests.Doctest):' | |||||
776 | if not tests: |
|
696 | if not tests: | |
777 | return |
|
697 | return | |
778 |
|
698 | |||
|
699 | # always use whitespace and ellipsis options | |||
|
700 | optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS | |||
|
701 | ||||
779 | tests.sort() |
|
702 | tests.sort() | |
780 | module_file = module.__file__ |
|
703 | module_file = module.__file__ | |
781 | if module_file[-4:] in ('.pyc', '.pyo'): |
|
704 | if module_file[-4:] in ('.pyc', '.pyo'): | |
@@ -785,15 +708,11 b' class ExtensionDoctest(doctests.Doctest):' | |||||
785 | continue |
|
708 | continue | |
786 | if not test.filename: |
|
709 | if not test.filename: | |
787 | test.filename = module_file |
|
710 | test.filename = module_file | |
788 |
|
||||
789 | # xxx - checker and options may be ok instantiated once outside loop |
|
|||
790 | # always use whitespace and ellipsis options |
|
|||
791 | optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS |
|
|||
792 | checker = IPDoctestOutputChecker() |
|
|||
793 |
|
711 | |||
794 | yield DocTestCase(test, |
|
712 | yield DocTestCase(test, | |
795 | optionflags=optionflags, |
|
713 | optionflags=optionflags, | |
796 | checker=checker) |
|
714 | checker=self.checker) | |
|
715 | ||||
797 |
|
716 | |||
798 | def loadTestsFromFile(self, filename): |
|
717 | def loadTestsFromFile(self, filename): | |
799 | #print 'lTF',filename # dbg |
|
718 | #print 'lTF',filename # dbg | |
@@ -826,7 +745,7 b' class ExtensionDoctest(doctests.Doctest):' | |||||
826 | """ |
|
745 | """ | |
827 | #print 'Filename:',filename # dbg |
|
746 | #print 'Filename:',filename # dbg | |
828 |
|
747 | |||
829 | # temporarily hardcoded list, will move to driver later |
|
748 | # XXX - temporarily hardcoded list, will move to driver later | |
830 | exclude = ['IPython/external/', |
|
749 | exclude = ['IPython/external/', | |
831 | 'IPython/Extensions/ipy_', |
|
750 | 'IPython/Extensions/ipy_', | |
832 | 'IPython/platutils_win32', |
|
751 | 'IPython/platutils_win32', | |
@@ -857,11 +776,9 b' class IPythonDoctest(ExtensionDoctest):' | |||||
857 | Plugin.configure(self, options, config) |
|
776 | Plugin.configure(self, options, config) | |
858 | self.doctest_tests = options.doctest_tests |
|
777 | self.doctest_tests = options.doctest_tests | |
859 | self.extension = tolist(options.doctestExtension) |
|
778 | self.extension = tolist(options.doctestExtension) | |
|
779 | ||||
860 | self.parser = IPDocTestParser() |
|
780 | self.parser = IPDocTestParser() | |
861 | self.finder = DocTestFinder(parser=self.parser) |
|
781 | self.finder = DocTestFinder(parser=self.parser) | |
862 |
|
782 | self.checker = IPDoctestOutputChecker() | ||
863 | # XXX1 - namespace handling |
|
|||
864 | self.globs = None |
|
783 | self.globs = None | |
865 | #self.globs = _ip.IP.user_ns |
|
|||
866 |
|
||||
867 | self.extraglobs = None |
|
784 | self.extraglobs = None |
General Comments 0
You need to be logged in to leave comments.
Login now