##// END OF EJS Templates
Limit special-casing of _ variable to doctests....
Fernando Perez -
Show More
@@ -1,184 +1,195 b''
1 """Global IPython app to support test running.
1 """Global IPython app to support test running.
2
2
3 We must start our own ipython object and heavily muck with it so that all the
3 We must start our own ipython object and heavily muck with it so that all the
4 modifications IPython makes to system behavior don't send the doctest machinery
4 modifications IPython makes to system behavior don't send the doctest machinery
5 into a fit. This code should be considered a gross hack, but it gets the job
5 into a fit. This code should be considered a gross hack, but it gets the job
6 done.
6 done.
7 """
7 """
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9 from __future__ import print_function
9 from __future__ import print_function
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2009-2010 The IPython Development Team
12 # Copyright (C) 2009-2010 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 # stdlib
22 # stdlib
23 import __builtin__
23 import __builtin__
24 import os
24 import os
25 import sys
25 import sys
26 from types import MethodType
26 from types import MethodType
27
27
28 # our own
28 # our own
29 from . import tools
29 from . import tools
30
30
31 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
31 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Functions
34 # Functions
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 # Hack to modify the %run command so we can sync the user's namespace with the
37 # Hack to modify the %run command so we can sync the user's namespace with the
38 # test globals. Once we move over to a clean magic system, this will be done
38 # test globals. Once we move over to a clean magic system, this will be done
39 # with much less ugliness.
39 # with much less ugliness.
40
40
41 class py_file_finder(object):
41 class py_file_finder(object):
42 def __init__(self,test_filename):
42 def __init__(self,test_filename):
43 self.test_filename = test_filename
43 self.test_filename = test_filename
44
44
45 def __call__(self,name):
45 def __call__(self,name):
46 from IPython.utils.path import get_py_filename
46 from IPython.utils.path import get_py_filename
47 try:
47 try:
48 return get_py_filename(name)
48 return get_py_filename(name)
49 except IOError:
49 except IOError:
50 test_dir = os.path.dirname(self.test_filename)
50 test_dir = os.path.dirname(self.test_filename)
51 new_path = os.path.join(test_dir,name)
51 new_path = os.path.join(test_dir,name)
52 return get_py_filename(new_path)
52 return get_py_filename(new_path)
53
53
54
54
55 def _run_ns_sync(self,arg_s,runner=None):
55 def _run_ns_sync(self,arg_s,runner=None):
56 """Modified version of %run that syncs testing namespaces.
56 """Modified version of %run that syncs testing namespaces.
57
57
58 This is strictly needed for running doctests that call %run.
58 This is strictly needed for running doctests that call %run.
59 """
59 """
60 #print('in run_ns_sync', arg_s, file=sys.stderr) # dbg
60 #print('in run_ns_sync', arg_s, file=sys.stderr) # dbg
61 finder = py_file_finder(arg_s)
61 finder = py_file_finder(arg_s)
62 return get_ipython().magic_run_ori(arg_s, runner, finder)
62 return get_ipython().magic_run_ori(arg_s, runner, finder)
63
63
64
64
65 class ipnsdict(dict):
65 class ipnsdict(dict):
66 """A special subclass of dict for use as an IPython namespace in doctests.
66 """A special subclass of dict for use as an IPython namespace in doctests.
67
67
68 This subclass adds a simple checkpointing capability so that when testing
68 This subclass adds a simple checkpointing capability so that when testing
69 machinery clears it (we use it as the test execution context), it doesn't
69 machinery clears it (we use it as the test execution context), it doesn't
70 get completely destroyed.
70 get completely destroyed.
71
72 In addition, it can handle the presence of the '_' key in a special manner,
73 which is needed because of how Python's doctest machinery operates with
74 '_'. See constructor and :meth:`update` for details.
71 """
75 """
72
76
73 def __init__(self,*a):
77 def __init__(self,*a):
74 dict.__init__(self,*a)
78 dict.__init__(self,*a)
75 self._savedict = {}
79 self._savedict = {}
80 # If this flag is True, the .update() method will unconditionally
81 # remove a key named '_'. This is so that such a dict can be used as a
82 # namespace in doctests that call '_'.
83 self.protect_underscore = False
76
84
77 def clear(self):
85 def clear(self):
78 dict.clear(self)
86 dict.clear(self)
79 self.update(self._savedict)
87 self.update(self._savedict)
80
88
81 def _checkpoint(self):
89 def _checkpoint(self):
82 self._savedict.clear()
90 self._savedict.clear()
83 self._savedict.update(self)
91 self._savedict.update(self)
84
92
85 def update(self,other):
93 def update(self,other):
86 self._checkpoint()
94 self._checkpoint()
87 dict.update(self,other)
95 dict.update(self,other)
88
96
89 # If '_' is in the namespace, python won't set it when executing code
97 if self.protect_underscore:
90 # *in doctests*, and we have multiple doctests that use '_'. So we
98 # If '_' is in the namespace, python won't set it when executing
91 # ensure that the namespace is always 'clean' of it before it's used
99 # code *in doctests*, and we have multiple doctests that use '_'.
92 # for test code execution. Note: this means that outside of doctests,
100 # So we ensure that the namespace is always 'clean' of it before
93 # our own testing
101 # it's used for test code execution.
94 self.pop('_',None)
102 # This flag is only turned on by the doctest machinery, so that
103 # normal test code can assume the _ key is updated like any other
104 # key and can test for its presence after cell executions.
105 self.pop('_', None)
95
106
96 # The builtins namespace must *always* be the real __builtin__ module,
107 # The builtins namespace must *always* be the real __builtin__ module,
97 # else weird stuff happens. The main ipython code does have provisions
108 # else weird stuff happens. The main ipython code does have provisions
98 # to ensure this after %run, but since in this class we do some
109 # to ensure this after %run, but since in this class we do some
99 # aggressive low-level cleaning of the execution namespace, we need to
110 # aggressive low-level cleaning of the execution namespace, we need to
100 # correct for that ourselves, to ensure consitency with the 'real'
111 # correct for that ourselves, to ensure consitency with the 'real'
101 # ipython.
112 # ipython.
102 self['__builtins__'] = __builtin__
113 self['__builtins__'] = __builtin__
103
114
104
115
105 def get_ipython():
116 def get_ipython():
106 # This will get replaced by the real thing once we start IPython below
117 # This will get replaced by the real thing once we start IPython below
107 return start_ipython()
118 return start_ipython()
108
119
109
120
110 # A couple of methods to override those in the running IPython to interact
121 # A couple of methods to override those in the running IPython to interact
111 # better with doctest (doctest captures on raw stdout, so we need to direct
122 # better with doctest (doctest captures on raw stdout, so we need to direct
112 # various types of output there otherwise it will miss them).
123 # various types of output there otherwise it will miss them).
113
124
114 def xsys(self, cmd):
125 def xsys(self, cmd):
115 """Replace the default system call with a capturing one for doctest.
126 """Replace the default system call with a capturing one for doctest.
116 """
127 """
117 # We use getoutput, but we need to strip it because pexpect captures
128 # We use getoutput, but we need to strip it because pexpect captures
118 # the trailing newline differently from commands.getoutput
129 # the trailing newline differently from commands.getoutput
119 print(self.getoutput(cmd, split=False).rstrip(), end='', file=sys.stdout)
130 print(self.getoutput(cmd, split=False).rstrip(), end='', file=sys.stdout)
120 sys.stdout.flush()
131 sys.stdout.flush()
121
132
122
133
123 def _showtraceback(self, etype, evalue, stb):
134 def _showtraceback(self, etype, evalue, stb):
124 """Print the traceback purely on stdout for doctest to capture it.
135 """Print the traceback purely on stdout for doctest to capture it.
125 """
136 """
126 print(self.InteractiveTB.stb2text(stb), file=sys.stdout)
137 print(self.InteractiveTB.stb2text(stb), file=sys.stdout)
127
138
128
139
129 def start_ipython():
140 def start_ipython():
130 """Start a global IPython shell, which we need for IPython-specific syntax.
141 """Start a global IPython shell, which we need for IPython-specific syntax.
131 """
142 """
132 global get_ipython
143 global get_ipython
133
144
134 # This function should only ever run once!
145 # This function should only ever run once!
135 if hasattr(start_ipython, 'already_called'):
146 if hasattr(start_ipython, 'already_called'):
136 return
147 return
137 start_ipython.already_called = True
148 start_ipython.already_called = True
138
149
139 # Store certain global objects that IPython modifies
150 # Store certain global objects that IPython modifies
140 _displayhook = sys.displayhook
151 _displayhook = sys.displayhook
141 _excepthook = sys.excepthook
152 _excepthook = sys.excepthook
142 _main = sys.modules.get('__main__')
153 _main = sys.modules.get('__main__')
143
154
144 # Create custom argv and namespaces for our IPython to be test-friendly
155 # Create custom argv and namespaces for our IPython to be test-friendly
145 config = tools.default_config()
156 config = tools.default_config()
146
157
147 # Create and initialize our test-friendly IPython instance.
158 # Create and initialize our test-friendly IPython instance.
148 shell = TerminalInteractiveShell.instance(config=config,
159 shell = TerminalInteractiveShell.instance(config=config,
149 user_ns=ipnsdict(),
160 user_ns=ipnsdict(),
150 user_global_ns={}
161 user_global_ns={}
151 )
162 )
152
163
153 # A few more tweaks needed for playing nicely with doctests...
164 # A few more tweaks needed for playing nicely with doctests...
154
165
155 # These traps are normally only active for interactive use, set them
166 # These traps are normally only active for interactive use, set them
156 # permanently since we'll be mocking interactive sessions.
167 # permanently since we'll be mocking interactive sessions.
157 shell.builtin_trap.activate()
168 shell.builtin_trap.activate()
158
169
159 # Modify the IPython system call with one that uses getoutput, so that we
170 # Modify the IPython system call with one that uses getoutput, so that we
160 # can capture subcommands and print them to Python's stdout, otherwise the
171 # can capture subcommands and print them to Python's stdout, otherwise the
161 # doctest machinery would miss them.
172 # doctest machinery would miss them.
162 shell.system = MethodType(xsys, shell, TerminalInteractiveShell)
173 shell.system = MethodType(xsys, shell, TerminalInteractiveShell)
163
174
164
175
165 shell._showtraceback = MethodType(_showtraceback, shell,
176 shell._showtraceback = MethodType(_showtraceback, shell,
166 TerminalInteractiveShell)
177 TerminalInteractiveShell)
167
178
168 # IPython is ready, now clean up some global state...
179 # IPython is ready, now clean up some global state...
169
180
170 # Deactivate the various python system hooks added by ipython for
181 # Deactivate the various python system hooks added by ipython for
171 # interactive convenience so we don't confuse the doctest system
182 # interactive convenience so we don't confuse the doctest system
172 sys.modules['__main__'] = _main
183 sys.modules['__main__'] = _main
173 sys.displayhook = _displayhook
184 sys.displayhook = _displayhook
174 sys.excepthook = _excepthook
185 sys.excepthook = _excepthook
175
186
176 # So that ipython magics and aliases can be doctested (they work by making
187 # So that ipython magics and aliases can be doctested (they work by making
177 # a call into a global _ip object). Also make the top-level get_ipython
188 # a call into a global _ip object). Also make the top-level get_ipython
178 # now return this without recursively calling here again.
189 # now return this without recursively calling here again.
179 _ip = shell
190 _ip = shell
180 get_ipython = _ip.get_ipython
191 get_ipython = _ip.get_ipython
181 __builtin__._ip = _ip
192 __builtin__._ip = _ip
182 __builtin__.get_ipython = get_ipython
193 __builtin__.get_ipython = get_ipython
183
194
184 return _ip
195 return _ip
@@ -1,786 +1,793 b''
1 """Nose Plugin that supports IPython doctests.
1 """Nose Plugin that supports IPython doctests.
2
2
3 Limitations:
3 Limitations:
4
4
5 - When generating examples for use as doctests, make sure that you have
5 - When generating examples for use as doctests, make sure that you have
6 pretty-printing OFF. This can be done either by starting ipython with the
6 pretty-printing OFF. This can be done either by starting ipython with the
7 flag '--nopprint', by setting pprint to 0 in your ipythonrc file, or by
7 flag '--nopprint', by setting pprint to 0 in your ipythonrc file, or by
8 interactively disabling it with %Pprint. This is required so that IPython
8 interactively disabling it with %Pprint. This is required so that IPython
9 output matches that of normal Python, which is used by doctest for internal
9 output matches that of normal Python, which is used by doctest for internal
10 execution.
10 execution.
11
11
12 - Do not rely on specific prompt numbers for results (such as using
12 - Do not rely on specific prompt numbers for results (such as using
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 """
16 """
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Module imports
19 # Module imports
20
20
21 # From the standard library
21 # From the standard library
22 import __builtin__
22 import __builtin__
23 import commands
23 import commands
24 import doctest
24 import doctest
25 import inspect
25 import inspect
26 import logging
26 import logging
27 import os
27 import os
28 import re
28 import re
29 import sys
29 import sys
30 import traceback
30 import traceback
31 import unittest
31 import unittest
32
32
33 from inspect import getmodule
33 from inspect import getmodule
34 from StringIO import StringIO
34 from StringIO import StringIO
35
35
36 # We are overriding the default doctest runner, so we need to import a few
36 # We are overriding the default doctest runner, so we need to import a few
37 # things from doctest directly
37 # things from doctest directly
38 from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE,
38 from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE,
39 _unittest_reportflags, DocTestRunner,
39 _unittest_reportflags, DocTestRunner,
40 _extract_future_flags, pdb, _OutputRedirectingPdb,
40 _extract_future_flags, pdb, _OutputRedirectingPdb,
41 _exception_traceback,
41 _exception_traceback,
42 linecache)
42 linecache)
43
43
44 # Third-party modules
44 # Third-party modules
45 import nose.core
45 import nose.core
46
46
47 from nose.plugins import doctests, Plugin
47 from nose.plugins import doctests, Plugin
48 from nose.util import anyp, getpackage, test_address, resolve_name, tolist
48 from nose.util import anyp, getpackage, test_address, resolve_name, tolist
49
49
50 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
51 # Module globals and other constants
51 # Module globals and other constants
52 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56
56
57 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
58 # Classes and functions
58 # Classes and functions
59 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
60
60
61 def is_extension_module(filename):
61 def is_extension_module(filename):
62 """Return whether the given filename is an extension module.
62 """Return whether the given filename is an extension module.
63
63
64 This simply checks that the extension is either .so or .pyd.
64 This simply checks that the extension is either .so or .pyd.
65 """
65 """
66 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
66 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
67
67
68
68
69 class DocTestSkip(object):
69 class DocTestSkip(object):
70 """Object wrapper for doctests to be skipped."""
70 """Object wrapper for doctests to be skipped."""
71
71
72 ds_skip = """Doctest to skip.
72 ds_skip = """Doctest to skip.
73 >>> 1 #doctest: +SKIP
73 >>> 1 #doctest: +SKIP
74 """
74 """
75
75
76 def __init__(self,obj):
76 def __init__(self,obj):
77 self.obj = obj
77 self.obj = obj
78
78
79 def __getattribute__(self,key):
79 def __getattribute__(self,key):
80 if key == '__doc__':
80 if key == '__doc__':
81 return DocTestSkip.ds_skip
81 return DocTestSkip.ds_skip
82 else:
82 else:
83 return getattr(object.__getattribute__(self,'obj'),key)
83 return getattr(object.__getattribute__(self,'obj'),key)
84
84
85 # Modified version of the one in the stdlib, that fixes a python bug (doctests
85 # Modified version of the one in the stdlib, that fixes a python bug (doctests
86 # not found in extension modules, http://bugs.python.org/issue3158)
86 # not found in extension modules, http://bugs.python.org/issue3158)
87 class DocTestFinder(doctest.DocTestFinder):
87 class DocTestFinder(doctest.DocTestFinder):
88
88
89 def _from_module(self, module, object):
89 def _from_module(self, module, object):
90 """
90 """
91 Return true if the given object is defined in the given
91 Return true if the given object is defined in the given
92 module.
92 module.
93 """
93 """
94 if module is None:
94 if module is None:
95 return True
95 return True
96 elif inspect.isfunction(object):
96 elif inspect.isfunction(object):
97 return module.__dict__ is object.func_globals
97 return module.__dict__ is object.func_globals
98 elif inspect.isbuiltin(object):
98 elif inspect.isbuiltin(object):
99 return module.__name__ == object.__module__
99 return module.__name__ == object.__module__
100 elif inspect.isclass(object):
100 elif inspect.isclass(object):
101 return module.__name__ == object.__module__
101 return module.__name__ == object.__module__
102 elif inspect.ismethod(object):
102 elif inspect.ismethod(object):
103 # This one may be a bug in cython that fails to correctly set the
103 # This one may be a bug in cython that fails to correctly set the
104 # __module__ attribute of methods, but since the same error is easy
104 # __module__ attribute of methods, but since the same error is easy
105 # to make by extension code writers, having this safety in place
105 # to make by extension code writers, having this safety in place
106 # isn't such a bad idea
106 # isn't such a bad idea
107 return module.__name__ == object.im_class.__module__
107 return module.__name__ == object.im_class.__module__
108 elif inspect.getmodule(object) is not None:
108 elif inspect.getmodule(object) is not None:
109 return module is inspect.getmodule(object)
109 return module is inspect.getmodule(object)
110 elif hasattr(object, '__module__'):
110 elif hasattr(object, '__module__'):
111 return module.__name__ == object.__module__
111 return module.__name__ == object.__module__
112 elif isinstance(object, property):
112 elif isinstance(object, property):
113 return True # [XX] no way not be sure.
113 return True # [XX] no way not be sure.
114 else:
114 else:
115 raise ValueError("object must be a class or function")
115 raise ValueError("object must be a class or function")
116
116
117 def _find(self, tests, obj, name, module, source_lines, globs, seen):
117 def _find(self, tests, obj, name, module, source_lines, globs, seen):
118 """
118 """
119 Find tests for the given object and any contained objects, and
119 Find tests for the given object and any contained objects, and
120 add them to `tests`.
120 add them to `tests`.
121 """
121 """
122 #print '_find for:', obj, name, module # dbg
122 #print '_find for:', obj, name, module # dbg
123 if hasattr(obj,"skip_doctest"):
123 if hasattr(obj,"skip_doctest"):
124 #print 'SKIPPING DOCTEST FOR:',obj # dbg
124 #print 'SKIPPING DOCTEST FOR:',obj # dbg
125 obj = DocTestSkip(obj)
125 obj = DocTestSkip(obj)
126
126
127 doctest.DocTestFinder._find(self,tests, obj, name, module,
127 doctest.DocTestFinder._find(self,tests, obj, name, module,
128 source_lines, globs, seen)
128 source_lines, globs, seen)
129
129
130 # Below we re-run pieces of the above method with manual modifications,
130 # Below we re-run pieces of the above method with manual modifications,
131 # because the original code is buggy and fails to correctly identify
131 # because the original code is buggy and fails to correctly identify
132 # doctests in extension modules.
132 # doctests in extension modules.
133
133
134 # Local shorthands
134 # Local shorthands
135 from inspect import isroutine, isclass, ismodule
135 from inspect import isroutine, isclass, ismodule
136
136
137 # Look for tests in a module's contained objects.
137 # Look for tests in a module's contained objects.
138 if inspect.ismodule(obj) and self._recurse:
138 if inspect.ismodule(obj) and self._recurse:
139 for valname, val in obj.__dict__.items():
139 for valname, val in obj.__dict__.items():
140 valname1 = '%s.%s' % (name, valname)
140 valname1 = '%s.%s' % (name, valname)
141 if ( (isroutine(val) or isclass(val))
141 if ( (isroutine(val) or isclass(val))
142 and self._from_module(module, val) ):
142 and self._from_module(module, val) ):
143
143
144 self._find(tests, val, valname1, module, source_lines,
144 self._find(tests, val, valname1, module, source_lines,
145 globs, seen)
145 globs, seen)
146
146
147 # Look for tests in a class's contained objects.
147 # Look for tests in a class's contained objects.
148 if inspect.isclass(obj) and self._recurse:
148 if inspect.isclass(obj) and self._recurse:
149 #print 'RECURSE into class:',obj # dbg
149 #print 'RECURSE into class:',obj # dbg
150 for valname, val in obj.__dict__.items():
150 for valname, val in obj.__dict__.items():
151 # Special handling for staticmethod/classmethod.
151 # Special handling for staticmethod/classmethod.
152 if isinstance(val, staticmethod):
152 if isinstance(val, staticmethod):
153 val = getattr(obj, valname)
153 val = getattr(obj, valname)
154 if isinstance(val, classmethod):
154 if isinstance(val, classmethod):
155 val = getattr(obj, valname).im_func
155 val = getattr(obj, valname).im_func
156
156
157 # Recurse to methods, properties, and nested classes.
157 # Recurse to methods, properties, and nested classes.
158 if ((inspect.isfunction(val) or inspect.isclass(val) or
158 if ((inspect.isfunction(val) or inspect.isclass(val) or
159 inspect.ismethod(val) or
159 inspect.ismethod(val) or
160 isinstance(val, property)) and
160 isinstance(val, property)) and
161 self._from_module(module, val)):
161 self._from_module(module, val)):
162 valname = '%s.%s' % (name, valname)
162 valname = '%s.%s' % (name, valname)
163 self._find(tests, val, valname, module, source_lines,
163 self._find(tests, val, valname, module, source_lines,
164 globs, seen)
164 globs, seen)
165
165
166
166
167 class IPDoctestOutputChecker(doctest.OutputChecker):
167 class IPDoctestOutputChecker(doctest.OutputChecker):
168 """Second-chance checker with support for random tests.
168 """Second-chance checker with support for random tests.
169
169
170 If the default comparison doesn't pass, this checker looks in the expected
170 If the default comparison doesn't pass, this checker looks in the expected
171 output string for flags that tell us to ignore the output.
171 output string for flags that tell us to ignore the output.
172 """
172 """
173
173
174 random_re = re.compile(r'#\s*random\s+')
174 random_re = re.compile(r'#\s*random\s+')
175
175
176 def check_output(self, want, got, optionflags):
176 def check_output(self, want, got, optionflags):
177 """Check output, accepting special markers embedded in the output.
177 """Check output, accepting special markers embedded in the output.
178
178
179 If the output didn't pass the default validation but the special string
179 If the output didn't pass the default validation but the special string
180 '#random' is included, we accept it."""
180 '#random' is included, we accept it."""
181
181
182 # Let the original tester verify first, in case people have valid tests
182 # Let the original tester verify first, in case people have valid tests
183 # that happen to have a comment saying '#random' embedded in.
183 # that happen to have a comment saying '#random' embedded in.
184 ret = doctest.OutputChecker.check_output(self, want, got,
184 ret = doctest.OutputChecker.check_output(self, want, got,
185 optionflags)
185 optionflags)
186 if not ret and self.random_re.search(want):
186 if not ret and self.random_re.search(want):
187 #print >> sys.stderr, 'RANDOM OK:',want # dbg
187 #print >> sys.stderr, 'RANDOM OK:',want # dbg
188 return True
188 return True
189
189
190 return ret
190 return ret
191
191
192
192
193 class DocTestCase(doctests.DocTestCase):
193 class DocTestCase(doctests.DocTestCase):
194 """Proxy for DocTestCase: provides an address() method that
194 """Proxy for DocTestCase: provides an address() method that
195 returns the correct address for the doctest case. Otherwise
195 returns the correct address for the doctest case. Otherwise
196 acts as a proxy to the test case. To provide hints for address(),
196 acts as a proxy to the test case. To provide hints for address(),
197 an obj may also be passed -- this will be used as the test object
197 an obj may also be passed -- this will be used as the test object
198 for purposes of determining the test address, if it is provided.
198 for purposes of determining the test address, if it is provided.
199 """
199 """
200
200
201 # Note: this method was taken from numpy's nosetester module.
201 # Note: this method was taken from numpy's nosetester module.
202
202
203 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
203 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
204 # its constructor that blocks non-default arguments from being passed
204 # its constructor that blocks non-default arguments from being passed
205 # down into doctest.DocTestCase
205 # down into doctest.DocTestCase
206
206
207 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
207 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
208 checker=None, obj=None, result_var='_'):
208 checker=None, obj=None, result_var='_'):
209 self._result_var = result_var
209 self._result_var = result_var
210 doctests.DocTestCase.__init__(self, test,
210 doctests.DocTestCase.__init__(self, test,
211 optionflags=optionflags,
211 optionflags=optionflags,
212 setUp=setUp, tearDown=tearDown,
212 setUp=setUp, tearDown=tearDown,
213 checker=checker)
213 checker=checker)
214 # Now we must actually copy the original constructor from the stdlib
214 # Now we must actually copy the original constructor from the stdlib
215 # doctest class, because we can't call it directly and a bug in nose
215 # doctest class, because we can't call it directly and a bug in nose
216 # means it never gets passed the right arguments.
216 # means it never gets passed the right arguments.
217
217
218 self._dt_optionflags = optionflags
218 self._dt_optionflags = optionflags
219 self._dt_checker = checker
219 self._dt_checker = checker
220 self._dt_test = test
220 self._dt_test = test
221 self._dt_test_globs_ori = test.globs
221 self._dt_test_globs_ori = test.globs
222 self._dt_setUp = setUp
222 self._dt_setUp = setUp
223 self._dt_tearDown = tearDown
223 self._dt_tearDown = tearDown
224
224
225 # XXX - store this runner once in the object!
225 # XXX - store this runner once in the object!
226 runner = IPDocTestRunner(optionflags=optionflags,
226 runner = IPDocTestRunner(optionflags=optionflags,
227 checker=checker, verbose=False)
227 checker=checker, verbose=False)
228 self._dt_runner = runner
228 self._dt_runner = runner
229
229
230
230
231 # Each doctest should remember the directory it was loaded from, so
231 # Each doctest should remember the directory it was loaded from, so
232 # things like %run work without too many contortions
232 # things like %run work without too many contortions
233 self._ori_dir = os.path.dirname(test.filename)
233 self._ori_dir = os.path.dirname(test.filename)
234
234
235 # Modified runTest from the default stdlib
235 # Modified runTest from the default stdlib
236 def runTest(self):
236 def runTest(self):
237 test = self._dt_test
237 test = self._dt_test
238 runner = self._dt_runner
238 runner = self._dt_runner
239
239
240 old = sys.stdout
240 old = sys.stdout
241 new = StringIO()
241 new = StringIO()
242 optionflags = self._dt_optionflags
242 optionflags = self._dt_optionflags
243
243
244 if not (optionflags & REPORTING_FLAGS):
244 if not (optionflags & REPORTING_FLAGS):
245 # The option flags don't include any reporting flags,
245 # The option flags don't include any reporting flags,
246 # so add the default reporting flags
246 # so add the default reporting flags
247 optionflags |= _unittest_reportflags
247 optionflags |= _unittest_reportflags
248
248
249 try:
249 try:
250 # Save our current directory and switch out to the one where the
250 # Save our current directory and switch out to the one where the
251 # test was originally created, in case another doctest did a
251 # test was originally created, in case another doctest did a
252 # directory change. We'll restore this in the finally clause.
252 # directory change. We'll restore this in the finally clause.
253 curdir = os.getcwd()
253 curdir = os.getcwd()
254 #print 'runTest in dir:', self._ori_dir # dbg
254 #print 'runTest in dir:', self._ori_dir # dbg
255 os.chdir(self._ori_dir)
255 os.chdir(self._ori_dir)
256
256
257 runner.DIVIDER = "-"*70
257 runner.DIVIDER = "-"*70
258 failures, tries = runner.run(test,out=new.write,
258 failures, tries = runner.run(test,out=new.write,
259 clear_globs=False)
259 clear_globs=False)
260 finally:
260 finally:
261 sys.stdout = old
261 sys.stdout = old
262 os.chdir(curdir)
262 os.chdir(curdir)
263
263
264 if failures:
264 if failures:
265 raise self.failureException(self.format_failure(new.getvalue()))
265 raise self.failureException(self.format_failure(new.getvalue()))
266
266
267 def setUp(self):
267 def setUp(self):
268 """Modified test setup that syncs with ipython namespace"""
268 """Modified test setup that syncs with ipython namespace"""
269 #print "setUp test", self._dt_test.examples # dbg
269 #print "setUp test", self._dt_test.examples # dbg
270 if isinstance(self._dt_test.examples[0],IPExample):
270 if isinstance(self._dt_test.examples[0],IPExample):
271 # for IPython examples *only*, we swap the globals with the ipython
271 # for IPython examples *only*, we swap the globals with the ipython
272 # namespace, after updating it with the globals (which doctest
272 # namespace, after updating it with the globals (which doctest
273 # fills with the necessary info from the module being tested).
273 # fills with the necessary info from the module being tested).
274 _ip.user_ns.update(self._dt_test.globs)
274 _ip.user_ns.update(self._dt_test.globs)
275 self._dt_test.globs = _ip.user_ns
275 self._dt_test.globs = _ip.user_ns
276 # IPython must protect the _ key in the namespace (it can't exist)
277 # so that Python's doctest code sets it naturally, so we enable
278 # this feature of our testing namespace.
279 _ip.user_ns.protect_underscore = True
276
280
277 super(DocTestCase, self).setUp()
281 super(DocTestCase, self).setUp()
278
282
279 def tearDown(self):
283 def tearDown(self):
280
284
281 # Undo the test.globs reassignment we made, so that the parent class
285 # Undo the test.globs reassignment we made, so that the parent class
282 # teardown doesn't destroy the ipython namespace
286 # teardown doesn't destroy the ipython namespace
283 if isinstance(self._dt_test.examples[0],IPExample):
287 if isinstance(self._dt_test.examples[0],IPExample):
284 self._dt_test.globs = self._dt_test_globs_ori
288 self._dt_test.globs = self._dt_test_globs_ori
289 # Restore the behavior of the '_' key in the user namespace to
290 # normal after each doctest, so that unittests behave normally
291 _ip.user_ns.protect_underscore = False
285
292
286 # XXX - fperez: I am not sure if this is truly a bug in nose 0.11, but
293 # XXX - fperez: I am not sure if this is truly a bug in nose 0.11, but
287 # it does look like one to me: its tearDown method tries to run
294 # it does look like one to me: its tearDown method tries to run
288 #
295 #
289 # delattr(__builtin__, self._result_var)
296 # delattr(__builtin__, self._result_var)
290 #
297 #
291 # without checking that the attribute really is there; it implicitly
298 # without checking that the attribute really is there; it implicitly
292 # assumes it should have been set via displayhook. But if the
299 # assumes it should have been set via displayhook. But if the
293 # displayhook was never called, this doesn't necessarily happen. I
300 # displayhook was never called, this doesn't necessarily happen. I
294 # haven't been able to find a little self-contained example outside of
301 # haven't been able to find a little self-contained example outside of
295 # ipython that would show the problem so I can report it to the nose
302 # ipython that would show the problem so I can report it to the nose
296 # team, but it does happen a lot in our code.
303 # team, but it does happen a lot in our code.
297 #
304 #
298 # So here, we just protect as narrowly as possible by trapping an
305 # So here, we just protect as narrowly as possible by trapping an
299 # attribute error whose message would be the name of self._result_var,
306 # attribute error whose message would be the name of self._result_var,
300 # and letting any other error propagate.
307 # and letting any other error propagate.
301 try:
308 try:
302 super(DocTestCase, self).tearDown()
309 super(DocTestCase, self).tearDown()
303 except AttributeError, exc:
310 except AttributeError, exc:
304 if exc.args[0] != self._result_var:
311 if exc.args[0] != self._result_var:
305 raise
312 raise
306
313
307
314
308 # A simple subclassing of the original with a different class name, so we can
315 # A simple subclassing of the original with a different class name, so we can
309 # distinguish and treat differently IPython examples from pure python ones.
316 # distinguish and treat differently IPython examples from pure python ones.
310 class IPExample(doctest.Example): pass
317 class IPExample(doctest.Example): pass
311
318
312
319
313 class IPExternalExample(doctest.Example):
320 class IPExternalExample(doctest.Example):
314 """Doctest examples to be run in an external process."""
321 """Doctest examples to be run in an external process."""
315
322
316 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
323 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
317 options=None):
324 options=None):
318 # Parent constructor
325 # Parent constructor
319 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
326 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
320
327
321 # An EXTRA newline is needed to prevent pexpect hangs
328 # An EXTRA newline is needed to prevent pexpect hangs
322 self.source += '\n'
329 self.source += '\n'
323
330
324
331
325 class IPDocTestParser(doctest.DocTestParser):
332 class IPDocTestParser(doctest.DocTestParser):
326 """
333 """
327 A class used to parse strings containing doctest examples.
334 A class used to parse strings containing doctest examples.
328
335
329 Note: This is a version modified to properly recognize IPython input and
336 Note: This is a version modified to properly recognize IPython input and
330 convert any IPython examples into valid Python ones.
337 convert any IPython examples into valid Python ones.
331 """
338 """
332 # This regular expression is used to find doctest examples in a
339 # This regular expression is used to find doctest examples in a
333 # string. It defines three groups: `source` is the source code
340 # string. It defines three groups: `source` is the source code
334 # (including leading indentation and prompts); `indent` is the
341 # (including leading indentation and prompts); `indent` is the
335 # indentation of the first (PS1) line of the source code; and
342 # indentation of the first (PS1) line of the source code; and
336 # `want` is the expected output (including leading indentation).
343 # `want` is the expected output (including leading indentation).
337
344
338 # Classic Python prompts or default IPython ones
345 # Classic Python prompts or default IPython ones
339 _PS1_PY = r'>>>'
346 _PS1_PY = r'>>>'
340 _PS2_PY = r'\.\.\.'
347 _PS2_PY = r'\.\.\.'
341
348
342 _PS1_IP = r'In\ \[\d+\]:'
349 _PS1_IP = r'In\ \[\d+\]:'
343 _PS2_IP = r'\ \ \ \.\.\.+:'
350 _PS2_IP = r'\ \ \ \.\.\.+:'
344
351
345 _RE_TPL = r'''
352 _RE_TPL = r'''
346 # Source consists of a PS1 line followed by zero or more PS2 lines.
353 # Source consists of a PS1 line followed by zero or more PS2 lines.
347 (?P<source>
354 (?P<source>
348 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
355 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
349 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
356 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
350 \n? # a newline
357 \n? # a newline
351 # Want consists of any non-blank lines that do not start with PS1.
358 # Want consists of any non-blank lines that do not start with PS1.
352 (?P<want> (?:(?![ ]*$) # Not a blank line
359 (?P<want> (?:(?![ ]*$) # Not a blank line
353 (?![ ]*%s) # Not a line starting with PS1
360 (?![ ]*%s) # Not a line starting with PS1
354 (?![ ]*%s) # Not a line starting with PS2
361 (?![ ]*%s) # Not a line starting with PS2
355 .*$\n? # But any other line
362 .*$\n? # But any other line
356 )*)
363 )*)
357 '''
364 '''
358
365
359 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
366 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
360 re.MULTILINE | re.VERBOSE)
367 re.MULTILINE | re.VERBOSE)
361
368
362 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
369 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
363 re.MULTILINE | re.VERBOSE)
370 re.MULTILINE | re.VERBOSE)
364
371
365 # Mark a test as being fully random. In this case, we simply append the
372 # Mark a test as being fully random. In this case, we simply append the
366 # random marker ('#random') to each individual example's output. This way
373 # random marker ('#random') to each individual example's output. This way
367 # we don't need to modify any other code.
374 # we don't need to modify any other code.
368 _RANDOM_TEST = re.compile(r'#\s*all-random\s+')
375 _RANDOM_TEST = re.compile(r'#\s*all-random\s+')
369
376
370 # Mark tests to be executed in an external process - currently unsupported.
377 # Mark tests to be executed in an external process - currently unsupported.
371 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
378 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
372
379
373 def ip2py(self,source):
380 def ip2py(self,source):
374 """Convert input IPython source into valid Python."""
381 """Convert input IPython source into valid Python."""
375 out = []
382 out = []
376 newline = out.append
383 newline = out.append
377 #print 'IPSRC:\n',source,'\n###' # dbg
384 #print 'IPSRC:\n',source,'\n###' # dbg
378 # The input source must be first stripped of all bracketing whitespace
385 # The input source must be first stripped of all bracketing whitespace
379 # and turned into lines, so it looks to the parser like regular user
386 # and turned into lines, so it looks to the parser like regular user
380 # input
387 # input
381 for lnum,line in enumerate(source.strip().splitlines()):
388 for lnum,line in enumerate(source.strip().splitlines()):
382 newline(_ip.prefilter(line,lnum>0))
389 newline(_ip.prefilter(line,lnum>0))
383 newline('') # ensure a closing newline, needed by doctest
390 newline('') # ensure a closing newline, needed by doctest
384 #print "PYSRC:", '\n'.join(out) # dbg
391 #print "PYSRC:", '\n'.join(out) # dbg
385 return '\n'.join(out)
392 return '\n'.join(out)
386
393
387 def parse(self, string, name='<string>'):
394 def parse(self, string, name='<string>'):
388 """
395 """
389 Divide the given string into examples and intervening text,
396 Divide the given string into examples and intervening text,
390 and return them as a list of alternating Examples and strings.
397 and return them as a list of alternating Examples and strings.
391 Line numbers for the Examples are 0-based. The optional
398 Line numbers for the Examples are 0-based. The optional
392 argument `name` is a name identifying this string, and is only
399 argument `name` is a name identifying this string, and is only
393 used for error messages.
400 used for error messages.
394 """
401 """
395
402
396 #print 'Parse string:\n',string # dbg
403 #print 'Parse string:\n',string # dbg
397
404
398 string = string.expandtabs()
405 string = string.expandtabs()
399 # If all lines begin with the same indentation, then strip it.
406 # If all lines begin with the same indentation, then strip it.
400 min_indent = self._min_indent(string)
407 min_indent = self._min_indent(string)
401 if min_indent > 0:
408 if min_indent > 0:
402 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
409 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
403
410
404 output = []
411 output = []
405 charno, lineno = 0, 0
412 charno, lineno = 0, 0
406
413
407 # We make 'all random' tests by adding the '# random' mark to every
414 # We make 'all random' tests by adding the '# random' mark to every
408 # block of output in the test.
415 # block of output in the test.
409 if self._RANDOM_TEST.search(string):
416 if self._RANDOM_TEST.search(string):
410 random_marker = '\n# random'
417 random_marker = '\n# random'
411 else:
418 else:
412 random_marker = ''
419 random_marker = ''
413
420
414 # Whether to convert the input from ipython to python syntax
421 # Whether to convert the input from ipython to python syntax
415 ip2py = False
422 ip2py = False
416 # Find all doctest examples in the string. First, try them as Python
423 # Find all doctest examples in the string. First, try them as Python
417 # examples, then as IPython ones
424 # examples, then as IPython ones
418 terms = list(self._EXAMPLE_RE_PY.finditer(string))
425 terms = list(self._EXAMPLE_RE_PY.finditer(string))
419 if terms:
426 if terms:
420 # Normal Python example
427 # Normal Python example
421 #print '-'*70 # dbg
428 #print '-'*70 # dbg
422 #print 'PyExample, Source:\n',string # dbg
429 #print 'PyExample, Source:\n',string # dbg
423 #print '-'*70 # dbg
430 #print '-'*70 # dbg
424 Example = doctest.Example
431 Example = doctest.Example
425 else:
432 else:
426 # It's an ipython example. Note that IPExamples are run
433 # It's an ipython example. Note that IPExamples are run
427 # in-process, so their syntax must be turned into valid python.
434 # in-process, so their syntax must be turned into valid python.
428 # IPExternalExamples are run out-of-process (via pexpect) so they
435 # IPExternalExamples are run out-of-process (via pexpect) so they
429 # don't need any filtering (a real ipython will be executing them).
436 # don't need any filtering (a real ipython will be executing them).
430 terms = list(self._EXAMPLE_RE_IP.finditer(string))
437 terms = list(self._EXAMPLE_RE_IP.finditer(string))
431 if self._EXTERNAL_IP.search(string):
438 if self._EXTERNAL_IP.search(string):
432 #print '-'*70 # dbg
439 #print '-'*70 # dbg
433 #print 'IPExternalExample, Source:\n',string # dbg
440 #print 'IPExternalExample, Source:\n',string # dbg
434 #print '-'*70 # dbg
441 #print '-'*70 # dbg
435 Example = IPExternalExample
442 Example = IPExternalExample
436 else:
443 else:
437 #print '-'*70 # dbg
444 #print '-'*70 # dbg
438 #print 'IPExample, Source:\n',string # dbg
445 #print 'IPExample, Source:\n',string # dbg
439 #print '-'*70 # dbg
446 #print '-'*70 # dbg
440 Example = IPExample
447 Example = IPExample
441 ip2py = True
448 ip2py = True
442
449
443 for m in terms:
450 for m in terms:
444 # Add the pre-example text to `output`.
451 # Add the pre-example text to `output`.
445 output.append(string[charno:m.start()])
452 output.append(string[charno:m.start()])
446 # Update lineno (lines before this example)
453 # Update lineno (lines before this example)
447 lineno += string.count('\n', charno, m.start())
454 lineno += string.count('\n', charno, m.start())
448 # Extract info from the regexp match.
455 # Extract info from the regexp match.
449 (source, options, want, exc_msg) = \
456 (source, options, want, exc_msg) = \
450 self._parse_example(m, name, lineno,ip2py)
457 self._parse_example(m, name, lineno,ip2py)
451
458
452 # Append the random-output marker (it defaults to empty in most
459 # Append the random-output marker (it defaults to empty in most
453 # cases, it's only non-empty for 'all-random' tests):
460 # cases, it's only non-empty for 'all-random' tests):
454 want += random_marker
461 want += random_marker
455
462
456 if Example is IPExternalExample:
463 if Example is IPExternalExample:
457 options[doctest.NORMALIZE_WHITESPACE] = True
464 options[doctest.NORMALIZE_WHITESPACE] = True
458 want += '\n'
465 want += '\n'
459
466
460 # Create an Example, and add it to the list.
467 # Create an Example, and add it to the list.
461 if not self._IS_BLANK_OR_COMMENT(source):
468 if not self._IS_BLANK_OR_COMMENT(source):
462 output.append(Example(source, want, exc_msg,
469 output.append(Example(source, want, exc_msg,
463 lineno=lineno,
470 lineno=lineno,
464 indent=min_indent+len(m.group('indent')),
471 indent=min_indent+len(m.group('indent')),
465 options=options))
472 options=options))
466 # Update lineno (lines inside this example)
473 # Update lineno (lines inside this example)
467 lineno += string.count('\n', m.start(), m.end())
474 lineno += string.count('\n', m.start(), m.end())
468 # Update charno.
475 # Update charno.
469 charno = m.end()
476 charno = m.end()
470 # Add any remaining post-example text to `output`.
477 # Add any remaining post-example text to `output`.
471 output.append(string[charno:])
478 output.append(string[charno:])
472 return output
479 return output
473
480
474 def _parse_example(self, m, name, lineno,ip2py=False):
481 def _parse_example(self, m, name, lineno,ip2py=False):
475 """
482 """
476 Given a regular expression match from `_EXAMPLE_RE` (`m`),
483 Given a regular expression match from `_EXAMPLE_RE` (`m`),
477 return a pair `(source, want)`, where `source` is the matched
484 return a pair `(source, want)`, where `source` is the matched
478 example's source code (with prompts and indentation stripped);
485 example's source code (with prompts and indentation stripped);
479 and `want` is the example's expected output (with indentation
486 and `want` is the example's expected output (with indentation
480 stripped).
487 stripped).
481
488
482 `name` is the string's name, and `lineno` is the line number
489 `name` is the string's name, and `lineno` is the line number
483 where the example starts; both are used for error messages.
490 where the example starts; both are used for error messages.
484
491
485 Optional:
492 Optional:
486 `ip2py`: if true, filter the input via IPython to convert the syntax
493 `ip2py`: if true, filter the input via IPython to convert the syntax
487 into valid python.
494 into valid python.
488 """
495 """
489
496
490 # Get the example's indentation level.
497 # Get the example's indentation level.
491 indent = len(m.group('indent'))
498 indent = len(m.group('indent'))
492
499
493 # Divide source into lines; check that they're properly
500 # Divide source into lines; check that they're properly
494 # indented; and then strip their indentation & prompts.
501 # indented; and then strip their indentation & prompts.
495 source_lines = m.group('source').split('\n')
502 source_lines = m.group('source').split('\n')
496
503
497 # We're using variable-length input prompts
504 # We're using variable-length input prompts
498 ps1 = m.group('ps1')
505 ps1 = m.group('ps1')
499 ps2 = m.group('ps2')
506 ps2 = m.group('ps2')
500 ps1_len = len(ps1)
507 ps1_len = len(ps1)
501
508
502 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
509 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
503 if ps2:
510 if ps2:
504 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
511 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
505
512
506 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
513 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
507
514
508 if ip2py:
515 if ip2py:
509 # Convert source input from IPython into valid Python syntax
516 # Convert source input from IPython into valid Python syntax
510 source = self.ip2py(source)
517 source = self.ip2py(source)
511
518
512 # Divide want into lines; check that it's properly indented; and
519 # Divide want into lines; check that it's properly indented; and
513 # then strip the indentation. Spaces before the last newline should
520 # then strip the indentation. Spaces before the last newline should
514 # be preserved, so plain rstrip() isn't good enough.
521 # be preserved, so plain rstrip() isn't good enough.
515 want = m.group('want')
522 want = m.group('want')
516 want_lines = want.split('\n')
523 want_lines = want.split('\n')
517 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
524 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
518 del want_lines[-1] # forget final newline & spaces after it
525 del want_lines[-1] # forget final newline & spaces after it
519 self._check_prefix(want_lines, ' '*indent, name,
526 self._check_prefix(want_lines, ' '*indent, name,
520 lineno + len(source_lines))
527 lineno + len(source_lines))
521
528
522 # Remove ipython output prompt that might be present in the first line
529 # Remove ipython output prompt that might be present in the first line
523 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
530 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
524
531
525 want = '\n'.join([wl[indent:] for wl in want_lines])
532 want = '\n'.join([wl[indent:] for wl in want_lines])
526
533
527 # If `want` contains a traceback message, then extract it.
534 # If `want` contains a traceback message, then extract it.
528 m = self._EXCEPTION_RE.match(want)
535 m = self._EXCEPTION_RE.match(want)
529 if m:
536 if m:
530 exc_msg = m.group('msg')
537 exc_msg = m.group('msg')
531 else:
538 else:
532 exc_msg = None
539 exc_msg = None
533
540
534 # Extract options from the source.
541 # Extract options from the source.
535 options = self._find_options(source, name, lineno)
542 options = self._find_options(source, name, lineno)
536
543
537 return source, options, want, exc_msg
544 return source, options, want, exc_msg
538
545
539 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
546 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
540 """
547 """
541 Given the lines of a source string (including prompts and
548 Given the lines of a source string (including prompts and
542 leading indentation), check to make sure that every prompt is
549 leading indentation), check to make sure that every prompt is
543 followed by a space character. If any line is not followed by
550 followed by a space character. If any line is not followed by
544 a space character, then raise ValueError.
551 a space character, then raise ValueError.
545
552
546 Note: IPython-modified version which takes the input prompt length as a
553 Note: IPython-modified version which takes the input prompt length as a
547 parameter, so that prompts of variable length can be dealt with.
554 parameter, so that prompts of variable length can be dealt with.
548 """
555 """
549 space_idx = indent+ps1_len
556 space_idx = indent+ps1_len
550 min_len = space_idx+1
557 min_len = space_idx+1
551 for i, line in enumerate(lines):
558 for i, line in enumerate(lines):
552 if len(line) >= min_len and line[space_idx] != ' ':
559 if len(line) >= min_len and line[space_idx] != ' ':
553 raise ValueError('line %r of the docstring for %s '
560 raise ValueError('line %r of the docstring for %s '
554 'lacks blank after %s: %r' %
561 'lacks blank after %s: %r' %
555 (lineno+i+1, name,
562 (lineno+i+1, name,
556 line[indent:space_idx], line))
563 line[indent:space_idx], line))
557
564
558
565
559 SKIP = doctest.register_optionflag('SKIP')
566 SKIP = doctest.register_optionflag('SKIP')
560
567
561
568
562 class IPDocTestRunner(doctest.DocTestRunner,object):
569 class IPDocTestRunner(doctest.DocTestRunner,object):
563 """Test runner that synchronizes the IPython namespace with test globals.
570 """Test runner that synchronizes the IPython namespace with test globals.
564 """
571 """
565
572
566 def run(self, test, compileflags=None, out=None, clear_globs=True):
573 def run(self, test, compileflags=None, out=None, clear_globs=True):
567
574
568 # Hack: ipython needs access to the execution context of the example,
575 # Hack: ipython needs access to the execution context of the example,
569 # so that it can propagate user variables loaded by %run into
576 # so that it can propagate user variables loaded by %run into
570 # test.globs. We put them here into our modified %run as a function
577 # test.globs. We put them here into our modified %run as a function
571 # attribute. Our new %run will then only make the namespace update
578 # attribute. Our new %run will then only make the namespace update
572 # when called (rather than unconconditionally updating test.globs here
579 # when called (rather than unconconditionally updating test.globs here
573 # for all examples, most of which won't be calling %run anyway).
580 # for all examples, most of which won't be calling %run anyway).
574 #_ip._ipdoctest_test_globs = test.globs
581 #_ip._ipdoctest_test_globs = test.globs
575 #_ip._ipdoctest_test_filename = test.filename
582 #_ip._ipdoctest_test_filename = test.filename
576
583
577 test.globs.update(_ip.user_ns)
584 test.globs.update(_ip.user_ns)
578
585
579 return super(IPDocTestRunner,self).run(test,
586 return super(IPDocTestRunner,self).run(test,
580 compileflags,out,clear_globs)
587 compileflags,out,clear_globs)
581
588
582
589
583 class DocFileCase(doctest.DocFileCase):
590 class DocFileCase(doctest.DocFileCase):
584 """Overrides to provide filename
591 """Overrides to provide filename
585 """
592 """
586 def address(self):
593 def address(self):
587 return (self._dt_test.filename, None, None)
594 return (self._dt_test.filename, None, None)
588
595
589
596
590 class ExtensionDoctest(doctests.Doctest):
597 class ExtensionDoctest(doctests.Doctest):
591 """Nose Plugin that supports doctests in extension modules.
598 """Nose Plugin that supports doctests in extension modules.
592 """
599 """
593 name = 'extdoctest' # call nosetests with --with-extdoctest
600 name = 'extdoctest' # call nosetests with --with-extdoctest
594 enabled = True
601 enabled = True
595
602
596 def __init__(self,exclude_patterns=None):
603 def __init__(self,exclude_patterns=None):
597 """Create a new ExtensionDoctest plugin.
604 """Create a new ExtensionDoctest plugin.
598
605
599 Parameters
606 Parameters
600 ----------
607 ----------
601
608
602 exclude_patterns : sequence of strings, optional
609 exclude_patterns : sequence of strings, optional
603 These patterns are compiled as regular expressions, subsequently used
610 These patterns are compiled as regular expressions, subsequently used
604 to exclude any filename which matches them from inclusion in the test
611 to exclude any filename which matches them from inclusion in the test
605 suite (using pattern.search(), NOT pattern.match() ).
612 suite (using pattern.search(), NOT pattern.match() ).
606 """
613 """
607
614
608 if exclude_patterns is None:
615 if exclude_patterns is None:
609 exclude_patterns = []
616 exclude_patterns = []
610 self.exclude_patterns = map(re.compile,exclude_patterns)
617 self.exclude_patterns = map(re.compile,exclude_patterns)
611 doctests.Doctest.__init__(self)
618 doctests.Doctest.__init__(self)
612
619
613 def options(self, parser, env=os.environ):
620 def options(self, parser, env=os.environ):
614 Plugin.options(self, parser, env)
621 Plugin.options(self, parser, env)
615 parser.add_option('--doctest-tests', action='store_true',
622 parser.add_option('--doctest-tests', action='store_true',
616 dest='doctest_tests',
623 dest='doctest_tests',
617 default=env.get('NOSE_DOCTEST_TESTS',True),
624 default=env.get('NOSE_DOCTEST_TESTS',True),
618 help="Also look for doctests in test modules. "
625 help="Also look for doctests in test modules. "
619 "Note that classes, methods and functions should "
626 "Note that classes, methods and functions should "
620 "have either doctests or non-doctest tests, "
627 "have either doctests or non-doctest tests, "
621 "not both. [NOSE_DOCTEST_TESTS]")
628 "not both. [NOSE_DOCTEST_TESTS]")
622 parser.add_option('--doctest-extension', action="append",
629 parser.add_option('--doctest-extension', action="append",
623 dest="doctestExtension",
630 dest="doctestExtension",
624 help="Also look for doctests in files with "
631 help="Also look for doctests in files with "
625 "this extension [NOSE_DOCTEST_EXTENSION]")
632 "this extension [NOSE_DOCTEST_EXTENSION]")
626 # Set the default as a list, if given in env; otherwise
633 # Set the default as a list, if given in env; otherwise
627 # an additional value set on the command line will cause
634 # an additional value set on the command line will cause
628 # an error.
635 # an error.
629 env_setting = env.get('NOSE_DOCTEST_EXTENSION')
636 env_setting = env.get('NOSE_DOCTEST_EXTENSION')
630 if env_setting is not None:
637 if env_setting is not None:
631 parser.set_defaults(doctestExtension=tolist(env_setting))
638 parser.set_defaults(doctestExtension=tolist(env_setting))
632
639
633
640
634 def configure(self, options, config):
641 def configure(self, options, config):
635 Plugin.configure(self, options, config)
642 Plugin.configure(self, options, config)
636 self.doctest_tests = options.doctest_tests
643 self.doctest_tests = options.doctest_tests
637 self.extension = tolist(options.doctestExtension)
644 self.extension = tolist(options.doctestExtension)
638
645
639 self.parser = doctest.DocTestParser()
646 self.parser = doctest.DocTestParser()
640 self.finder = DocTestFinder()
647 self.finder = DocTestFinder()
641 self.checker = IPDoctestOutputChecker()
648 self.checker = IPDoctestOutputChecker()
642 self.globs = None
649 self.globs = None
643 self.extraglobs = None
650 self.extraglobs = None
644
651
645
652
646 def loadTestsFromExtensionModule(self,filename):
653 def loadTestsFromExtensionModule(self,filename):
647 bpath,mod = os.path.split(filename)
654 bpath,mod = os.path.split(filename)
648 modname = os.path.splitext(mod)[0]
655 modname = os.path.splitext(mod)[0]
649 try:
656 try:
650 sys.path.append(bpath)
657 sys.path.append(bpath)
651 module = __import__(modname)
658 module = __import__(modname)
652 tests = list(self.loadTestsFromModule(module))
659 tests = list(self.loadTestsFromModule(module))
653 finally:
660 finally:
654 sys.path.pop()
661 sys.path.pop()
655 return tests
662 return tests
656
663
657 # NOTE: the method below is almost a copy of the original one in nose, with
664 # NOTE: the method below is almost a copy of the original one in nose, with
658 # a few modifications to control output checking.
665 # a few modifications to control output checking.
659
666
660 def loadTestsFromModule(self, module):
667 def loadTestsFromModule(self, module):
661 #print '*** ipdoctest - lTM',module # dbg
668 #print '*** ipdoctest - lTM',module # dbg
662
669
663 if not self.matches(module.__name__):
670 if not self.matches(module.__name__):
664 log.debug("Doctest doesn't want module %s", module)
671 log.debug("Doctest doesn't want module %s", module)
665 return
672 return
666
673
667 tests = self.finder.find(module,globs=self.globs,
674 tests = self.finder.find(module,globs=self.globs,
668 extraglobs=self.extraglobs)
675 extraglobs=self.extraglobs)
669 if not tests:
676 if not tests:
670 return
677 return
671
678
672 # always use whitespace and ellipsis options
679 # always use whitespace and ellipsis options
673 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
680 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
674
681
675 tests.sort()
682 tests.sort()
676 module_file = module.__file__
683 module_file = module.__file__
677 if module_file[-4:] in ('.pyc', '.pyo'):
684 if module_file[-4:] in ('.pyc', '.pyo'):
678 module_file = module_file[:-1]
685 module_file = module_file[:-1]
679 for test in tests:
686 for test in tests:
680 if not test.examples:
687 if not test.examples:
681 continue
688 continue
682 if not test.filename:
689 if not test.filename:
683 test.filename = module_file
690 test.filename = module_file
684
691
685 yield DocTestCase(test,
692 yield DocTestCase(test,
686 optionflags=optionflags,
693 optionflags=optionflags,
687 checker=self.checker)
694 checker=self.checker)
688
695
689
696
690 def loadTestsFromFile(self, filename):
697 def loadTestsFromFile(self, filename):
691 #print "ipdoctest - from file", filename # dbg
698 #print "ipdoctest - from file", filename # dbg
692 if is_extension_module(filename):
699 if is_extension_module(filename):
693 for t in self.loadTestsFromExtensionModule(filename):
700 for t in self.loadTestsFromExtensionModule(filename):
694 yield t
701 yield t
695 else:
702 else:
696 if self.extension and anyp(filename.endswith, self.extension):
703 if self.extension and anyp(filename.endswith, self.extension):
697 name = os.path.basename(filename)
704 name = os.path.basename(filename)
698 dh = open(filename)
705 dh = open(filename)
699 try:
706 try:
700 doc = dh.read()
707 doc = dh.read()
701 finally:
708 finally:
702 dh.close()
709 dh.close()
703 test = self.parser.get_doctest(
710 test = self.parser.get_doctest(
704 doc, globs={'__file__': filename}, name=name,
711 doc, globs={'__file__': filename}, name=name,
705 filename=filename, lineno=0)
712 filename=filename, lineno=0)
706 if test.examples:
713 if test.examples:
707 #print 'FileCase:',test.examples # dbg
714 #print 'FileCase:',test.examples # dbg
708 yield DocFileCase(test)
715 yield DocFileCase(test)
709 else:
716 else:
710 yield False # no tests to load
717 yield False # no tests to load
711
718
712 def wantFile(self,filename):
719 def wantFile(self,filename):
713 """Return whether the given filename should be scanned for tests.
720 """Return whether the given filename should be scanned for tests.
714
721
715 Modified version that accepts extension modules as valid containers for
722 Modified version that accepts extension modules as valid containers for
716 doctests.
723 doctests.
717 """
724 """
718 #print '*** ipdoctest- wantFile:',filename # dbg
725 #print '*** ipdoctest- wantFile:',filename # dbg
719
726
720 for pat in self.exclude_patterns:
727 for pat in self.exclude_patterns:
721 if pat.search(filename):
728 if pat.search(filename):
722 # print '###>>> SKIP:',filename # dbg
729 # print '###>>> SKIP:',filename # dbg
723 return False
730 return False
724
731
725 if is_extension_module(filename):
732 if is_extension_module(filename):
726 return True
733 return True
727 else:
734 else:
728 return doctests.Doctest.wantFile(self,filename)
735 return doctests.Doctest.wantFile(self,filename)
729
736
730
737
731 class IPythonDoctest(ExtensionDoctest):
738 class IPythonDoctest(ExtensionDoctest):
732 """Nose Plugin that supports doctests in extension modules.
739 """Nose Plugin that supports doctests in extension modules.
733 """
740 """
734 name = 'ipdoctest' # call nosetests with --with-ipdoctest
741 name = 'ipdoctest' # call nosetests with --with-ipdoctest
735 enabled = True
742 enabled = True
736
743
737 def makeTest(self, obj, parent):
744 def makeTest(self, obj, parent):
738 """Look for doctests in the given object, which will be a
745 """Look for doctests in the given object, which will be a
739 function, method or class.
746 function, method or class.
740 """
747 """
741 #print 'Plugin analyzing:', obj, parent # dbg
748 #print 'Plugin analyzing:', obj, parent # dbg
742 # always use whitespace and ellipsis options
749 # always use whitespace and ellipsis options
743 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
750 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
744
751
745 doctests = self.finder.find(obj, module=getmodule(parent))
752 doctests = self.finder.find(obj, module=getmodule(parent))
746 if doctests:
753 if doctests:
747 for test in doctests:
754 for test in doctests:
748 if len(test.examples) == 0:
755 if len(test.examples) == 0:
749 continue
756 continue
750
757
751 yield DocTestCase(test, obj=obj,
758 yield DocTestCase(test, obj=obj,
752 optionflags=optionflags,
759 optionflags=optionflags,
753 checker=self.checker)
760 checker=self.checker)
754
761
755 def options(self, parser, env=os.environ):
762 def options(self, parser, env=os.environ):
756 #print "Options for nose plugin:", self.name # dbg
763 #print "Options for nose plugin:", self.name # dbg
757 Plugin.options(self, parser, env)
764 Plugin.options(self, parser, env)
758 parser.add_option('--ipdoctest-tests', action='store_true',
765 parser.add_option('--ipdoctest-tests', action='store_true',
759 dest='ipdoctest_tests',
766 dest='ipdoctest_tests',
760 default=env.get('NOSE_IPDOCTEST_TESTS',True),
767 default=env.get('NOSE_IPDOCTEST_TESTS',True),
761 help="Also look for doctests in test modules. "
768 help="Also look for doctests in test modules. "
762 "Note that classes, methods and functions should "
769 "Note that classes, methods and functions should "
763 "have either doctests or non-doctest tests, "
770 "have either doctests or non-doctest tests, "
764 "not both. [NOSE_IPDOCTEST_TESTS]")
771 "not both. [NOSE_IPDOCTEST_TESTS]")
765 parser.add_option('--ipdoctest-extension', action="append",
772 parser.add_option('--ipdoctest-extension', action="append",
766 dest="ipdoctest_extension",
773 dest="ipdoctest_extension",
767 help="Also look for doctests in files with "
774 help="Also look for doctests in files with "
768 "this extension [NOSE_IPDOCTEST_EXTENSION]")
775 "this extension [NOSE_IPDOCTEST_EXTENSION]")
769 # Set the default as a list, if given in env; otherwise
776 # Set the default as a list, if given in env; otherwise
770 # an additional value set on the command line will cause
777 # an additional value set on the command line will cause
771 # an error.
778 # an error.
772 env_setting = env.get('NOSE_IPDOCTEST_EXTENSION')
779 env_setting = env.get('NOSE_IPDOCTEST_EXTENSION')
773 if env_setting is not None:
780 if env_setting is not None:
774 parser.set_defaults(ipdoctest_extension=tolist(env_setting))
781 parser.set_defaults(ipdoctest_extension=tolist(env_setting))
775
782
776 def configure(self, options, config):
783 def configure(self, options, config):
777 #print "Configuring nose plugin:", self.name # dbg
784 #print "Configuring nose plugin:", self.name # dbg
778 Plugin.configure(self, options, config)
785 Plugin.configure(self, options, config)
779 self.doctest_tests = options.ipdoctest_tests
786 self.doctest_tests = options.ipdoctest_tests
780 self.extension = tolist(options.ipdoctest_extension)
787 self.extension = tolist(options.ipdoctest_extension)
781
788
782 self.parser = IPDocTestParser()
789 self.parser = IPDocTestParser()
783 self.finder = DocTestFinder(parser=self.parser)
790 self.finder = DocTestFinder(parser=self.parser)
784 self.checker = IPDoctestOutputChecker()
791 self.checker = IPDoctestOutputChecker()
785 self.globs = None
792 self.globs = None
786 self.extraglobs = None
793 self.extraglobs = None
General Comments 0
You need to be logged in to leave comments. Login now