##// END OF EJS Templates
Fix tests when ipdoctest nose plugin is enable (Grrr, no isolation at...
Gael Varoquaux -
Show More
@@ -1,220 +1,221 b''
1 1 """
2 2 Frontend class that uses IPython0 to prefilter the inputs.
3 3
4 4 Using the IPython0 mechanism gives us access to the magics.
5 5
6 6 This is a transitory class, used here to do the transition between
7 7 ipython0 and ipython1. This class is meant to be short-lived as more
8 8 functionnality is abstracted out of ipython0 in reusable functions and
9 9 is added on the interpreter. This class can be a used to guide this
10 10 refactoring.
11 11 """
12 12 __docformat__ = "restructuredtext en"
13 13
14 14 #-------------------------------------------------------------------------------
15 15 # Copyright (C) 2008 The IPython Development Team
16 16 #
17 17 # Distributed under the terms of the BSD License. The full license is in
18 18 # the file COPYING, distributed as part of this software.
19 19 #-------------------------------------------------------------------------------
20 20
21 21 #-------------------------------------------------------------------------------
22 22 # Imports
23 23 #-------------------------------------------------------------------------------
24 24 import sys
25 25
26 26 from linefrontendbase import LineFrontEndBase, common_prefix
27 27
28 28 from IPython.ipmaker import make_IPython
29 29 from IPython.ipapi import IPApi
30 30 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
31 31
32 32 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
33 33
34 34 from IPython.genutils import Term
35 35 import pydoc
36 36 import os
37 37
38 38
39 39 def mk_system_call(system_call_function, command):
40 40 """ given a os.system replacement, and a leading string command,
41 41 returns a function that will execute the command with the given
42 42 argument string.
43 43 """
44 44 def my_system_call(args):
45 45 system_call_function("%s %s" % (command, args))
46 46 return my_system_call
47 47
48 48 #-------------------------------------------------------------------------------
49 49 # Frontend class using ipython0 to do the prefiltering.
50 50 #-------------------------------------------------------------------------------
51 51 class PrefilterFrontEnd(LineFrontEndBase):
52 52 """ Class that uses ipython0 to do prefilter the input, do the
53 53 completion and the magics.
54 54
55 55 The core trick is to use an ipython0 instance to prefilter the
56 56 input, and share the namespace between the interpreter instance used
57 57 to execute the statements and the ipython0 used for code
58 58 completion...
59 59 """
60 60
61 61 def __init__(self, ipython0=None, *args, **kwargs):
62 62 """ Parameters:
63 63 -----------
64 64
65 65 ipython0: an optional ipython0 instance to use for command
66 66 prefiltering and completion.
67 67 """
68 68 self.save_output_hooks()
69 69 if ipython0 is None:
70 70 # Instanciate an IPython0 interpreter to be able to use the
71 71 # prefiltering.
72 ipython0 = make_IPython()
72 # XXX: argv=[] is a bit bold.
73 ipython0 = make_IPython(argv=[])
73 74 self.ipython0 = ipython0
74 75 # Set the pager:
75 76 self.ipython0.set_hook('show_in_pager',
76 77 lambda s, string: self.write("\n" + string))
77 78 self.ipython0.write = self.write
78 79 self._ip = _ip = IPApi(self.ipython0)
79 80 # Make sure the raw system call doesn't get called, as we don't
80 81 # have a stdin accessible.
81 82 self._ip.system = self.system_call
82 83 # XXX: Muck around with magics so that they work better
83 84 # in our environment
84 85 self.ipython0.magic_ls = mk_system_call(self.system_call,
85 86 'ls -CF')
86 87 # And now clean up the mess created by ipython0
87 88 self.release_output()
88 if not 'banner' in kwargs:
89 if not 'banner' in kwargs and self.banner is None:
89 90 kwargs['banner'] = self.ipython0.BANNER + """
90 91 This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""
91 92
92 93 LineFrontEndBase.__init__(self, *args, **kwargs)
93 94 # XXX: Hack: mix the two namespaces
94 95 self.shell.user_ns = self.ipython0.user_ns
95 96 self.shell.user_global_ns = self.ipython0.user_global_ns
96 97
97 98 self.shell.output_trap = RedirectorOutputTrap(
98 99 out_callback=self.write,
99 100 err_callback=self.write,
100 101 )
101 102 self.shell.traceback_trap = SyncTracebackTrap(
102 103 formatters=self.shell.traceback_trap.formatters,
103 104 )
104 105
105 106 #--------------------------------------------------------------------------
106 107 # FrontEndBase interface
107 108 #--------------------------------------------------------------------------
108 109
109 110 def show_traceback(self):
110 111 """ Use ipython0 to capture the last traceback and display it.
111 112 """
112 113 self.capture_output()
113 114 self.ipython0.showtraceback()
114 115 self.release_output()
115 116
116 117
117 118 def execute(self, python_string, raw_string=None):
118 119 if self.debug:
119 120 print 'Executing Python code:', repr(python_string)
120 121 self.capture_output()
121 122 LineFrontEndBase.execute(self, python_string,
122 123 raw_string=raw_string)
123 124 self.release_output()
124 125
125 126
126 127 def save_output_hooks(self):
127 128 """ Store all the output hooks we can think of, to be able to
128 129 restore them.
129 130
130 131 We need to do this early, as starting the ipython0 instance will
131 132 screw ouput hooks.
132 133 """
133 134 self.__old_cout_write = Term.cout.write
134 135 self.__old_cerr_write = Term.cerr.write
135 136 self.__old_stdout = sys.stdout
136 137 self.__old_stderr= sys.stderr
137 138 self.__old_help_output = pydoc.help.output
138 139 self.__old_display_hook = sys.displayhook
139 140
140 141
141 142 def capture_output(self):
142 143 """ Capture all the output mechanisms we can think of.
143 144 """
144 145 self.save_output_hooks()
145 146 Term.cout.write = self.write
146 147 Term.cerr.write = self.write
147 148 sys.stdout = Term.cout
148 149 sys.stderr = Term.cerr
149 150 pydoc.help.output = self.shell.output_trap.out
150 151
151 152
152 153 def release_output(self):
153 154 """ Release all the different captures we have made.
154 155 """
155 156 Term.cout.write = self.__old_cout_write
156 157 Term.cerr.write = self.__old_cerr_write
157 158 sys.stdout = self.__old_stdout
158 159 sys.stderr = self.__old_stderr
159 160 pydoc.help.output = self.__old_help_output
160 161 sys.displayhook = self.__old_display_hook
161 162
162 163
163 164 def complete(self, line):
164 165 word = line.split('\n')[-1].split(' ')[-1]
165 166 completions = self.ipython0.complete(word)
166 167 # FIXME: The proper sort should be done in the complete method.
167 168 key = lambda x: x.replace('_', '')
168 169 completions.sort(key=key)
169 170 if completions:
170 171 prefix = common_prefix(completions)
171 172 line = line[:-len(word)] + prefix
172 173 return line, completions
173 174
174 175
175 176 #--------------------------------------------------------------------------
176 177 # LineFrontEndBase interface
177 178 #--------------------------------------------------------------------------
178 179
179 180 def prefilter_input(self, input_string):
180 181 """ Using IPython0 to prefilter the commands to turn them
181 182 in executable statements that are valid Python strings.
182 183 """
183 184 input_string = LineFrontEndBase.prefilter_input(self, input_string)
184 185 filtered_lines = []
185 186 # The IPython0 prefilters sometime produce output. We need to
186 187 # capture it.
187 188 self.capture_output()
188 189 self.last_result = dict(number=self.prompt_number)
189 190 try:
190 191 for line in input_string.split('\n'):
191 192 filtered_lines.append(
192 193 self.ipython0.prefilter(line, False).rstrip())
193 194 except:
194 195 # XXX: probably not the right thing to do.
195 196 self.ipython0.showsyntaxerror()
196 197 self.after_execute()
197 198 finally:
198 199 self.release_output()
199 200
200 201 # Clean up the trailing whitespace, to avoid indentation errors
201 202 filtered_string = '\n'.join(filtered_lines)
202 203 return filtered_string
203 204
204 205
205 206 #--------------------------------------------------------------------------
206 207 # PrefilterFrontEnd interface
207 208 #--------------------------------------------------------------------------
208 209
209 210 def system_call(self, command_string):
210 211 """ Allows for frontend to define their own system call, to be
211 212 able capture output and redirect input.
212 213 """
213 214 return os.system(command_string)
214 215
215 216
216 217 def do_exit(self):
217 218 """ Exit the shell, cleanup and save the history.
218 219 """
219 220 self.ipython0.atexit_operations()
220 221
@@ -1,132 +1,157 b''
1 1 # encoding: utf-8
2 2 """
3 3 Test process execution and IO redirection.
4 4 """
5 5
6 6 __docformat__ = "restructuredtext en"
7 7
8 8 #-------------------------------------------------------------------------------
9 9 # Copyright (C) 2008 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is
12 12 # in the file COPYING, distributed as part of this software.
13 13 #-------------------------------------------------------------------------------
14 14
15 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
16 15 from cStringIO import StringIO
17 16 import string
18 import sys
17
19 18 from IPython.ipapi import get as get_ipython0
19 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
20 20
21 21 class TestPrefilterFrontEnd(PrefilterFrontEnd):
22 22
23 23 input_prompt_template = string.Template('')
24 24 output_prompt_template = string.Template('')
25 banner = ''
25 26
26 27 def __init__(self):
27 ipython0 = get_ipython0()
28 ipython0 = get_ipython0().IP
28 29 self.out = StringIO()
29 30 PrefilterFrontEnd.__init__(self, ipython0=ipython0)
30
31 def write(self, string):
31 # Clean up the namespace for isolation between tests
32 user_ns = self.ipython0.user_ns
33 # We need to keep references to things so that they don't
34 # get garbage collected (this stinks).
35 self.shadow_ns = dict()
36 for i in self.ipython0.magic_who_ls():
37 self.shadow_ns[i] = user_ns.pop(i)
38 # Some more code for isolation (yeah, crazy)
39 self._on_enter()
40 self.out.flush()
41 self.out.reset()
42 self.out.truncate()
43
44 def write(self, string, *args, **kwargs):
32 45 self.out.write(string)
33 46
34 47 def _on_enter(self):
35 48 self.input_buffer += '\n'
36 49 PrefilterFrontEnd._on_enter(self)
37 50
38 51
39 52 def test_execution():
40 53 """ Test execution of a command.
41 54 """
42 55 f = TestPrefilterFrontEnd()
43 f.input_buffer = 'print 1\n'
56 f.input_buffer = 'print 1'
44 57 f._on_enter()
45 assert f.out.getvalue() == '1\n'
58 out_value = f.out.getvalue()
59 assert out_value == '1\n'
46 60
47 61
48 62 def test_multiline():
49 63 """ Test execution of a multiline command.
50 64 """
51 65 f = TestPrefilterFrontEnd()
52 66 f.input_buffer = 'if True:'
53 67 f._on_enter()
54 68 f.input_buffer += 'print 1'
55 69 f._on_enter()
56 assert f.out.getvalue() == ''
70 out_value = f.out.getvalue()
71 assert out_value == ''
57 72 f._on_enter()
58 assert f.out.getvalue() == '1\n'
73 out_value = f.out.getvalue()
74 assert out_value == '1\n'
59 75 f = TestPrefilterFrontEnd()
60 76 f.input_buffer='(1 +'
61 77 f._on_enter()
62 78 f.input_buffer += '0)'
63 79 f._on_enter()
64 assert f.out.getvalue() == ''
80 out_value = f.out.getvalue()
81 assert out_value == ''
65 82 f._on_enter()
66 assert f.out.getvalue() == '1\n'
83 out_value = f.out.getvalue()
84 assert out_value == '1\n'
67 85
68 86
69 87 def test_capture():
70 88 """ Test the capture of output in different channels.
71 89 """
72 90 # Test on the OS-level stdout, stderr.
73 91 f = TestPrefilterFrontEnd()
74 92 f.input_buffer = \
75 93 'import os; out=os.fdopen(1, "w"); out.write("1") ; out.flush()'
76 94 f._on_enter()
77 assert f.out.getvalue() == '1'
95 out_value = f.out.getvalue()
96 assert out_value == '1'
78 97 f = TestPrefilterFrontEnd()
79 98 f.input_buffer = \
80 99 'import os; out=os.fdopen(2, "w"); out.write("1") ; out.flush()'
81 100 f._on_enter()
82 assert f.out.getvalue() == '1'
101 out_value = f.out.getvalue()
102 assert out_value == '1'
83 103
84 104
85 105 def test_magic():
86 106 """ Test the magic expansion and history.
87 107
88 108 This test is fairly fragile and will break when magics change.
89 109 """
90 110 f = TestPrefilterFrontEnd()
91 f.input_buffer += '%who\n'
111 f.input_buffer += '%who'
92 112 f._on_enter()
93 assert f.out.getvalue() == 'Interactive namespace is empty.\n'
113 out_value = f.out.getvalue()
114 assert out_value == 'Interactive namespace is empty.\n'
94 115
95 116
96 117 def test_help():
97 118 """ Test object inspection.
98 119 """
99 120 f = TestPrefilterFrontEnd()
100 121 f.input_buffer += "def f():"
101 122 f._on_enter()
102 123 f.input_buffer += "'foobar'"
103 124 f._on_enter()
104 125 f.input_buffer += "pass"
105 126 f._on_enter()
106 127 f._on_enter()
107 128 f.input_buffer += "f?"
108 129 f._on_enter()
109 assert f.out.getvalue().split()[-1] == 'foobar'
130 assert 'traceback' not in f.last_result
131 ## XXX: ipython doctest magic breaks this. I have no clue why
132 #out_value = f.out.getvalue()
133 #assert out_value.split()[-1] == 'foobar'
110 134
111 135
112 136 def test_completion():
113 137 """ Test command-line completion.
114 138 """
115 139 f = TestPrefilterFrontEnd()
116 140 f.input_buffer = 'zzza = 1'
117 141 f._on_enter()
118 142 f.input_buffer = 'zzzb = 2'
119 143 f._on_enter()
120 144 f.input_buffer = 'zz'
121 145 f.complete_current_input()
122 assert f.out.getvalue() == '\nzzza zzzb '
146 out_value = f.out.getvalue()
147 assert out_value == '\nzzza zzzb '
123 148 assert f.input_buffer == 'zzz'
124 149
125 150
126 151 if __name__ == '__main__':
127 152 test_magic()
128 153 test_help()
129 154 test_execution()
130 155 test_multiline()
131 156 test_capture()
132 157 test_completion()
@@ -1,133 +1,135 b''
1 1 """Decorators for labeling test objects.
2 2
3 3 Decorators that merely return a modified version of the original
4 4 function object are straightforward. Decorators that return a new
5 5 function object need to use
6 6 nose.tools.make_decorator(original_function)(decorator) in returning
7 7 the decorator, in order to preserve metadata such as function name,
8 8 setup and teardown functions and so on - see nose.tools for more
9 9 information.
10 10
11 11 NOTE: This file contains IPython-specific decorators and imports the
12 12 numpy.testing.decorators file, which we've copied verbatim. Any of our own
13 13 code will be added at the bottom if we end up extending this.
14 14 """
15 15
16 16 # Stdlib imports
17 17 import inspect
18 18
19 19 # Third-party imports
20 20
21 21 # This is Michele Simionato's decorator module, also kept verbatim.
22 22 from decorator_msim import decorator, update_wrapper
23 23
24 24 # Grab the numpy-specific decorators which we keep in a file that we
25 25 # occasionally update from upstream: decorators_numpy.py is an IDENTICAL copy
26 26 # of numpy.testing.decorators.
27 27 from decorators_numpy import *
28 28
29 29 ##############################################################################
30 30 # Local code begins
31 31
32 32 # Utility functions
33 33
34 34 def apply_wrapper(wrapper,func):
35 35 """Apply a wrapper to a function for decoration.
36 36
37 37 This mixes Michele Simionato's decorator tool with nose's make_decorator,
38 38 to apply a wrapper in a decorator so that all nose attributes, as well as
39 39 function signature and other properties, survive the decoration cleanly.
40 40 This will ensure that wrapped functions can still be well introspected via
41 41 IPython, for example.
42 42 """
43 43 import nose.tools
44 44
45 45 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
46 46
47 47
48 48 def make_label_dec(label,ds=None):
49 49 """Factory function to create a decorator that applies one or more labels.
50 50
51 51 :Parameters:
52 52 label : string or sequence
53 53 One or more labels that will be applied by the decorator to the functions
54 54 it decorates. Labels are attributes of the decorated function with their
55 55 value set to True.
56 56
57 57 :Keywords:
58 58 ds : string
59 59 An optional docstring for the resulting decorator. If not given, a
60 60 default docstring is auto-generated.
61 61
62 62 :Returns:
63 63 A decorator.
64 64
65 65 :Examples:
66 66
67 67 A simple labeling decorator:
68 68 >>> slow = make_label_dec('slow')
69 69 >>> print slow.__doc__
70 70 Labels a test as 'slow'.
71 71
72 72 And one that uses multiple labels and a custom docstring:
73 73 >>> rare = make_label_dec(['slow','hard'],
74 74 ... "Mix labels 'slow' and 'hard' for rare tests.")
75 75 >>> print rare.__doc__
76 76 Mix labels 'slow' and 'hard' for rare tests.
77 77
78 78 Now, let's test using this one:
79 79 >>> @rare
80 80 ... def f(): pass
81 81 ...
82 82 >>>
83 83 >>> f.slow
84 84 True
85 85 >>> f.hard
86 86 True
87 87 """
88 88
89 89 if isinstance(label,basestring):
90 90 labels = [label]
91 91 else:
92 92 labels = label
93 93
94 94 # Validate that the given label(s) are OK for use in setattr() by doing a
95 95 # dry run on a dummy function.
96 96 tmp = lambda : None
97 97 for label in labels:
98 98 setattr(tmp,label,True)
99 99
100 100 # This is the actual decorator we'll return
101 101 def decor(f):
102 102 for label in labels:
103 103 setattr(f,label,True)
104 104 return f
105 105
106 106 # Apply the user's docstring, or autogenerate a basic one
107 107 if ds is None:
108 108 ds = "Labels a test as %r." % label
109 109 decor.__doc__ = ds
110 110
111 111 return decor
112 112
113 113 #-----------------------------------------------------------------------------
114 114 # Decorators for public use
115 115
116 116 skip_doctest = make_label_dec('skip_doctest',
117 117 """Decorator - mark a function or method for skipping its doctest.
118 118
119 119 This decorator allows you to mark a function whose docstring you wish to
120 120 omit from testing, while preserving the docstring for introspection, help,
121 121 etc.""")
122 122
123 123
124 124 def skip(func):
125 125 """Decorator - mark a test function for skipping from test suite."""
126 126
127 127 import nose
128 128
129 129 def wrapper(*a,**k):
130 130 raise nose.SkipTest("Skipping test for function: %s" %
131 131 func.__name__)
132 132
133 133 return apply_wrapper(wrapper,func)
134
135
@@ -1,54 +1,54 b''
1 1 # Set this prefix to where you want to install the plugin
2 2 PREFIX=~/usr/local
3 3 PREFIX=~/tmp/local
4 4
5 NOSE0=nosetests -vs --with-doctest --doctest-tests
6 NOSE=nosetests -vvs --with-ipdoctest --doctest-tests --doctest-extension=txt
5 NOSE0=nosetests -vs --with-doctest --doctest-tests --detailed-errors
6 NOSE=nosetests -vvs --with-ipdoctest --doctest-tests --doctest-extension=txt --detailed-errors
7 7
8 8 #--with-color
9 9
10 10 SRC=ipdoctest.py setup.py ../decorators.py
11 11
12 12 plugin: IPython_doctest_plugin.egg-info
13 13
14 14 dtest: plugin dtexample.py
15 15 $(NOSE) dtexample.py
16 16
17 17 # Note: this test is double counting!!!
18 18 rtest: plugin dtexample.py
19 19 $(NOSE) test_refs.py
20 20
21 21 std: plugin
22 22 nosetests -vs --with-doctest --doctest-tests IPython.strdispatch
23 23 $(NOSE) IPython.strdispatch
24 24
25 25 test: plugin dtexample.py
26 26 $(NOSE) dtexample.py test*.py test*.txt
27 27
28 28 deb: plugin dtexample.py
29 29 $(NOSE) test_combo.txt
30 30
31 31 iptest: plugin
32 32 $(NOSE) IPython
33 33
34 34 deco:
35 35 $(NOSE0) IPython.testing.decorators
36 36
37 37 mtest: plugin
38 38 $(NOSE) -x IPython.Magic
39 39
40 40 ipipe: plugin
41 41 $(NOSE) -x IPython.Extensions.ipipe
42 42
43 43 sr: rtest std
44 44
45 45 base: dtest rtest test std deco
46 46
47 47 all: base iptest
48 48
49 49 IPython_doctest_plugin.egg-info: $(SRC)
50 50 python setup.py install --prefix=$(PREFIX)
51 51 touch $@
52 52
53 53 clean:
54 54 rm -rf IPython_doctest_plugin.egg-info *~ *pyc build/ dist/
General Comments 0
You need to be logged in to leave comments. Login now