##// END OF EJS Templates
Add decorators to mark known failures on Python 3.
Thomas Kluyver -
Show More
@@ -1,771 +1,774 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tools for inspecting Python objects.
2 """Tools for inspecting Python objects.
3
3
4 Uses syntax highlighting for presenting the various information elements.
4 Uses syntax highlighting for presenting the various information elements.
5
5
6 Similar in spirit to the inspect module, but all calls take a name argument to
6 Similar in spirit to the inspect module, but all calls take a name argument to
7 reference the name under which an object is being read.
7 reference the name under which an object is being read.
8 """
8 """
9
9
10 #*****************************************************************************
10 #*****************************************************************************
11 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
11 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
12 #
12 #
13 # Distributed under the terms of the BSD License. The full license is in
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
14 # the file COPYING, distributed as part of this software.
15 #*****************************************************************************
15 #*****************************************************************************
16
16
17 __all__ = ['Inspector','InspectColors']
17 __all__ = ['Inspector','InspectColors']
18
18
19 # stdlib modules
19 # stdlib modules
20 import __builtin__
20 import __builtin__
21 import inspect
21 import inspect
22 import linecache
22 import linecache
23 import os
23 import os
24 import sys
24 import sys
25 import types
25 import types
26 from collections import namedtuple
26 from collections import namedtuple
27 try:
27 try:
28 from itertools import izip_longest
28 from itertools import izip_longest
29 except ImportError:
29 except ImportError:
30 from itertools import zip_longest as izip_longest
30 from itertools import zip_longest as izip_longest
31
31
32 # IPython's own
32 # IPython's own
33 from IPython.core import page
33 from IPython.core import page
34 from IPython.testing.skipdoctest import skip_doctest_py3
34 from IPython.utils import PyColorize
35 from IPython.utils import PyColorize
35 from IPython.utils import io
36 from IPython.utils import io
36 from IPython.utils import py3compat
37 from IPython.utils import py3compat
37 from IPython.utils.text import indent
38 from IPython.utils.text import indent
38 from IPython.utils.wildcard import list_namespace
39 from IPython.utils.wildcard import list_namespace
39 from IPython.utils.coloransi import *
40 from IPython.utils.coloransi import *
40
41
41 #****************************************************************************
42 #****************************************************************************
42 # Builtin color schemes
43 # Builtin color schemes
43
44
44 Colors = TermColors # just a shorthand
45 Colors = TermColors # just a shorthand
45
46
46 # Build a few color schemes
47 # Build a few color schemes
47 NoColor = ColorScheme(
48 NoColor = ColorScheme(
48 'NoColor',{
49 'NoColor',{
49 'header' : Colors.NoColor,
50 'header' : Colors.NoColor,
50 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
51 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
51 } )
52 } )
52
53
53 LinuxColors = ColorScheme(
54 LinuxColors = ColorScheme(
54 'Linux',{
55 'Linux',{
55 'header' : Colors.LightRed,
56 'header' : Colors.LightRed,
56 'normal' : Colors.Normal # color off (usu. Colors.Normal)
57 'normal' : Colors.Normal # color off (usu. Colors.Normal)
57 } )
58 } )
58
59
59 LightBGColors = ColorScheme(
60 LightBGColors = ColorScheme(
60 'LightBG',{
61 'LightBG',{
61 'header' : Colors.Red,
62 'header' : Colors.Red,
62 'normal' : Colors.Normal # color off (usu. Colors.Normal)
63 'normal' : Colors.Normal # color off (usu. Colors.Normal)
63 } )
64 } )
64
65
65 # Build table of color schemes (needed by the parser)
66 # Build table of color schemes (needed by the parser)
66 InspectColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
67 InspectColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
67 'Linux')
68 'Linux')
68
69
69 #****************************************************************************
70 #****************************************************************************
70 # Auxiliary functions and objects
71 # Auxiliary functions and objects
71
72
72 # See the messaging spec for the definition of all these fields. This list
73 # See the messaging spec for the definition of all these fields. This list
73 # effectively defines the order of display
74 # effectively defines the order of display
74 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
75 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
75 'length', 'file', 'definition', 'docstring', 'source',
76 'length', 'file', 'definition', 'docstring', 'source',
76 'init_definition', 'class_docstring', 'init_docstring',
77 'init_definition', 'class_docstring', 'init_docstring',
77 'call_def', 'call_docstring',
78 'call_def', 'call_docstring',
78 # These won't be printed but will be used to determine how to
79 # These won't be printed but will be used to determine how to
79 # format the object
80 # format the object
80 'ismagic', 'isalias', 'isclass', 'argspec', 'found', 'name'
81 'ismagic', 'isalias', 'isclass', 'argspec', 'found', 'name'
81 ]
82 ]
82
83
83
84
84 def object_info(**kw):
85 def object_info(**kw):
85 """Make an object info dict with all fields present."""
86 """Make an object info dict with all fields present."""
86 infodict = dict(izip_longest(info_fields, [None]))
87 infodict = dict(izip_longest(info_fields, [None]))
87 infodict.update(kw)
88 infodict.update(kw)
88 return infodict
89 return infodict
89
90
90
91
91 def getdoc(obj):
92 def getdoc(obj):
92 """Stable wrapper around inspect.getdoc.
93 """Stable wrapper around inspect.getdoc.
93
94
94 This can't crash because of attribute problems.
95 This can't crash because of attribute problems.
95
96
96 It also attempts to call a getdoc() method on the given object. This
97 It also attempts to call a getdoc() method on the given object. This
97 allows objects which provide their docstrings via non-standard mechanisms
98 allows objects which provide their docstrings via non-standard mechanisms
98 (like Pyro proxies) to still be inspected by ipython's ? system."""
99 (like Pyro proxies) to still be inspected by ipython's ? system."""
99
100
100 ds = None # default return value
101 ds = None # default return value
101 try:
102 try:
102 ds = inspect.getdoc(obj)
103 ds = inspect.getdoc(obj)
103 except:
104 except:
104 # Harden against an inspect failure, which can occur with
105 # Harden against an inspect failure, which can occur with
105 # SWIG-wrapped extensions.
106 # SWIG-wrapped extensions.
106 pass
107 pass
107 # Allow objects to offer customized documentation via a getdoc method:
108 # Allow objects to offer customized documentation via a getdoc method:
108 try:
109 try:
109 ds2 = obj.getdoc()
110 ds2 = obj.getdoc()
110 except:
111 except:
111 pass
112 pass
112 else:
113 else:
113 # if we get extra info, we add it to the normal docstring.
114 # if we get extra info, we add it to the normal docstring.
114 if ds is None:
115 if ds is None:
115 ds = ds2
116 ds = ds2
116 else:
117 else:
117 ds = '%s\n%s' % (ds,ds2)
118 ds = '%s\n%s' % (ds,ds2)
118 return ds
119 return ds
119
120
120
121
121 def getsource(obj,is_binary=False):
122 def getsource(obj,is_binary=False):
122 """Wrapper around inspect.getsource.
123 """Wrapper around inspect.getsource.
123
124
124 This can be modified by other projects to provide customized source
125 This can be modified by other projects to provide customized source
125 extraction.
126 extraction.
126
127
127 Inputs:
128 Inputs:
128
129
129 - obj: an object whose source code we will attempt to extract.
130 - obj: an object whose source code we will attempt to extract.
130
131
131 Optional inputs:
132 Optional inputs:
132
133
133 - is_binary: whether the object is known to come from a binary source.
134 - is_binary: whether the object is known to come from a binary source.
134 This implementation will skip returning any output for binary objects, but
135 This implementation will skip returning any output for binary objects, but
135 custom extractors may know how to meaningfully process them."""
136 custom extractors may know how to meaningfully process them."""
136
137
137 if is_binary:
138 if is_binary:
138 return None
139 return None
139 else:
140 else:
140 # get source if obj was decorated with @decorator
141 # get source if obj was decorated with @decorator
141 if hasattr(obj,"__wrapped__"):
142 if hasattr(obj,"__wrapped__"):
142 obj = obj.__wrapped__
143 obj = obj.__wrapped__
143 try:
144 try:
144 src = inspect.getsource(obj)
145 src = inspect.getsource(obj)
145 except TypeError:
146 except TypeError:
146 if hasattr(obj,'__class__'):
147 if hasattr(obj,'__class__'):
147 src = inspect.getsource(obj.__class__)
148 src = inspect.getsource(obj.__class__)
148 return src
149 return src
149
150
150 def getargspec(obj):
151 def getargspec(obj):
151 """Get the names and default values of a function's arguments.
152 """Get the names and default values of a function's arguments.
152
153
153 A tuple of four things is returned: (args, varargs, varkw, defaults).
154 A tuple of four things is returned: (args, varargs, varkw, defaults).
154 'args' is a list of the argument names (it may contain nested lists).
155 'args' is a list of the argument names (it may contain nested lists).
155 'varargs' and 'varkw' are the names of the * and ** arguments or None.
156 'varargs' and 'varkw' are the names of the * and ** arguments or None.
156 'defaults' is an n-tuple of the default values of the last n arguments.
157 'defaults' is an n-tuple of the default values of the last n arguments.
157
158
158 Modified version of inspect.getargspec from the Python Standard
159 Modified version of inspect.getargspec from the Python Standard
159 Library."""
160 Library."""
160
161
161 if inspect.isfunction(obj):
162 if inspect.isfunction(obj):
162 func_obj = obj
163 func_obj = obj
163 elif inspect.ismethod(obj):
164 elif inspect.ismethod(obj):
164 func_obj = obj.im_func
165 func_obj = obj.im_func
165 elif hasattr(obj, '__call__'):
166 elif hasattr(obj, '__call__'):
166 func_obj = obj.__call__
167 func_obj = obj.__call__
167 else:
168 else:
168 raise TypeError('arg is not a Python function')
169 raise TypeError('arg is not a Python function')
169 args, varargs, varkw = inspect.getargs(func_obj.func_code)
170 args, varargs, varkw = inspect.getargs(func_obj.func_code)
170 return args, varargs, varkw, func_obj.func_defaults
171 return args, varargs, varkw, func_obj.func_defaults
171
172
172
173
173 def format_argspec(argspec):
174 def format_argspec(argspec):
174 """Format argspect, convenience wrapper around inspect's.
175 """Format argspect, convenience wrapper around inspect's.
175
176
176 This takes a dict instead of ordered arguments and calls
177 This takes a dict instead of ordered arguments and calls
177 inspect.format_argspec with the arguments in the necessary order.
178 inspect.format_argspec with the arguments in the necessary order.
178 """
179 """
179 return inspect.formatargspec(argspec['args'], argspec['varargs'],
180 return inspect.formatargspec(argspec['args'], argspec['varargs'],
180 argspec['varkw'], argspec['defaults'])
181 argspec['varkw'], argspec['defaults'])
181
182
182
183
183 def call_tip(oinfo, format_call=True):
184 def call_tip(oinfo, format_call=True):
184 """Extract call tip data from an oinfo dict.
185 """Extract call tip data from an oinfo dict.
185
186
186 Parameters
187 Parameters
187 ----------
188 ----------
188 oinfo : dict
189 oinfo : dict
189
190
190 format_call : bool, optional
191 format_call : bool, optional
191 If True, the call line is formatted and returned as a string. If not, a
192 If True, the call line is formatted and returned as a string. If not, a
192 tuple of (name, argspec) is returned.
193 tuple of (name, argspec) is returned.
193
194
194 Returns
195 Returns
195 -------
196 -------
196 call_info : None, str or (str, dict) tuple.
197 call_info : None, str or (str, dict) tuple.
197 When format_call is True, the whole call information is formattted as a
198 When format_call is True, the whole call information is formattted as a
198 single string. Otherwise, the object's name and its argspec dict are
199 single string. Otherwise, the object's name and its argspec dict are
199 returned. If no call information is available, None is returned.
200 returned. If no call information is available, None is returned.
200
201
201 docstring : str or None
202 docstring : str or None
202 The most relevant docstring for calling purposes is returned, if
203 The most relevant docstring for calling purposes is returned, if
203 available. The priority is: call docstring for callable instances, then
204 available. The priority is: call docstring for callable instances, then
204 constructor docstring for classes, then main object's docstring otherwise
205 constructor docstring for classes, then main object's docstring otherwise
205 (regular functions).
206 (regular functions).
206 """
207 """
207 # Get call definition
208 # Get call definition
208 argspec = oinfo.get('argspec')
209 argspec = oinfo.get('argspec')
209 if argspec is None:
210 if argspec is None:
210 call_line = None
211 call_line = None
211 else:
212 else:
212 # Callable objects will have 'self' as their first argument, prune
213 # Callable objects will have 'self' as their first argument, prune
213 # it out if it's there for clarity (since users do *not* pass an
214 # it out if it's there for clarity (since users do *not* pass an
214 # extra first argument explicitly).
215 # extra first argument explicitly).
215 try:
216 try:
216 has_self = argspec['args'][0] == 'self'
217 has_self = argspec['args'][0] == 'self'
217 except (KeyError, IndexError):
218 except (KeyError, IndexError):
218 pass
219 pass
219 else:
220 else:
220 if has_self:
221 if has_self:
221 argspec['args'] = argspec['args'][1:]
222 argspec['args'] = argspec['args'][1:]
222
223
223 call_line = oinfo['name']+format_argspec(argspec)
224 call_line = oinfo['name']+format_argspec(argspec)
224
225
225 # Now get docstring.
226 # Now get docstring.
226 # The priority is: call docstring, constructor docstring, main one.
227 # The priority is: call docstring, constructor docstring, main one.
227 doc = oinfo.get('call_docstring')
228 doc = oinfo.get('call_docstring')
228 if doc is None:
229 if doc is None:
229 doc = oinfo.get('init_docstring')
230 doc = oinfo.get('init_docstring')
230 if doc is None:
231 if doc is None:
231 doc = oinfo.get('docstring','')
232 doc = oinfo.get('docstring','')
232
233
233 return call_line, doc
234 return call_line, doc
234
235
235
236
236 class Inspector:
237 class Inspector:
237 def __init__(self, color_table=InspectColors,
238 def __init__(self, color_table=InspectColors,
238 code_color_table=PyColorize.ANSICodeColors,
239 code_color_table=PyColorize.ANSICodeColors,
239 scheme='NoColor',
240 scheme='NoColor',
240 str_detail_level=0):
241 str_detail_level=0):
241 self.color_table = color_table
242 self.color_table = color_table
242 self.parser = PyColorize.Parser(code_color_table,out='str')
243 self.parser = PyColorize.Parser(code_color_table,out='str')
243 self.format = self.parser.format
244 self.format = self.parser.format
244 self.str_detail_level = str_detail_level
245 self.str_detail_level = str_detail_level
245 self.set_active_scheme(scheme)
246 self.set_active_scheme(scheme)
246
247
247 def _getdef(self,obj,oname=''):
248 def _getdef(self,obj,oname=''):
248 """Return the definition header for any callable object.
249 """Return the definition header for any callable object.
249
250
250 If any exception is generated, None is returned instead and the
251 If any exception is generated, None is returned instead and the
251 exception is suppressed."""
252 exception is suppressed."""
252
253
253 try:
254 try:
254 # We need a plain string here, NOT unicode!
255 # We need a plain string here, NOT unicode!
255 hdef = oname + inspect.formatargspec(*getargspec(obj))
256 hdef = oname + inspect.formatargspec(*getargspec(obj))
256 return py3compat.unicode_to_str(hdef, 'ascii')
257 return py3compat.unicode_to_str(hdef, 'ascii')
257 except:
258 except:
258 return None
259 return None
259
260
260 def __head(self,h):
261 def __head(self,h):
261 """Return a header string with proper colors."""
262 """Return a header string with proper colors."""
262 return '%s%s%s' % (self.color_table.active_colors.header,h,
263 return '%s%s%s' % (self.color_table.active_colors.header,h,
263 self.color_table.active_colors.normal)
264 self.color_table.active_colors.normal)
264
265
265 def set_active_scheme(self,scheme):
266 def set_active_scheme(self,scheme):
266 self.color_table.set_active_scheme(scheme)
267 self.color_table.set_active_scheme(scheme)
267 self.parser.color_table.set_active_scheme(scheme)
268 self.parser.color_table.set_active_scheme(scheme)
268
269
269 def noinfo(self,msg,oname):
270 def noinfo(self,msg,oname):
270 """Generic message when no information is found."""
271 """Generic message when no information is found."""
271 print 'No %s found' % msg,
272 print 'No %s found' % msg,
272 if oname:
273 if oname:
273 print 'for %s' % oname
274 print 'for %s' % oname
274 else:
275 else:
275 print
276 print
276
277
277 def pdef(self,obj,oname=''):
278 def pdef(self,obj,oname=''):
278 """Print the definition header for any callable object.
279 """Print the definition header for any callable object.
279
280
280 If the object is a class, print the constructor information."""
281 If the object is a class, print the constructor information."""
281
282
282 if not callable(obj):
283 if not callable(obj):
283 print 'Object is not callable.'
284 print 'Object is not callable.'
284 return
285 return
285
286
286 header = ''
287 header = ''
287
288
288 if inspect.isclass(obj):
289 if inspect.isclass(obj):
289 header = self.__head('Class constructor information:\n')
290 header = self.__head('Class constructor information:\n')
290 obj = obj.__init__
291 obj = obj.__init__
291 elif type(obj) is types.InstanceType:
292 elif type(obj) is types.InstanceType:
292 obj = obj.__call__
293 obj = obj.__call__
293
294
294 output = self._getdef(obj,oname)
295 output = self._getdef(obj,oname)
295 if output is None:
296 if output is None:
296 self.noinfo('definition header',oname)
297 self.noinfo('definition header',oname)
297 else:
298 else:
298 print >>io.stdout, header,self.format(output),
299 print >>io.stdout, header,self.format(output),
299
300
301 # In Python 3, all classes are new-style, so they all have __init__.
302 @skip_doctest_py3
300 def pdoc(self,obj,oname='',formatter = None):
303 def pdoc(self,obj,oname='',formatter = None):
301 """Print the docstring for any object.
304 """Print the docstring for any object.
302
305
303 Optional:
306 Optional:
304 -formatter: a function to run the docstring through for specially
307 -formatter: a function to run the docstring through for specially
305 formatted docstrings.
308 formatted docstrings.
306
309
307 Examples
310 Examples
308 --------
311 --------
309
312
310 In [1]: class NoInit:
313 In [1]: class NoInit:
311 ...: pass
314 ...: pass
312
315
313 In [2]: class NoDoc:
316 In [2]: class NoDoc:
314 ...: def __init__(self):
317 ...: def __init__(self):
315 ...: pass
318 ...: pass
316
319
317 In [3]: %pdoc NoDoc
320 In [3]: %pdoc NoDoc
318 No documentation found for NoDoc
321 No documentation found for NoDoc
319
322
320 In [4]: %pdoc NoInit
323 In [4]: %pdoc NoInit
321 No documentation found for NoInit
324 No documentation found for NoInit
322
325
323 In [5]: obj = NoInit()
326 In [5]: obj = NoInit()
324
327
325 In [6]: %pdoc obj
328 In [6]: %pdoc obj
326 No documentation found for obj
329 No documentation found for obj
327
330
328 In [5]: obj2 = NoDoc()
331 In [5]: obj2 = NoDoc()
329
332
330 In [6]: %pdoc obj2
333 In [6]: %pdoc obj2
331 No documentation found for obj2
334 No documentation found for obj2
332 """
335 """
333
336
334 head = self.__head # For convenience
337 head = self.__head # For convenience
335 lines = []
338 lines = []
336 ds = getdoc(obj)
339 ds = getdoc(obj)
337 if formatter:
340 if formatter:
338 ds = formatter(ds)
341 ds = formatter(ds)
339 if ds:
342 if ds:
340 lines.append(head("Class Docstring:"))
343 lines.append(head("Class Docstring:"))
341 lines.append(indent(ds))
344 lines.append(indent(ds))
342 if inspect.isclass(obj) and hasattr(obj, '__init__'):
345 if inspect.isclass(obj) and hasattr(obj, '__init__'):
343 init_ds = getdoc(obj.__init__)
346 init_ds = getdoc(obj.__init__)
344 if init_ds is not None:
347 if init_ds is not None:
345 lines.append(head("Constructor Docstring:"))
348 lines.append(head("Constructor Docstring:"))
346 lines.append(indent(init_ds))
349 lines.append(indent(init_ds))
347 elif hasattr(obj,'__call__'):
350 elif hasattr(obj,'__call__'):
348 call_ds = getdoc(obj.__call__)
351 call_ds = getdoc(obj.__call__)
349 if call_ds:
352 if call_ds:
350 lines.append(head("Calling Docstring:"))
353 lines.append(head("Calling Docstring:"))
351 lines.append(indent(call_ds))
354 lines.append(indent(call_ds))
352
355
353 if not lines:
356 if not lines:
354 self.noinfo('documentation',oname)
357 self.noinfo('documentation',oname)
355 else:
358 else:
356 page.page('\n'.join(lines))
359 page.page('\n'.join(lines))
357
360
358 def psource(self,obj,oname=''):
361 def psource(self,obj,oname=''):
359 """Print the source code for an object."""
362 """Print the source code for an object."""
360
363
361 # Flush the source cache because inspect can return out-of-date source
364 # Flush the source cache because inspect can return out-of-date source
362 linecache.checkcache()
365 linecache.checkcache()
363 try:
366 try:
364 src = getsource(obj)
367 src = getsource(obj)
365 except:
368 except:
366 self.noinfo('source',oname)
369 self.noinfo('source',oname)
367 else:
370 else:
368 page.page(self.format(py3compat.unicode_to_str(src)))
371 page.page(self.format(py3compat.unicode_to_str(src)))
369
372
370 def pfile(self,obj,oname=''):
373 def pfile(self,obj,oname=''):
371 """Show the whole file where an object was defined."""
374 """Show the whole file where an object was defined."""
372
375
373 try:
376 try:
374 try:
377 try:
375 lineno = inspect.getsourcelines(obj)[1]
378 lineno = inspect.getsourcelines(obj)[1]
376 except TypeError:
379 except TypeError:
377 # For instances, try the class object like getsource() does
380 # For instances, try the class object like getsource() does
378 if hasattr(obj,'__class__'):
381 if hasattr(obj,'__class__'):
379 lineno = inspect.getsourcelines(obj.__class__)[1]
382 lineno = inspect.getsourcelines(obj.__class__)[1]
380 # Adjust the inspected object so getabsfile() below works
383 # Adjust the inspected object so getabsfile() below works
381 obj = obj.__class__
384 obj = obj.__class__
382 except:
385 except:
383 self.noinfo('file',oname)
386 self.noinfo('file',oname)
384 return
387 return
385
388
386 # We only reach this point if object was successfully queried
389 # We only reach this point if object was successfully queried
387
390
388 # run contents of file through pager starting at line
391 # run contents of file through pager starting at line
389 # where the object is defined
392 # where the object is defined
390 ofile = inspect.getabsfile(obj)
393 ofile = inspect.getabsfile(obj)
391
394
392 if ofile.endswith(('.so', '.dll', '.pyd')):
395 if ofile.endswith(('.so', '.dll', '.pyd')):
393 print 'File %r is binary, not printing.' % ofile
396 print 'File %r is binary, not printing.' % ofile
394 elif not os.path.isfile(ofile):
397 elif not os.path.isfile(ofile):
395 print 'File %r does not exist, not printing.' % ofile
398 print 'File %r does not exist, not printing.' % ofile
396 else:
399 else:
397 # Print only text files, not extension binaries. Note that
400 # Print only text files, not extension binaries. Note that
398 # getsourcelines returns lineno with 1-offset and page() uses
401 # getsourcelines returns lineno with 1-offset and page() uses
399 # 0-offset, so we must adjust.
402 # 0-offset, so we must adjust.
400 page.page(self.format(open(ofile).read()),lineno-1)
403 page.page(self.format(open(ofile).read()),lineno-1)
401
404
402 def _format_fields(self, fields, title_width=12):
405 def _format_fields(self, fields, title_width=12):
403 """Formats a list of fields for display.
406 """Formats a list of fields for display.
404
407
405 Parameters
408 Parameters
406 ----------
409 ----------
407 fields : list
410 fields : list
408 A list of 2-tuples: (field_title, field_content)
411 A list of 2-tuples: (field_title, field_content)
409 title_width : int
412 title_width : int
410 How many characters to pad titles to. Default 12.
413 How many characters to pad titles to. Default 12.
411 """
414 """
412 out = []
415 out = []
413 header = self.__head
416 header = self.__head
414 for title, content in fields:
417 for title, content in fields:
415 if len(content.splitlines()) > 1:
418 if len(content.splitlines()) > 1:
416 title = header(title + ":") + "\n"
419 title = header(title + ":") + "\n"
417 else:
420 else:
418 title = header((title+":").ljust(title_width))
421 title = header((title+":").ljust(title_width))
419 out.append(title + content)
422 out.append(title + content)
420 return "\n".join(out)
423 return "\n".join(out)
421
424
422 # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict)
425 # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict)
423 pinfo_fields1 = [("Type", "type_name"),
426 pinfo_fields1 = [("Type", "type_name"),
424 ("Base Class", "base_class"),
427 ("Base Class", "base_class"),
425 ("String Form", "string_form"),
428 ("String Form", "string_form"),
426 ("Namespace", "namespace"),
429 ("Namespace", "namespace"),
427 ("Length", "length"),
430 ("Length", "length"),
428 ("File", "file"),
431 ("File", "file"),
429 ("Definition", "definition")]
432 ("Definition", "definition")]
430
433
431 pinfo_fields_obj = [("Class Docstring", "class_docstring"),
434 pinfo_fields_obj = [("Class Docstring", "class_docstring"),
432 ("Constructor Docstring","init_docstring"),
435 ("Constructor Docstring","init_docstring"),
433 ("Call def", "call_def"),
436 ("Call def", "call_def"),
434 ("Call docstring", "call_docstring")]
437 ("Call docstring", "call_docstring")]
435
438
436 def pinfo(self,obj,oname='',formatter=None,info=None,detail_level=0):
439 def pinfo(self,obj,oname='',formatter=None,info=None,detail_level=0):
437 """Show detailed information about an object.
440 """Show detailed information about an object.
438
441
439 Optional arguments:
442 Optional arguments:
440
443
441 - oname: name of the variable pointing to the object.
444 - oname: name of the variable pointing to the object.
442
445
443 - formatter: special formatter for docstrings (see pdoc)
446 - formatter: special formatter for docstrings (see pdoc)
444
447
445 - info: a structure with some information fields which may have been
448 - info: a structure with some information fields which may have been
446 precomputed already.
449 precomputed already.
447
450
448 - detail_level: if set to 1, more information is given.
451 - detail_level: if set to 1, more information is given.
449 """
452 """
450 info = self.info(obj, oname=oname, formatter=formatter,
453 info = self.info(obj, oname=oname, formatter=formatter,
451 info=info, detail_level=detail_level)
454 info=info, detail_level=detail_level)
452 displayfields = []
455 displayfields = []
453 for title, key in self.pinfo_fields1:
456 for title, key in self.pinfo_fields1:
454 field = info[key]
457 field = info[key]
455 if field is not None:
458 if field is not None:
456 displayfields.append((title, field.rstrip()))
459 displayfields.append((title, field.rstrip()))
457
460
458 # Source or docstring, depending on detail level and whether
461 # Source or docstring, depending on detail level and whether
459 # source found.
462 # source found.
460 if detail_level > 0 and info['source'] is not None:
463 if detail_level > 0 and info['source'] is not None:
461 displayfields.append(("Source", self.format(py3compat.unicode_to_str(info['source']))))
464 displayfields.append(("Source", self.format(py3compat.unicode_to_str(info['source']))))
462 elif info['docstring'] is not None:
465 elif info['docstring'] is not None:
463 displayfields.append(("Docstring", info["docstring"]))
466 displayfields.append(("Docstring", info["docstring"]))
464
467
465 # Constructor info for classes
468 # Constructor info for classes
466 if info['isclass']:
469 if info['isclass']:
467 if info['init_definition'] or info['init_docstring']:
470 if info['init_definition'] or info['init_docstring']:
468 displayfields.append(("Constructor information", ""))
471 displayfields.append(("Constructor information", ""))
469 if info['init_definition'] is not None:
472 if info['init_definition'] is not None:
470 displayfields.append((" Definition",
473 displayfields.append((" Definition",
471 info['init_definition'].rstrip()))
474 info['init_definition'].rstrip()))
472 if info['init_docstring'] is not None:
475 if info['init_docstring'] is not None:
473 displayfields.append((" Docstring",
476 displayfields.append((" Docstring",
474 indent(info['init_docstring'])))
477 indent(info['init_docstring'])))
475
478
476 # Info for objects:
479 # Info for objects:
477 else:
480 else:
478 for title, key in self.pinfo_fields_obj:
481 for title, key in self.pinfo_fields_obj:
479 field = info[key]
482 field = info[key]
480 if field is not None:
483 if field is not None:
481 displayfields.append((title, field.rstrip()))
484 displayfields.append((title, field.rstrip()))
482
485
483 # Finally send to printer/pager:
486 # Finally send to printer/pager:
484 if displayfields:
487 if displayfields:
485 page.page(self._format_fields(displayfields))
488 page.page(self._format_fields(displayfields))
486
489
487 def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
490 def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
488 """Compute a dict with detailed information about an object.
491 """Compute a dict with detailed information about an object.
489
492
490 Optional arguments:
493 Optional arguments:
491
494
492 - oname: name of the variable pointing to the object.
495 - oname: name of the variable pointing to the object.
493
496
494 - formatter: special formatter for docstrings (see pdoc)
497 - formatter: special formatter for docstrings (see pdoc)
495
498
496 - info: a structure with some information fields which may have been
499 - info: a structure with some information fields which may have been
497 precomputed already.
500 precomputed already.
498
501
499 - detail_level: if set to 1, more information is given.
502 - detail_level: if set to 1, more information is given.
500 """
503 """
501
504
502 obj_type = type(obj)
505 obj_type = type(obj)
503
506
504 header = self.__head
507 header = self.__head
505 if info is None:
508 if info is None:
506 ismagic = 0
509 ismagic = 0
507 isalias = 0
510 isalias = 0
508 ospace = ''
511 ospace = ''
509 else:
512 else:
510 ismagic = info.ismagic
513 ismagic = info.ismagic
511 isalias = info.isalias
514 isalias = info.isalias
512 ospace = info.namespace
515 ospace = info.namespace
513
516
514 # Get docstring, special-casing aliases:
517 # Get docstring, special-casing aliases:
515 if isalias:
518 if isalias:
516 if not callable(obj):
519 if not callable(obj):
517 try:
520 try:
518 ds = "Alias to the system command:\n %s" % obj[1]
521 ds = "Alias to the system command:\n %s" % obj[1]
519 except:
522 except:
520 ds = "Alias: " + str(obj)
523 ds = "Alias: " + str(obj)
521 else:
524 else:
522 ds = "Alias to " + str(obj)
525 ds = "Alias to " + str(obj)
523 if obj.__doc__:
526 if obj.__doc__:
524 ds += "\nDocstring:\n" + obj.__doc__
527 ds += "\nDocstring:\n" + obj.__doc__
525 else:
528 else:
526 ds = getdoc(obj)
529 ds = getdoc(obj)
527 if ds is None:
530 if ds is None:
528 ds = '<no docstring>'
531 ds = '<no docstring>'
529 if formatter is not None:
532 if formatter is not None:
530 ds = formatter(ds)
533 ds = formatter(ds)
531
534
532 # store output in a dict, we initialize it here and fill it as we go
535 # store output in a dict, we initialize it here and fill it as we go
533 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
536 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
534
537
535 string_max = 200 # max size of strings to show (snipped if longer)
538 string_max = 200 # max size of strings to show (snipped if longer)
536 shalf = int((string_max -5)/2)
539 shalf = int((string_max -5)/2)
537
540
538 if ismagic:
541 if ismagic:
539 obj_type_name = 'Magic function'
542 obj_type_name = 'Magic function'
540 elif isalias:
543 elif isalias:
541 obj_type_name = 'System alias'
544 obj_type_name = 'System alias'
542 else:
545 else:
543 obj_type_name = obj_type.__name__
546 obj_type_name = obj_type.__name__
544 out['type_name'] = obj_type_name
547 out['type_name'] = obj_type_name
545
548
546 try:
549 try:
547 bclass = obj.__class__
550 bclass = obj.__class__
548 out['base_class'] = str(bclass)
551 out['base_class'] = str(bclass)
549 except: pass
552 except: pass
550
553
551 # String form, but snip if too long in ? form (full in ??)
554 # String form, but snip if too long in ? form (full in ??)
552 if detail_level >= self.str_detail_level:
555 if detail_level >= self.str_detail_level:
553 try:
556 try:
554 ostr = str(obj)
557 ostr = str(obj)
555 str_head = 'string_form'
558 str_head = 'string_form'
556 if not detail_level and len(ostr)>string_max:
559 if not detail_level and len(ostr)>string_max:
557 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
560 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
558 ostr = ("\n" + " " * len(str_head.expandtabs())).\
561 ostr = ("\n" + " " * len(str_head.expandtabs())).\
559 join(q.strip() for q in ostr.split("\n"))
562 join(q.strip() for q in ostr.split("\n"))
560 out[str_head] = ostr
563 out[str_head] = ostr
561 except:
564 except:
562 pass
565 pass
563
566
564 if ospace:
567 if ospace:
565 out['namespace'] = ospace
568 out['namespace'] = ospace
566
569
567 # Length (for strings and lists)
570 # Length (for strings and lists)
568 try:
571 try:
569 out['length'] = str(len(obj))
572 out['length'] = str(len(obj))
570 except: pass
573 except: pass
571
574
572 # Filename where object was defined
575 # Filename where object was defined
573 binary_file = False
576 binary_file = False
574 try:
577 try:
575 try:
578 try:
576 fname = inspect.getabsfile(obj)
579 fname = inspect.getabsfile(obj)
577 except TypeError:
580 except TypeError:
578 # For an instance, the file that matters is where its class was
581 # For an instance, the file that matters is where its class was
579 # declared.
582 # declared.
580 if hasattr(obj,'__class__'):
583 if hasattr(obj,'__class__'):
581 fname = inspect.getabsfile(obj.__class__)
584 fname = inspect.getabsfile(obj.__class__)
582 if fname.endswith('<string>'):
585 if fname.endswith('<string>'):
583 fname = 'Dynamically generated function. No source code available.'
586 fname = 'Dynamically generated function. No source code available.'
584 if fname.endswith(('.so', '.dll', '.pyd')):
587 if fname.endswith(('.so', '.dll', '.pyd')):
585 binary_file = True
588 binary_file = True
586 out['file'] = fname
589 out['file'] = fname
587 except:
590 except:
588 # if anything goes wrong, we don't want to show source, so it's as
591 # if anything goes wrong, we don't want to show source, so it's as
589 # if the file was binary
592 # if the file was binary
590 binary_file = True
593 binary_file = True
591
594
592 # reconstruct the function definition and print it:
595 # reconstruct the function definition and print it:
593 defln = self._getdef(obj, oname)
596 defln = self._getdef(obj, oname)
594 if defln:
597 if defln:
595 out['definition'] = self.format(defln)
598 out['definition'] = self.format(defln)
596
599
597 # Docstrings only in detail 0 mode, since source contains them (we
600 # Docstrings only in detail 0 mode, since source contains them (we
598 # avoid repetitions). If source fails, we add them back, see below.
601 # avoid repetitions). If source fails, we add them back, see below.
599 if ds and detail_level == 0:
602 if ds and detail_level == 0:
600 out['docstring'] = ds
603 out['docstring'] = ds
601
604
602 # Original source code for any callable
605 # Original source code for any callable
603 if detail_level:
606 if detail_level:
604 # Flush the source cache because inspect can return out-of-date
607 # Flush the source cache because inspect can return out-of-date
605 # source
608 # source
606 linecache.checkcache()
609 linecache.checkcache()
607 source = None
610 source = None
608 try:
611 try:
609 try:
612 try:
610 source = getsource(obj,binary_file)
613 source = getsource(obj,binary_file)
611 except TypeError:
614 except TypeError:
612 if hasattr(obj,'__class__'):
615 if hasattr(obj,'__class__'):
613 source = getsource(obj.__class__,binary_file)
616 source = getsource(obj.__class__,binary_file)
614 if source is not None:
617 if source is not None:
615 out['source'] = source.rstrip()
618 out['source'] = source.rstrip()
616 except Exception:
619 except Exception:
617 pass
620 pass
618
621
619 if ds and source is None:
622 if ds and source is None:
620 out['docstring'] = ds
623 out['docstring'] = ds
621
624
622
625
623 # Constructor docstring for classes
626 # Constructor docstring for classes
624 if inspect.isclass(obj):
627 if inspect.isclass(obj):
625 out['isclass'] = True
628 out['isclass'] = True
626 # reconstruct the function definition and print it:
629 # reconstruct the function definition and print it:
627 try:
630 try:
628 obj_init = obj.__init__
631 obj_init = obj.__init__
629 except AttributeError:
632 except AttributeError:
630 init_def = init_ds = None
633 init_def = init_ds = None
631 else:
634 else:
632 init_def = self._getdef(obj_init,oname)
635 init_def = self._getdef(obj_init,oname)
633 init_ds = getdoc(obj_init)
636 init_ds = getdoc(obj_init)
634 # Skip Python's auto-generated docstrings
637 # Skip Python's auto-generated docstrings
635 if init_ds and \
638 if init_ds and \
636 init_ds.startswith('x.__init__(...) initializes'):
639 init_ds.startswith('x.__init__(...) initializes'):
637 init_ds = None
640 init_ds = None
638
641
639 if init_def or init_ds:
642 if init_def or init_ds:
640 if init_def:
643 if init_def:
641 out['init_definition'] = self.format(init_def)
644 out['init_definition'] = self.format(init_def)
642 if init_ds:
645 if init_ds:
643 out['init_docstring'] = init_ds
646 out['init_docstring'] = init_ds
644
647
645 # and class docstring for instances:
648 # and class docstring for instances:
646 else:
649 else:
647 # First, check whether the instance docstring is identical to the
650 # First, check whether the instance docstring is identical to the
648 # class one, and print it separately if they don't coincide. In
651 # class one, and print it separately if they don't coincide. In
649 # most cases they will, but it's nice to print all the info for
652 # most cases they will, but it's nice to print all the info for
650 # objects which use instance-customized docstrings.
653 # objects which use instance-customized docstrings.
651 if ds:
654 if ds:
652 try:
655 try:
653 cls = getattr(obj,'__class__')
656 cls = getattr(obj,'__class__')
654 except:
657 except:
655 class_ds = None
658 class_ds = None
656 else:
659 else:
657 class_ds = getdoc(cls)
660 class_ds = getdoc(cls)
658 # Skip Python's auto-generated docstrings
661 # Skip Python's auto-generated docstrings
659 if class_ds and \
662 if class_ds and \
660 (class_ds.startswith('function(code, globals[,') or \
663 (class_ds.startswith('function(code, globals[,') or \
661 class_ds.startswith('instancemethod(function, instance,') or \
664 class_ds.startswith('instancemethod(function, instance,') or \
662 class_ds.startswith('module(name[,') ):
665 class_ds.startswith('module(name[,') ):
663 class_ds = None
666 class_ds = None
664 if class_ds and ds != class_ds:
667 if class_ds and ds != class_ds:
665 out['class_docstring'] = class_ds
668 out['class_docstring'] = class_ds
666
669
667 # Next, try to show constructor docstrings
670 # Next, try to show constructor docstrings
668 try:
671 try:
669 init_ds = getdoc(obj.__init__)
672 init_ds = getdoc(obj.__init__)
670 # Skip Python's auto-generated docstrings
673 # Skip Python's auto-generated docstrings
671 if init_ds and \
674 if init_ds and \
672 init_ds.startswith('x.__init__(...) initializes'):
675 init_ds.startswith('x.__init__(...) initializes'):
673 init_ds = None
676 init_ds = None
674 except AttributeError:
677 except AttributeError:
675 init_ds = None
678 init_ds = None
676 if init_ds:
679 if init_ds:
677 out['init_docstring'] = init_ds
680 out['init_docstring'] = init_ds
678
681
679 # Call form docstring for callable instances
682 # Call form docstring for callable instances
680 if hasattr(obj, '__call__'):
683 if hasattr(obj, '__call__'):
681 call_def = self._getdef(obj.__call__, oname)
684 call_def = self._getdef(obj.__call__, oname)
682 if call_def is not None:
685 if call_def is not None:
683 out['call_def'] = self.format(call_def)
686 out['call_def'] = self.format(call_def)
684 call_ds = getdoc(obj.__call__)
687 call_ds = getdoc(obj.__call__)
685 # Skip Python's auto-generated docstrings
688 # Skip Python's auto-generated docstrings
686 if call_ds and call_ds.startswith('x.__call__(...) <==> x(...)'):
689 if call_ds and call_ds.startswith('x.__call__(...) <==> x(...)'):
687 call_ds = None
690 call_ds = None
688 if call_ds:
691 if call_ds:
689 out['call_docstring'] = call_ds
692 out['call_docstring'] = call_ds
690
693
691 # Compute the object's argspec as a callable. The key is to decide
694 # Compute the object's argspec as a callable. The key is to decide
692 # whether to pull it from the object itself, from its __init__ or
695 # whether to pull it from the object itself, from its __init__ or
693 # from its __call__ method.
696 # from its __call__ method.
694
697
695 if inspect.isclass(obj):
698 if inspect.isclass(obj):
696 # Old-style classes need not have an __init__
699 # Old-style classes need not have an __init__
697 callable_obj = getattr(obj, "__init__", None)
700 callable_obj = getattr(obj, "__init__", None)
698 elif callable(obj):
701 elif callable(obj):
699 callable_obj = obj
702 callable_obj = obj
700 else:
703 else:
701 callable_obj = None
704 callable_obj = None
702
705
703 if callable_obj:
706 if callable_obj:
704 try:
707 try:
705 args, varargs, varkw, defaults = getargspec(callable_obj)
708 args, varargs, varkw, defaults = getargspec(callable_obj)
706 except (TypeError, AttributeError):
709 except (TypeError, AttributeError):
707 # For extensions/builtins we can't retrieve the argspec
710 # For extensions/builtins we can't retrieve the argspec
708 pass
711 pass
709 else:
712 else:
710 out['argspec'] = dict(args=args, varargs=varargs,
713 out['argspec'] = dict(args=args, varargs=varargs,
711 varkw=varkw, defaults=defaults)
714 varkw=varkw, defaults=defaults)
712
715
713 return object_info(**out)
716 return object_info(**out)
714
717
715
718
716 def psearch(self,pattern,ns_table,ns_search=[],
719 def psearch(self,pattern,ns_table,ns_search=[],
717 ignore_case=False,show_all=False):
720 ignore_case=False,show_all=False):
718 """Search namespaces with wildcards for objects.
721 """Search namespaces with wildcards for objects.
719
722
720 Arguments:
723 Arguments:
721
724
722 - pattern: string containing shell-like wildcards to use in namespace
725 - pattern: string containing shell-like wildcards to use in namespace
723 searches and optionally a type specification to narrow the search to
726 searches and optionally a type specification to narrow the search to
724 objects of that type.
727 objects of that type.
725
728
726 - ns_table: dict of name->namespaces for search.
729 - ns_table: dict of name->namespaces for search.
727
730
728 Optional arguments:
731 Optional arguments:
729
732
730 - ns_search: list of namespace names to include in search.
733 - ns_search: list of namespace names to include in search.
731
734
732 - ignore_case(False): make the search case-insensitive.
735 - ignore_case(False): make the search case-insensitive.
733
736
734 - show_all(False): show all names, including those starting with
737 - show_all(False): show all names, including those starting with
735 underscores.
738 underscores.
736 """
739 """
737 #print 'ps pattern:<%r>' % pattern # dbg
740 #print 'ps pattern:<%r>' % pattern # dbg
738
741
739 # defaults
742 # defaults
740 type_pattern = 'all'
743 type_pattern = 'all'
741 filter = ''
744 filter = ''
742
745
743 cmds = pattern.split()
746 cmds = pattern.split()
744 len_cmds = len(cmds)
747 len_cmds = len(cmds)
745 if len_cmds == 1:
748 if len_cmds == 1:
746 # Only filter pattern given
749 # Only filter pattern given
747 filter = cmds[0]
750 filter = cmds[0]
748 elif len_cmds == 2:
751 elif len_cmds == 2:
749 # Both filter and type specified
752 # Both filter and type specified
750 filter,type_pattern = cmds
753 filter,type_pattern = cmds
751 else:
754 else:
752 raise ValueError('invalid argument string for psearch: <%s>' %
755 raise ValueError('invalid argument string for psearch: <%s>' %
753 pattern)
756 pattern)
754
757
755 # filter search namespaces
758 # filter search namespaces
756 for name in ns_search:
759 for name in ns_search:
757 if name not in ns_table:
760 if name not in ns_table:
758 raise ValueError('invalid namespace <%s>. Valid names: %s' %
761 raise ValueError('invalid namespace <%s>. Valid names: %s' %
759 (name,ns_table.keys()))
762 (name,ns_table.keys()))
760
763
761 #print 'type_pattern:',type_pattern # dbg
764 #print 'type_pattern:',type_pattern # dbg
762 search_result = []
765 search_result = []
763 for ns_name in ns_search:
766 for ns_name in ns_search:
764 ns = ns_table[ns_name]
767 ns = ns_table[ns_name]
765 tmp_res = list(list_namespace(ns,type_pattern,filter,
768 tmp_res = list(list_namespace(ns,type_pattern,filter,
766 ignore_case=ignore_case,
769 ignore_case=ignore_case,
767 show_all=show_all))
770 show_all=show_all))
768 search_result.extend(tmp_res)
771 search_result.extend(tmp_res)
769 search_result.sort()
772 search_result.sort()
770
773
771 page.page('\n'.join(search_result))
774 page.page('\n'.join(search_result))
@@ -1,300 +1,305 b''
1 import os
1 import os
2 import sys
2 import sys
3 import tempfile
3 import tempfile
4 import shutil
4 import shutil
5 import random
5 import random
6 import time
6 import time
7 from StringIO import StringIO
7 from StringIO import StringIO
8
8
9 import nose.tools as nt
9 import nose.tools as nt
10 import IPython.testing.tools as tt
10 import IPython.testing.tools as tt
11
11
12 from IPython.extensions.autoreload import AutoreloadInterface
12 from IPython.extensions.autoreload import AutoreloadInterface
13 from IPython.core.hooks import TryNext
13 from IPython.core.hooks import TryNext
14 from IPython.testing.decorators import knownfailureif
14
15
15 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
16 # Test fixture
17 # Test fixture
17 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
18
19
19 class FakeShell(object):
20 class FakeShell(object):
20 def __init__(self):
21 def __init__(self):
21 self.ns = {}
22 self.ns = {}
22 self.reloader = AutoreloadInterface()
23 self.reloader = AutoreloadInterface()
23
24
24 def run_code(self, code):
25 def run_code(self, code):
25 try:
26 try:
26 self.reloader.pre_run_code_hook(self)
27 self.reloader.pre_run_code_hook(self)
27 except TryNext:
28 except TryNext:
28 pass
29 pass
29 exec code in self.ns
30 exec code in self.ns
30
31
31 def push(self, items):
32 def push(self, items):
32 self.ns.update(items)
33 self.ns.update(items)
33
34
34 def magic_autoreload(self, parameter):
35 def magic_autoreload(self, parameter):
35 self.reloader.magic_autoreload(self, parameter)
36 self.reloader.magic_autoreload(self, parameter)
36
37
37 def magic_aimport(self, parameter, stream=None):
38 def magic_aimport(self, parameter, stream=None):
38 self.reloader.magic_aimport(self, parameter, stream=stream)
39 self.reloader.magic_aimport(self, parameter, stream=stream)
39
40
40
41
41 class Fixture(object):
42 class Fixture(object):
42 """Fixture for creating test module files"""
43 """Fixture for creating test module files"""
43
44
44 test_dir = None
45 test_dir = None
45 old_sys_path = None
46 old_sys_path = None
46 filename_chars = "abcdefghijklmopqrstuvwxyz0123456789"
47 filename_chars = "abcdefghijklmopqrstuvwxyz0123456789"
47
48
48 def setUp(self):
49 def setUp(self):
49 self.test_dir = tempfile.mkdtemp()
50 self.test_dir = tempfile.mkdtemp()
50 self.old_sys_path = list(sys.path)
51 self.old_sys_path = list(sys.path)
51 sys.path.insert(0, self.test_dir)
52 sys.path.insert(0, self.test_dir)
52 self.shell = FakeShell()
53 self.shell = FakeShell()
53
54
54 def tearDown(self):
55 def tearDown(self):
55 shutil.rmtree(self.test_dir)
56 shutil.rmtree(self.test_dir)
56 sys.path = self.old_sys_path
57 sys.path = self.old_sys_path
57 self.shell.reloader.enabled = False
58 self.shell.reloader.enabled = False
58
59
59 self.test_dir = None
60 self.test_dir = None
60 self.old_sys_path = None
61 self.old_sys_path = None
61 self.shell = None
62 self.shell = None
62
63
63 def get_module(self):
64 def get_module(self):
64 module_name = "tmpmod_" + "".join(random.sample(self.filename_chars,20))
65 module_name = "tmpmod_" + "".join(random.sample(self.filename_chars,20))
65 if module_name in sys.modules:
66 if module_name in sys.modules:
66 del sys.modules[module_name]
67 del sys.modules[module_name]
67 file_name = os.path.join(self.test_dir, module_name + ".py")
68 file_name = os.path.join(self.test_dir, module_name + ".py")
68 return module_name, file_name
69 return module_name, file_name
69
70
70 def write_file(self, filename, content):
71 def write_file(self, filename, content):
71 """
72 """
72 Write a file, and force a timestamp difference of at least one second
73 Write a file, and force a timestamp difference of at least one second
73
74
74 Notes
75 Notes
75 -----
76 -----
76 Python's .pyc files record the timestamp of their compilation
77 Python's .pyc files record the timestamp of their compilation
77 with a time resolution of one second.
78 with a time resolution of one second.
78
79
79 Therefore, we need to force a timestamp difference between .py
80 Therefore, we need to force a timestamp difference between .py
80 and .pyc, without having the .py file be timestamped in the
81 and .pyc, without having the .py file be timestamped in the
81 future, and without changing the timestamp of the .pyc file
82 future, and without changing the timestamp of the .pyc file
82 (because that is stored in the file). The only reliable way
83 (because that is stored in the file). The only reliable way
83 to achieve this seems to be to sleep.
84 to achieve this seems to be to sleep.
84
85
85 """
86 """
86
87
87 # Sleep one second + eps
88 # Sleep one second + eps
88 time.sleep(1.05)
89 time.sleep(1.05)
89
90
90 # Write
91 # Write
91 f = open(filename, 'w')
92 f = open(filename, 'w')
92 try:
93 try:
93 f.write(content)
94 f.write(content)
94 finally:
95 finally:
95 f.close()
96 f.close()
96
97
97 def new_module(self, code):
98 def new_module(self, code):
98 mod_name, mod_fn = self.get_module()
99 mod_name, mod_fn = self.get_module()
99 f = open(mod_fn, 'w')
100 f = open(mod_fn, 'w')
100 try:
101 try:
101 f.write(code)
102 f.write(code)
102 finally:
103 finally:
103 f.close()
104 f.close()
104 return mod_name, mod_fn
105 return mod_name, mod_fn
105
106
106 #-----------------------------------------------------------------------------
107 #-----------------------------------------------------------------------------
107 # Test automatic reloading
108 # Test automatic reloading
108 #-----------------------------------------------------------------------------
109 #-----------------------------------------------------------------------------
109
110
110 class TestAutoreload(Fixture):
111 class TestAutoreload(Fixture):
111 def _check_smoketest(self, use_aimport=True):
112 def _check_smoketest(self, use_aimport=True):
112 """
113 """
113 Functional test for the automatic reloader using either
114 Functional test for the automatic reloader using either
114 '%autoreload 1' or '%autoreload 2'
115 '%autoreload 1' or '%autoreload 2'
115 """
116 """
116
117
117 mod_name, mod_fn = self.new_module("""
118 mod_name, mod_fn = self.new_module("""
118 x = 9
119 x = 9
119
120
120 z = 123 # this item will be deleted
121 z = 123 # this item will be deleted
121
122
122 def foo(y):
123 def foo(y):
123 return y + 3
124 return y + 3
124
125
125 class Baz(object):
126 class Baz(object):
126 def __init__(self, x):
127 def __init__(self, x):
127 self.x = x
128 self.x = x
128 def bar(self, y):
129 def bar(self, y):
129 return self.x + y
130 return self.x + y
130 @property
131 @property
131 def quux(self):
132 def quux(self):
132 return 42
133 return 42
133 def zzz(self):
134 def zzz(self):
134 '''This method will be deleted below'''
135 '''This method will be deleted below'''
135 return 99
136 return 99
136
137
137 class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
138 class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
138 def foo(self):
139 def foo(self):
139 return 1
140 return 1
140 """)
141 """)
141
142
142 #
143 #
143 # Import module, and mark for reloading
144 # Import module, and mark for reloading
144 #
145 #
145 if use_aimport:
146 if use_aimport:
146 self.shell.magic_autoreload("1")
147 self.shell.magic_autoreload("1")
147 self.shell.magic_aimport(mod_name)
148 self.shell.magic_aimport(mod_name)
148 stream = StringIO()
149 stream = StringIO()
149 self.shell.magic_aimport("", stream=stream)
150 self.shell.magic_aimport("", stream=stream)
150 nt.assert_true(("Modules to reload:\n%s" % mod_name) in
151 nt.assert_true(("Modules to reload:\n%s" % mod_name) in
151 stream.getvalue())
152 stream.getvalue())
152
153
153 nt.assert_raises(
154 nt.assert_raises(
154 ImportError,
155 ImportError,
155 self.shell.magic_aimport, "tmpmod_as318989e89ds")
156 self.shell.magic_aimport, "tmpmod_as318989e89ds")
156 else:
157 else:
157 self.shell.magic_autoreload("2")
158 self.shell.magic_autoreload("2")
158 self.shell.run_code("import %s" % mod_name)
159 self.shell.run_code("import %s" % mod_name)
159 stream = StringIO()
160 stream = StringIO()
160 self.shell.magic_aimport("", stream=stream)
161 self.shell.magic_aimport("", stream=stream)
161 nt.assert_true("Modules to reload:\nall-except-skipped" in
162 nt.assert_true("Modules to reload:\nall-except-skipped" in
162 stream.getvalue())
163 stream.getvalue())
163 nt.assert_true(mod_name in self.shell.ns)
164 nt.assert_true(mod_name in self.shell.ns)
164
165
165 mod = sys.modules[mod_name]
166 mod = sys.modules[mod_name]
166
167
167 #
168 #
168 # Test module contents
169 # Test module contents
169 #
170 #
170 old_foo = mod.foo
171 old_foo = mod.foo
171 old_obj = mod.Baz(9)
172 old_obj = mod.Baz(9)
172 old_obj2 = mod.Bar()
173 old_obj2 = mod.Bar()
173
174
174 def check_module_contents():
175 def check_module_contents():
175 nt.assert_equal(mod.x, 9)
176 nt.assert_equal(mod.x, 9)
176 nt.assert_equal(mod.z, 123)
177 nt.assert_equal(mod.z, 123)
177
178
178 nt.assert_equal(old_foo(0), 3)
179 nt.assert_equal(old_foo(0), 3)
179 nt.assert_equal(mod.foo(0), 3)
180 nt.assert_equal(mod.foo(0), 3)
180
181
181 obj = mod.Baz(9)
182 obj = mod.Baz(9)
182 nt.assert_equal(old_obj.bar(1), 10)
183 nt.assert_equal(old_obj.bar(1), 10)
183 nt.assert_equal(obj.bar(1), 10)
184 nt.assert_equal(obj.bar(1), 10)
184 nt.assert_equal(obj.quux, 42)
185 nt.assert_equal(obj.quux, 42)
185 nt.assert_equal(obj.zzz(), 99)
186 nt.assert_equal(obj.zzz(), 99)
186
187
187 obj2 = mod.Bar()
188 obj2 = mod.Bar()
188 nt.assert_equal(old_obj2.foo(), 1)
189 nt.assert_equal(old_obj2.foo(), 1)
189 nt.assert_equal(obj2.foo(), 1)
190 nt.assert_equal(obj2.foo(), 1)
190
191
191 check_module_contents()
192 check_module_contents()
192
193
193 #
194 #
194 # Simulate a failed reload: no reload should occur and exactly
195 # Simulate a failed reload: no reload should occur and exactly
195 # one error message should be printed
196 # one error message should be printed
196 #
197 #
197 self.write_file(mod_fn, """
198 self.write_file(mod_fn, """
198 a syntax error
199 a syntax error
199 """)
200 """)
200
201
201 with tt.AssertPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
202 with tt.AssertPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
202 self.shell.run_code("pass") # trigger reload
203 self.shell.run_code("pass") # trigger reload
203 with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
204 with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
204 self.shell.run_code("pass") # trigger another reload
205 self.shell.run_code("pass") # trigger another reload
205 check_module_contents()
206 check_module_contents()
206
207
207 #
208 #
208 # Rewrite module (this time reload should succeed)
209 # Rewrite module (this time reload should succeed)
209 #
210 #
210 self.write_file(mod_fn, """
211 self.write_file(mod_fn, """
211 x = 10
212 x = 10
212
213
213 def foo(y):
214 def foo(y):
214 return y + 4
215 return y + 4
215
216
216 class Baz(object):
217 class Baz(object):
217 def __init__(self, x):
218 def __init__(self, x):
218 self.x = x
219 self.x = x
219 def bar(self, y):
220 def bar(self, y):
220 return self.x + y + 1
221 return self.x + y + 1
221 @property
222 @property
222 def quux(self):
223 def quux(self):
223 return 43
224 return 43
224
225
225 class Bar: # old-style class
226 class Bar: # old-style class
226 def foo(self):
227 def foo(self):
227 return 2
228 return 2
228 """)
229 """)
229
230
230 def check_module_contents():
231 def check_module_contents():
231 nt.assert_equal(mod.x, 10)
232 nt.assert_equal(mod.x, 10)
232 nt.assert_false(hasattr(mod, 'z'))
233 nt.assert_false(hasattr(mod, 'z'))
233
234
234 nt.assert_equal(old_foo(0), 4) # superreload magic!
235 nt.assert_equal(old_foo(0), 4) # superreload magic!
235 nt.assert_equal(mod.foo(0), 4)
236 nt.assert_equal(mod.foo(0), 4)
236
237
237 obj = mod.Baz(9)
238 obj = mod.Baz(9)
238 nt.assert_equal(old_obj.bar(1), 11) # superreload magic!
239 nt.assert_equal(old_obj.bar(1), 11) # superreload magic!
239 nt.assert_equal(obj.bar(1), 11)
240 nt.assert_equal(obj.bar(1), 11)
240
241
241 nt.assert_equal(old_obj.quux, 43)
242 nt.assert_equal(old_obj.quux, 43)
242 nt.assert_equal(obj.quux, 43)
243 nt.assert_equal(obj.quux, 43)
243
244
244 nt.assert_false(hasattr(old_obj, 'zzz'))
245 nt.assert_false(hasattr(old_obj, 'zzz'))
245 nt.assert_false(hasattr(obj, 'zzz'))
246 nt.assert_false(hasattr(obj, 'zzz'))
246
247
247 obj2 = mod.Bar()
248 obj2 = mod.Bar()
248 nt.assert_equal(old_obj2.foo(), 2)
249 nt.assert_equal(old_obj2.foo(), 2)
249 nt.assert_equal(obj2.foo(), 2)
250 nt.assert_equal(obj2.foo(), 2)
250
251
251 self.shell.run_code("pass") # trigger reload
252 self.shell.run_code("pass") # trigger reload
252 check_module_contents()
253 check_module_contents()
253
254
254 #
255 #
255 # Another failure case: deleted file (shouldn't reload)
256 # Another failure case: deleted file (shouldn't reload)
256 #
257 #
257 os.unlink(mod_fn)
258 os.unlink(mod_fn)
258
259
259 self.shell.run_code("pass") # trigger reload
260 self.shell.run_code("pass") # trigger reload
260 check_module_contents()
261 check_module_contents()
261
262
262 #
263 #
263 # Disable autoreload and rewrite module: no reload should occur
264 # Disable autoreload and rewrite module: no reload should occur
264 #
265 #
265 if use_aimport:
266 if use_aimport:
266 self.shell.magic_aimport("-" + mod_name)
267 self.shell.magic_aimport("-" + mod_name)
267 stream = StringIO()
268 stream = StringIO()
268 self.shell.magic_aimport("", stream=stream)
269 self.shell.magic_aimport("", stream=stream)
269 nt.assert_true(("Modules to skip:\n%s" % mod_name) in
270 nt.assert_true(("Modules to skip:\n%s" % mod_name) in
270 stream.getvalue())
271 stream.getvalue())
271
272
272 # This should succeed, although no such module exists
273 # This should succeed, although no such module exists
273 self.shell.magic_aimport("-tmpmod_as318989e89ds")
274 self.shell.magic_aimport("-tmpmod_as318989e89ds")
274 else:
275 else:
275 self.shell.magic_autoreload("0")
276 self.shell.magic_autoreload("0")
276
277
277 self.write_file(mod_fn, """
278 self.write_file(mod_fn, """
278 x = -99
279 x = -99
279 """)
280 """)
280
281
281 self.shell.run_code("pass") # trigger reload
282 self.shell.run_code("pass") # trigger reload
282 self.shell.run_code("pass")
283 self.shell.run_code("pass")
283 check_module_contents()
284 check_module_contents()
284
285
285 #
286 #
286 # Re-enable autoreload: reload should now occur
287 # Re-enable autoreload: reload should now occur
287 #
288 #
288 if use_aimport:
289 if use_aimport:
289 self.shell.magic_aimport(mod_name)
290 self.shell.magic_aimport(mod_name)
290 else:
291 else:
291 self.shell.magic_autoreload("")
292 self.shell.magic_autoreload("")
292
293
293 self.shell.run_code("pass") # trigger reload
294 self.shell.run_code("pass") # trigger reload
294 nt.assert_equal(mod.x, -99)
295 nt.assert_equal(mod.x, -99)
295
296
297 # The autoreload extension needs to be updated for Python 3.2, as .pyc files
298 # are stored in a different location. See gh-846.
299 @knownfailureif(sys.version_info >= (3,2))
296 def test_smoketest_aimport(self):
300 def test_smoketest_aimport(self):
297 self._check_smoketest(use_aimport=True)
301 self._check_smoketest(use_aimport=True)
298
302
303 @knownfailureif(sys.version_info >= (3,2))
299 def test_smoketest_autoreload(self):
304 def test_smoketest_autoreload(self):
300 self._check_smoketest(use_aimport=False)
305 self._check_smoketest(use_aimport=False)
@@ -1,170 +1,174 b''
1 """Test suite for the irunner module.
1 """Test suite for the irunner module.
2
2
3 Not the most elegant or fine-grained, but it does cover at least the bulk
3 Not the most elegant or fine-grained, but it does cover at least the bulk
4 functionality."""
4 functionality."""
5
5
6 # Global to make tests extra verbose and help debugging
6 # Global to make tests extra verbose and help debugging
7 VERBOSE = True
7 VERBOSE = True
8
8
9 # stdlib imports
9 # stdlib imports
10 import StringIO
10 import StringIO
11 import sys
11 import sys
12 import unittest
12 import unittest
13
13
14 # IPython imports
14 # IPython imports
15 from IPython.lib import irunner
15 from IPython.lib import irunner
16 from IPython.testing.decorators import known_failure_py3
16
17
17 # Testing code begins
18 # Testing code begins
18 class RunnerTestCase(unittest.TestCase):
19 class RunnerTestCase(unittest.TestCase):
19
20
20 def setUp(self):
21 def setUp(self):
21 self.out = StringIO.StringIO()
22 self.out = StringIO.StringIO()
22 #self.out = sys.stdout
23 #self.out = sys.stdout
23
24
24 def _test_runner(self,runner,source,output):
25 def _test_runner(self,runner,source,output):
25 """Test that a given runner's input/output match."""
26 """Test that a given runner's input/output match."""
26
27
27 runner.run_source(source)
28 runner.run_source(source)
28 out = self.out.getvalue()
29 out = self.out.getvalue()
29 #out = ''
30 #out = ''
30 # this output contains nasty \r\n lineends, and the initial ipython
31 # this output contains nasty \r\n lineends, and the initial ipython
31 # banner. clean it up for comparison, removing lines of whitespace
32 # banner. clean it up for comparison, removing lines of whitespace
32 output_l = [l for l in output.splitlines() if l and not l.isspace()]
33 output_l = [l for l in output.splitlines() if l and not l.isspace()]
33 out_l = [l for l in out.splitlines() if l and not l.isspace()]
34 out_l = [l for l in out.splitlines() if l and not l.isspace()]
34 mismatch = 0
35 mismatch = 0
35 if len(output_l) != len(out_l):
36 if len(output_l) != len(out_l):
36 message = ("Mismatch in number of lines\n\n"
37 message = ("Mismatch in number of lines\n\n"
37 "Expected:\n"
38 "Expected:\n"
38 "~~~~~~~~~\n"
39 "~~~~~~~~~\n"
39 "%s\n\n"
40 "%s\n\n"
40 "Got:\n"
41 "Got:\n"
41 "~~~~~~~~~\n"
42 "~~~~~~~~~\n"
42 "%s"
43 "%s"
43 ) % ("\n".join(output_l), "\n".join(out_l))
44 ) % ("\n".join(output_l), "\n".join(out_l))
44 self.fail(message)
45 self.fail(message)
45 for n in range(len(output_l)):
46 for n in range(len(output_l)):
46 # Do a line-by-line comparison
47 # Do a line-by-line comparison
47 ol1 = output_l[n].strip()
48 ol1 = output_l[n].strip()
48 ol2 = out_l[n].strip()
49 ol2 = out_l[n].strip()
49 if ol1 != ol2:
50 if ol1 != ol2:
50 mismatch += 1
51 mismatch += 1
51 if VERBOSE:
52 if VERBOSE:
52 print '<<< line %s does not match:' % n
53 print '<<< line %s does not match:' % n
53 print repr(ol1)
54 print repr(ol1)
54 print repr(ol2)
55 print repr(ol2)
55 print '>>>'
56 print '>>>'
56 self.assert_(mismatch==0,'Number of mismatched lines: %s' %
57 self.assert_(mismatch==0,'Number of mismatched lines: %s' %
57 mismatch)
58 mismatch)
58
59
60 # irunner isn't working on Python 3 (due to pexpect)
61 @known_failure_py3
59 def testIPython(self):
62 def testIPython(self):
60 """Test the IPython runner."""
63 """Test the IPython runner."""
61 source = """
64 source = """
62 print 'hello, this is python'
65 print 'hello, this is python'
63 # some more code
66 # some more code
64 x=1;y=2
67 x=1;y=2
65 x+y**2
68 x+y**2
66
69
67 # An example of autocall functionality
70 # An example of autocall functionality
68 from math import *
71 from math import *
69 autocall 1
72 autocall 1
70 cos pi
73 cos pi
71 autocall 0
74 autocall 0
72 cos pi
75 cos pi
73 cos(pi)
76 cos(pi)
74
77
75 for i in range(5):
78 for i in range(5):
76 print i,
79 print i,
77
80
78 print "that's all folks!"
81 print "that's all folks!"
79
82
80 exit
83 exit
81 """
84 """
82 output = """\
85 output = """\
83 In [1]: print 'hello, this is python'
86 In [1]: print 'hello, this is python'
84 hello, this is python
87 hello, this is python
85
88
86
89
87 # some more code
90 # some more code
88 In [2]: x=1;y=2
91 In [2]: x=1;y=2
89
92
90 In [3]: x+y**2
93 In [3]: x+y**2
91 Out[3]: 5
94 Out[3]: 5
92
95
93
96
94 # An example of autocall functionality
97 # An example of autocall functionality
95 In [4]: from math import *
98 In [4]: from math import *
96
99
97 In [5]: autocall 1
100 In [5]: autocall 1
98 Automatic calling is: Smart
101 Automatic calling is: Smart
99
102
100 In [6]: cos pi
103 In [6]: cos pi
101 ------> cos(pi)
104 ------> cos(pi)
102 Out[6]: -1.0
105 Out[6]: -1.0
103
106
104 In [7]: autocall 0
107 In [7]: autocall 0
105 Automatic calling is: OFF
108 Automatic calling is: OFF
106
109
107 In [8]: cos pi
110 In [8]: cos pi
108 File "<ipython-input-8-6bd7313dd9a9>", line 1
111 File "<ipython-input-8-6bd7313dd9a9>", line 1
109 cos pi
112 cos pi
110 ^
113 ^
111 SyntaxError: invalid syntax
114 SyntaxError: invalid syntax
112
115
113
116
114 In [9]: cos(pi)
117 In [9]: cos(pi)
115 Out[9]: -1.0
118 Out[9]: -1.0
116
119
117
120
118 In [10]: for i in range(5):
121 In [10]: for i in range(5):
119 ....: print i,
122 ....: print i,
120 ....:
123 ....:
121 0 1 2 3 4
124 0 1 2 3 4
122
125
123 In [11]: print "that's all folks!"
126 In [11]: print "that's all folks!"
124 that's all folks!
127 that's all folks!
125
128
126
129
127 In [12]: exit
130 In [12]: exit
128 """
131 """
129 runner = irunner.IPythonRunner(out=self.out)
132 runner = irunner.IPythonRunner(out=self.out)
130 self._test_runner(runner,source,output)
133 self._test_runner(runner,source,output)
131
134
135 @known_failure_py3
132 def testPython(self):
136 def testPython(self):
133 """Test the Python runner."""
137 """Test the Python runner."""
134 runner = irunner.PythonRunner(out=self.out)
138 runner = irunner.PythonRunner(out=self.out)
135 source = """
139 source = """
136 print 'hello, this is python'
140 print 'hello, this is python'
137
141
138 # some more code
142 # some more code
139 x=1;y=2
143 x=1;y=2
140 x+y**2
144 x+y**2
141
145
142 from math import *
146 from math import *
143 cos(pi)
147 cos(pi)
144
148
145 for i in range(5):
149 for i in range(5):
146 print i,
150 print i,
147
151
148 print "that's all folks!"
152 print "that's all folks!"
149 """
153 """
150 output = """\
154 output = """\
151 >>> print 'hello, this is python'
155 >>> print 'hello, this is python'
152 hello, this is python
156 hello, this is python
153
157
154 # some more code
158 # some more code
155 >>> x=1;y=2
159 >>> x=1;y=2
156 >>> x+y**2
160 >>> x+y**2
157 5
161 5
158
162
159 >>> from math import *
163 >>> from math import *
160 >>> cos(pi)
164 >>> cos(pi)
161 -1.0
165 -1.0
162
166
163 >>> for i in range(5):
167 >>> for i in range(5):
164 ... print i,
168 ... print i,
165 ...
169 ...
166 0 1 2 3 4
170 0 1 2 3 4
167 >>> print "that's all folks!"
171 >>> print "that's all folks!"
168 that's all folks!
172 that's all folks!
169 """
173 """
170 self._test_runner(runner,source,output)
174 self._test_runner(runner,source,output)
@@ -1,108 +1,110 b''
1 """Test suite for pylab_import_all magic
1 """Test suite for pylab_import_all magic
2 Modified from the irunner module but using regex.
2 Modified from the irunner module but using regex.
3 """
3 """
4
4
5 # Global to make tests extra verbose and help debugging
5 # Global to make tests extra verbose and help debugging
6 VERBOSE = True
6 VERBOSE = True
7
7
8 # stdlib imports
8 # stdlib imports
9 import StringIO
9 import StringIO
10 import sys
10 import sys
11 import unittest
11 import unittest
12 import re
12 import re
13
13
14 # IPython imports
14 # IPython imports
15 from IPython.lib import irunner
15 from IPython.lib import irunner
16 from IPython.testing import decorators
16 from IPython.testing import decorators
17
17
18 # Testing code begins
18 # Testing code begins
19 class RunnerTestCase(unittest.TestCase):
19 class RunnerTestCase(unittest.TestCase):
20
20
21 def setUp(self):
21 def setUp(self):
22 self.out = StringIO.StringIO()
22 self.out = StringIO.StringIO()
23 #self.out = sys.stdout
23 #self.out = sys.stdout
24
24
25 @decorators.known_failure_py3
25 def _test_runner(self,runner,source,output):
26 def _test_runner(self,runner,source,output):
26 """Test that a given runner's input/output match."""
27 """Test that a given runner's input/output match."""
27
28
28 runner.run_source(source)
29 runner.run_source(source)
29 out = self.out.getvalue()
30 out = self.out.getvalue()
30 #out = ''
31 #out = ''
31 # this output contains nasty \r\n lineends, and the initial ipython
32 # this output contains nasty \r\n lineends, and the initial ipython
32 # banner. clean it up for comparison, removing lines of whitespace
33 # banner. clean it up for comparison, removing lines of whitespace
33 output_l = [l for l in output.splitlines() if l and not l.isspace()]
34 output_l = [l for l in output.splitlines() if l and not l.isspace()]
34 out_l = [l for l in out.splitlines() if l and not l.isspace()]
35 out_l = [l for l in out.splitlines() if l and not l.isspace()]
35 mismatch = 0
36 mismatch = 0
36 if len(output_l) != len(out_l):
37 if len(output_l) != len(out_l):
37 message = ("Mismatch in number of lines\n\n"
38 message = ("Mismatch in number of lines\n\n"
38 "Expected:\n"
39 "Expected:\n"
39 "~~~~~~~~~\n"
40 "~~~~~~~~~\n"
40 "%s\n\n"
41 "%s\n\n"
41 "Got:\n"
42 "Got:\n"
42 "~~~~~~~~~\n"
43 "~~~~~~~~~\n"
43 "%s"
44 "%s"
44 ) % ("\n".join(output_l), "\n".join(out_l))
45 ) % ("\n".join(output_l), "\n".join(out_l))
45 self.fail(message)
46 self.fail(message)
46 for n in range(len(output_l)):
47 for n in range(len(output_l)):
47 # Do a line-by-line comparison
48 # Do a line-by-line comparison
48 ol1 = output_l[n].strip()
49 ol1 = output_l[n].strip()
49 ol2 = out_l[n].strip()
50 ol2 = out_l[n].strip()
50 if not re.match(ol1,ol2):
51 if not re.match(ol1,ol2):
51 mismatch += 1
52 mismatch += 1
52 if VERBOSE:
53 if VERBOSE:
53 print '<<< line %s does not match:' % n
54 print '<<< line %s does not match:' % n
54 print repr(ol1)
55 print repr(ol1)
55 print repr(ol2)
56 print repr(ol2)
56 print '>>>'
57 print '>>>'
57 self.assert_(mismatch==0,'Number of mismatched lines: %s' %
58 self.assert_(mismatch==0,'Number of mismatched lines: %s' %
58 mismatch)
59 mismatch)
59
60
60 @decorators.skipif_not_matplotlib
61 @decorators.skipif_not_matplotlib
61 def test_pylab_import_all_enabled(self):
62 def test_pylab_import_all_enabled(self):
62 "Verify that plot is available when pylab_import_all = True"
63 "Verify that plot is available when pylab_import_all = True"
63 source = """
64 source = """
64 from IPython.config.application import Application
65 from IPython.config.application import Application
65 app = Application.instance()
66 app = Application.instance()
66 app.pylab_import_all = True
67 app.pylab_import_all = True
67 pylab
68 pylab
68 ip=get_ipython()
69 ip=get_ipython()
69 'plot' in ip.user_ns
70 'plot' in ip.user_ns
70 """
71 """
71 output = """
72 output = """
72 In \[1\]: from IPython\.config\.application import Application
73 In \[1\]: from IPython\.config\.application import Application
73 In \[2\]: app = Application\.instance\(\)
74 In \[2\]: app = Application\.instance\(\)
74 In \[3\]: app\.pylab_import_all = True
75 In \[3\]: app\.pylab_import_all = True
75 In \[4\]: pylab
76 In \[4\]: pylab
76 ^Welcome to pylab, a matplotlib-based Python environment
77 ^Welcome to pylab, a matplotlib-based Python environment
77 For more information, type 'help\(pylab\)'\.
78 For more information, type 'help\(pylab\)'\.
78 In \[5\]: ip=get_ipython\(\)
79 In \[5\]: ip=get_ipython\(\)
79 In \[6\]: \'plot\' in ip\.user_ns
80 In \[6\]: \'plot\' in ip\.user_ns
80 Out\[6\]: True
81 Out\[6\]: True
81 """
82 """
82 runner = irunner.IPythonRunner(out=self.out)
83 runner = irunner.IPythonRunner(out=self.out)
83 self._test_runner(runner,source,output)
84 self._test_runner(runner,source,output)
84
85
86 @decorators.known_failure_py3
85 @decorators.skipif_not_matplotlib
87 @decorators.skipif_not_matplotlib
86 def test_pylab_import_all_disabled(self):
88 def test_pylab_import_all_disabled(self):
87 "Verify that plot is not available when pylab_import_all = False"
89 "Verify that plot is not available when pylab_import_all = False"
88 source = """
90 source = """
89 from IPython.config.application import Application
91 from IPython.config.application import Application
90 app = Application.instance()
92 app = Application.instance()
91 app.pylab_import_all = False
93 app.pylab_import_all = False
92 pylab
94 pylab
93 ip=get_ipython()
95 ip=get_ipython()
94 'plot' in ip.user_ns
96 'plot' in ip.user_ns
95 """
97 """
96 output = """
98 output = """
97 In \[1\]: from IPython\.config\.application import Application
99 In \[1\]: from IPython\.config\.application import Application
98 In \[2\]: app = Application\.instance\(\)
100 In \[2\]: app = Application\.instance\(\)
99 In \[3\]: app\.pylab_import_all = False
101 In \[3\]: app\.pylab_import_all = False
100 In \[4\]: pylab
102 In \[4\]: pylab
101 ^Welcome to pylab, a matplotlib-based Python environment
103 ^Welcome to pylab, a matplotlib-based Python environment
102 For more information, type 'help\(pylab\)'\.
104 For more information, type 'help\(pylab\)'\.
103 In \[5\]: ip=get_ipython\(\)
105 In \[5\]: ip=get_ipython\(\)
104 In \[6\]: \'plot\' in ip\.user_ns
106 In \[6\]: \'plot\' in ip\.user_ns
105 Out\[6\]: False
107 Out\[6\]: False
106 """
108 """
107 runner = irunner.IPythonRunner(out=self.out)
109 runner = irunner.IPythonRunner(out=self.out)
108 self._test_runner(runner,source,output)
110 self._test_runner(runner,source,output)
@@ -1,341 +1,344 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Decorators for labeling test objects.
2 """Decorators for labeling test objects.
3
3
4 Decorators that merely return a modified version of the original function
4 Decorators that merely return a modified version of the original function
5 object are straightforward. Decorators that return a new function object need
5 object are straightforward. Decorators that return a new function object need
6 to use nose.tools.make_decorator(original_function)(decorator) in returning the
6 to use nose.tools.make_decorator(original_function)(decorator) in returning the
7 decorator, in order to preserve metadata such as function name, setup and
7 decorator, in order to preserve metadata such as function name, setup and
8 teardown functions and so on - see nose.tools for more information.
8 teardown functions and so on - see nose.tools for more information.
9
9
10 This module provides a set of useful decorators meant to be ready to use in
10 This module provides a set of useful decorators meant to be ready to use in
11 your own tests. See the bottom of the file for the ready-made ones, and if you
11 your own tests. See the bottom of the file for the ready-made ones, and if you
12 find yourself writing a new one that may be of generic use, add it here.
12 find yourself writing a new one that may be of generic use, add it here.
13
13
14 Included decorators:
14 Included decorators:
15
15
16
16
17 Lightweight testing that remains unittest-compatible.
17 Lightweight testing that remains unittest-compatible.
18
18
19 - @parametric, for parametric test support that is vastly easier to use than
19 - @parametric, for parametric test support that is vastly easier to use than
20 nose's for debugging. With ours, if a test fails, the stack under inspection
20 nose's for debugging. With ours, if a test fails, the stack under inspection
21 is that of the test and not that of the test framework.
21 is that of the test and not that of the test framework.
22
22
23 - An @as_unittest decorator can be used to tag any normal parameter-less
23 - An @as_unittest decorator can be used to tag any normal parameter-less
24 function as a unittest TestCase. Then, both nose and normal unittest will
24 function as a unittest TestCase. Then, both nose and normal unittest will
25 recognize it as such. This will make it easier to migrate away from Nose if
25 recognize it as such. This will make it easier to migrate away from Nose if
26 we ever need/want to while maintaining very lightweight tests.
26 we ever need/want to while maintaining very lightweight tests.
27
27
28 NOTE: This file contains IPython-specific decorators. Using the machinery in
28 NOTE: This file contains IPython-specific decorators. Using the machinery in
29 IPython.external.decorators, we import either numpy.testing.decorators if numpy is
29 IPython.external.decorators, we import either numpy.testing.decorators if numpy is
30 available, OR use equivalent code in IPython.external._decorators, which
30 available, OR use equivalent code in IPython.external._decorators, which
31 we've copied verbatim from numpy.
31 we've copied verbatim from numpy.
32
32
33 Authors
33 Authors
34 -------
34 -------
35
35
36 - Fernando Perez <Fernando.Perez@berkeley.edu>
36 - Fernando Perez <Fernando.Perez@berkeley.edu>
37 """
37 """
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # Copyright (C) 2009-2010 The IPython Development Team
40 # Copyright (C) 2009-2010 The IPython Development Team
41 #
41 #
42 # Distributed under the terms of the BSD License. The full license is in
42 # Distributed under the terms of the BSD License. The full license is in
43 # the file COPYING, distributed as part of this software.
43 # the file COPYING, distributed as part of this software.
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45
45
46 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
47 # Imports
47 # Imports
48 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49
49
50 # Stdlib imports
50 # Stdlib imports
51 import inspect
51 import inspect
52 import sys
52 import sys
53 import tempfile
53 import tempfile
54 import unittest
54 import unittest
55
55
56 # Third-party imports
56 # Third-party imports
57
57
58 # This is Michele Simionato's decorator module, kept verbatim.
58 # This is Michele Simionato's decorator module, kept verbatim.
59 from IPython.external.decorator import decorator
59 from IPython.external.decorator import decorator
60
60
61 # We already have python3-compliant code for parametric tests
61 # We already have python3-compliant code for parametric tests
62 if sys.version[0]=='2':
62 if sys.version[0]=='2':
63 from _paramtestpy2 import parametric, ParametricTestCase
63 from _paramtestpy2 import parametric, ParametricTestCase
64 else:
64 else:
65 from _paramtestpy3 import parametric, ParametricTestCase
65 from _paramtestpy3 import parametric, ParametricTestCase
66
66
67 # Expose the unittest-driven decorators
67 # Expose the unittest-driven decorators
68 from ipunittest import ipdoctest, ipdocstring
68 from ipunittest import ipdoctest, ipdocstring
69
69
70 # Grab the numpy-specific decorators which we keep in a file that we
70 # Grab the numpy-specific decorators which we keep in a file that we
71 # occasionally update from upstream: decorators.py is a copy of
71 # occasionally update from upstream: decorators.py is a copy of
72 # numpy.testing.decorators, we expose all of it here.
72 # numpy.testing.decorators, we expose all of it here.
73 from IPython.external.decorators import *
73 from IPython.external.decorators import *
74
74
75 #-----------------------------------------------------------------------------
75 #-----------------------------------------------------------------------------
76 # Classes and functions
76 # Classes and functions
77 #-----------------------------------------------------------------------------
77 #-----------------------------------------------------------------------------
78
78
79 # Simple example of the basic idea
79 # Simple example of the basic idea
80 def as_unittest(func):
80 def as_unittest(func):
81 """Decorator to make a simple function into a normal test via unittest."""
81 """Decorator to make a simple function into a normal test via unittest."""
82 class Tester(unittest.TestCase):
82 class Tester(unittest.TestCase):
83 def test(self):
83 def test(self):
84 func()
84 func()
85
85
86 Tester.__name__ = func.__name__
86 Tester.__name__ = func.__name__
87
87
88 return Tester
88 return Tester
89
89
90 # Utility functions
90 # Utility functions
91
91
92 def apply_wrapper(wrapper,func):
92 def apply_wrapper(wrapper,func):
93 """Apply a wrapper to a function for decoration.
93 """Apply a wrapper to a function for decoration.
94
94
95 This mixes Michele Simionato's decorator tool with nose's make_decorator,
95 This mixes Michele Simionato's decorator tool with nose's make_decorator,
96 to apply a wrapper in a decorator so that all nose attributes, as well as
96 to apply a wrapper in a decorator so that all nose attributes, as well as
97 function signature and other properties, survive the decoration cleanly.
97 function signature and other properties, survive the decoration cleanly.
98 This will ensure that wrapped functions can still be well introspected via
98 This will ensure that wrapped functions can still be well introspected via
99 IPython, for example.
99 IPython, for example.
100 """
100 """
101 import nose.tools
101 import nose.tools
102
102
103 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
103 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
104
104
105
105
106 def make_label_dec(label,ds=None):
106 def make_label_dec(label,ds=None):
107 """Factory function to create a decorator that applies one or more labels.
107 """Factory function to create a decorator that applies one or more labels.
108
108
109 Parameters
109 Parameters
110 ----------
110 ----------
111 label : string or sequence
111 label : string or sequence
112 One or more labels that will be applied by the decorator to the functions
112 One or more labels that will be applied by the decorator to the functions
113 it decorates. Labels are attributes of the decorated function with their
113 it decorates. Labels are attributes of the decorated function with their
114 value set to True.
114 value set to True.
115
115
116 ds : string
116 ds : string
117 An optional docstring for the resulting decorator. If not given, a
117 An optional docstring for the resulting decorator. If not given, a
118 default docstring is auto-generated.
118 default docstring is auto-generated.
119
119
120 Returns
120 Returns
121 -------
121 -------
122 A decorator.
122 A decorator.
123
123
124 Examples
124 Examples
125 --------
125 --------
126
126
127 A simple labeling decorator:
127 A simple labeling decorator:
128 >>> slow = make_label_dec('slow')
128 >>> slow = make_label_dec('slow')
129 >>> print slow.__doc__
129 >>> print slow.__doc__
130 Labels a test as 'slow'.
130 Labels a test as 'slow'.
131
131
132 And one that uses multiple labels and a custom docstring:
132 And one that uses multiple labels and a custom docstring:
133 >>> rare = make_label_dec(['slow','hard'],
133 >>> rare = make_label_dec(['slow','hard'],
134 ... "Mix labels 'slow' and 'hard' for rare tests.")
134 ... "Mix labels 'slow' and 'hard' for rare tests.")
135 >>> print rare.__doc__
135 >>> print rare.__doc__
136 Mix labels 'slow' and 'hard' for rare tests.
136 Mix labels 'slow' and 'hard' for rare tests.
137
137
138 Now, let's test using this one:
138 Now, let's test using this one:
139 >>> @rare
139 >>> @rare
140 ... def f(): pass
140 ... def f(): pass
141 ...
141 ...
142 >>>
142 >>>
143 >>> f.slow
143 >>> f.slow
144 True
144 True
145 >>> f.hard
145 >>> f.hard
146 True
146 True
147 """
147 """
148
148
149 if isinstance(label,basestring):
149 if isinstance(label,basestring):
150 labels = [label]
150 labels = [label]
151 else:
151 else:
152 labels = label
152 labels = label
153
153
154 # Validate that the given label(s) are OK for use in setattr() by doing a
154 # Validate that the given label(s) are OK for use in setattr() by doing a
155 # dry run on a dummy function.
155 # dry run on a dummy function.
156 tmp = lambda : None
156 tmp = lambda : None
157 for label in labels:
157 for label in labels:
158 setattr(tmp,label,True)
158 setattr(tmp,label,True)
159
159
160 # This is the actual decorator we'll return
160 # This is the actual decorator we'll return
161 def decor(f):
161 def decor(f):
162 for label in labels:
162 for label in labels:
163 setattr(f,label,True)
163 setattr(f,label,True)
164 return f
164 return f
165
165
166 # Apply the user's docstring, or autogenerate a basic one
166 # Apply the user's docstring, or autogenerate a basic one
167 if ds is None:
167 if ds is None:
168 ds = "Labels a test as %r." % label
168 ds = "Labels a test as %r." % label
169 decor.__doc__ = ds
169 decor.__doc__ = ds
170
170
171 return decor
171 return decor
172
172
173
173
174 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
174 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
175 # preserve function metadata better and allows the skip condition to be a
175 # preserve function metadata better and allows the skip condition to be a
176 # callable.
176 # callable.
177 def skipif(skip_condition, msg=None):
177 def skipif(skip_condition, msg=None):
178 ''' Make function raise SkipTest exception if skip_condition is true
178 ''' Make function raise SkipTest exception if skip_condition is true
179
179
180 Parameters
180 Parameters
181 ----------
181 ----------
182 skip_condition : bool or callable.
182 skip_condition : bool or callable.
183 Flag to determine whether to skip test. If the condition is a
183 Flag to determine whether to skip test. If the condition is a
184 callable, it is used at runtime to dynamically make the decision. This
184 callable, it is used at runtime to dynamically make the decision. This
185 is useful for tests that may require costly imports, to delay the cost
185 is useful for tests that may require costly imports, to delay the cost
186 until the test suite is actually executed.
186 until the test suite is actually executed.
187 msg : string
187 msg : string
188 Message to give on raising a SkipTest exception
188 Message to give on raising a SkipTest exception
189
189
190 Returns
190 Returns
191 -------
191 -------
192 decorator : function
192 decorator : function
193 Decorator, which, when applied to a function, causes SkipTest
193 Decorator, which, when applied to a function, causes SkipTest
194 to be raised when the skip_condition was True, and the function
194 to be raised when the skip_condition was True, and the function
195 to be called normally otherwise.
195 to be called normally otherwise.
196
196
197 Notes
197 Notes
198 -----
198 -----
199 You will see from the code that we had to further decorate the
199 You will see from the code that we had to further decorate the
200 decorator with the nose.tools.make_decorator function in order to
200 decorator with the nose.tools.make_decorator function in order to
201 transmit function name, and various other metadata.
201 transmit function name, and various other metadata.
202 '''
202 '''
203
203
204 def skip_decorator(f):
204 def skip_decorator(f):
205 # Local import to avoid a hard nose dependency and only incur the
205 # Local import to avoid a hard nose dependency and only incur the
206 # import time overhead at actual test-time.
206 # import time overhead at actual test-time.
207 import nose
207 import nose
208
208
209 # Allow for both boolean or callable skip conditions.
209 # Allow for both boolean or callable skip conditions.
210 if callable(skip_condition):
210 if callable(skip_condition):
211 skip_val = skip_condition
211 skip_val = skip_condition
212 else:
212 else:
213 skip_val = lambda : skip_condition
213 skip_val = lambda : skip_condition
214
214
215 def get_msg(func,msg=None):
215 def get_msg(func,msg=None):
216 """Skip message with information about function being skipped."""
216 """Skip message with information about function being skipped."""
217 if msg is None: out = 'Test skipped due to test condition.'
217 if msg is None: out = 'Test skipped due to test condition.'
218 else: out = msg
218 else: out = msg
219 return "Skipping test: %s. %s" % (func.__name__,out)
219 return "Skipping test: %s. %s" % (func.__name__,out)
220
220
221 # We need to define *two* skippers because Python doesn't allow both
221 # We need to define *two* skippers because Python doesn't allow both
222 # return with value and yield inside the same function.
222 # return with value and yield inside the same function.
223 def skipper_func(*args, **kwargs):
223 def skipper_func(*args, **kwargs):
224 """Skipper for normal test functions."""
224 """Skipper for normal test functions."""
225 if skip_val():
225 if skip_val():
226 raise nose.SkipTest(get_msg(f,msg))
226 raise nose.SkipTest(get_msg(f,msg))
227 else:
227 else:
228 return f(*args, **kwargs)
228 return f(*args, **kwargs)
229
229
230 def skipper_gen(*args, **kwargs):
230 def skipper_gen(*args, **kwargs):
231 """Skipper for test generators."""
231 """Skipper for test generators."""
232 if skip_val():
232 if skip_val():
233 raise nose.SkipTest(get_msg(f,msg))
233 raise nose.SkipTest(get_msg(f,msg))
234 else:
234 else:
235 for x in f(*args, **kwargs):
235 for x in f(*args, **kwargs):
236 yield x
236 yield x
237
237
238 # Choose the right skipper to use when building the actual generator.
238 # Choose the right skipper to use when building the actual generator.
239 if nose.util.isgenerator(f):
239 if nose.util.isgenerator(f):
240 skipper = skipper_gen
240 skipper = skipper_gen
241 else:
241 else:
242 skipper = skipper_func
242 skipper = skipper_func
243
243
244 return nose.tools.make_decorator(f)(skipper)
244 return nose.tools.make_decorator(f)(skipper)
245
245
246 return skip_decorator
246 return skip_decorator
247
247
248 # A version with the condition set to true, common case just to attacha message
248 # A version with the condition set to true, common case just to attacha message
249 # to a skip decorator
249 # to a skip decorator
250 def skip(msg=None):
250 def skip(msg=None):
251 """Decorator factory - mark a test function for skipping from test suite.
251 """Decorator factory - mark a test function for skipping from test suite.
252
252
253 Parameters
253 Parameters
254 ----------
254 ----------
255 msg : string
255 msg : string
256 Optional message to be added.
256 Optional message to be added.
257
257
258 Returns
258 Returns
259 -------
259 -------
260 decorator : function
260 decorator : function
261 Decorator, which, when applied to a function, causes SkipTest
261 Decorator, which, when applied to a function, causes SkipTest
262 to be raised, with the optional message added.
262 to be raised, with the optional message added.
263 """
263 """
264
264
265 return skipif(True,msg)
265 return skipif(True,msg)
266
266
267
267
268 def onlyif(condition, msg):
268 def onlyif(condition, msg):
269 """The reverse from skipif, see skipif for details."""
269 """The reverse from skipif, see skipif for details."""
270
270
271 if callable(condition):
271 if callable(condition):
272 skip_condition = lambda : not condition()
272 skip_condition = lambda : not condition()
273 else:
273 else:
274 skip_condition = lambda : not condition
274 skip_condition = lambda : not condition
275
275
276 return skipif(skip_condition, msg)
276 return skipif(skip_condition, msg)
277
277
278 #-----------------------------------------------------------------------------
278 #-----------------------------------------------------------------------------
279 # Utility functions for decorators
279 # Utility functions for decorators
280 def module_not_available(module):
280 def module_not_available(module):
281 """Can module be imported? Returns true if module does NOT import.
281 """Can module be imported? Returns true if module does NOT import.
282
282
283 This is used to make a decorator to skip tests that require module to be
283 This is used to make a decorator to skip tests that require module to be
284 available, but delay the 'import numpy' to test execution time.
284 available, but delay the 'import numpy' to test execution time.
285 """
285 """
286 try:
286 try:
287 mod = __import__(module)
287 mod = __import__(module)
288 mod_not_avail = False
288 mod_not_avail = False
289 except ImportError:
289 except ImportError:
290 mod_not_avail = True
290 mod_not_avail = True
291
291
292 return mod_not_avail
292 return mod_not_avail
293
293
294 #-----------------------------------------------------------------------------
294 #-----------------------------------------------------------------------------
295 # Decorators for public use
295 # Decorators for public use
296
296
297 # Decorators to skip certain tests on specific platforms.
297 # Decorators to skip certain tests on specific platforms.
298 skip_win32 = skipif(sys.platform == 'win32',
298 skip_win32 = skipif(sys.platform == 'win32',
299 "This test does not run under Windows")
299 "This test does not run under Windows")
300 skip_linux = skipif(sys.platform.startswith('linux'),
300 skip_linux = skipif(sys.platform.startswith('linux'),
301 "This test does not run under Linux")
301 "This test does not run under Linux")
302 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
302 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
303
303
304
304
305 # Decorators to skip tests if not on specific platforms.
305 # Decorators to skip tests if not on specific platforms.
306 skip_if_not_win32 = skipif(sys.platform != 'win32',
306 skip_if_not_win32 = skipif(sys.platform != 'win32',
307 "This test only runs under Windows")
307 "This test only runs under Windows")
308 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
308 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
309 "This test only runs under Linux")
309 "This test only runs under Linux")
310 skip_if_not_osx = skipif(sys.platform != 'darwin',
310 skip_if_not_osx = skipif(sys.platform != 'darwin',
311 "This test only runs under OSX")
311 "This test only runs under OSX")
312
312
313 # Other skip decorators
313 # Other skip decorators
314
314
315 # generic skip without module
315 # generic skip without module
316 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
316 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
317
317
318 skipif_not_numpy = skip_without('numpy')
318 skipif_not_numpy = skip_without('numpy')
319
319
320 skipif_not_matplotlib = skip_without('matplotlib')
320 skipif_not_matplotlib = skip_without('matplotlib')
321
321
322 skipif_not_sympy = skip_without('sympy')
322 skipif_not_sympy = skip_without('sympy')
323
323
324 skip_known_failure = knownfailureif(True,'This test is known to fail')
324 skip_known_failure = knownfailureif(True,'This test is known to fail')
325
325
326 known_failure_py3 = knownfailureif(sys.version_info[0] >= 3,
327 'This test is known to fail on Python 3.')
328
326 # A null 'decorator', useful to make more readable code that needs to pick
329 # A null 'decorator', useful to make more readable code that needs to pick
327 # between different decorators based on OS or other conditions
330 # between different decorators based on OS or other conditions
328 null_deco = lambda f: f
331 null_deco = lambda f: f
329
332
330 # Some tests only run where we can use unicode paths. Note that we can't just
333 # Some tests only run where we can use unicode paths. Note that we can't just
331 # check os.path.supports_unicode_filenames, which is always False on Linux.
334 # check os.path.supports_unicode_filenames, which is always False on Linux.
332 try:
335 try:
333 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
336 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
334 except UnicodeEncodeError:
337 except UnicodeEncodeError:
335 unicode_paths = False
338 unicode_paths = False
336 else:
339 else:
337 unicode_paths = True
340 unicode_paths = True
338 f.close()
341 f.close()
339
342
340 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
343 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
341 "where we can use unicode in filenames."))
344 "where we can use unicode in filenames."))
@@ -1,15 +1,21 b''
1 """This decorator marks that a doctest should be skipped.
1 """This decorator marks that a doctest should be skipped.
2
2
3 The IPython.testing.decorators module triggers various extra imports, including
3 The IPython.testing.decorators module triggers various extra imports, including
4 numpy and sympy if they're present. Since this decorator is used in core parts
4 numpy and sympy if they're present. Since this decorator is used in core parts
5 of IPython, it's in a separate module so that running IPython doesn't trigger
5 of IPython, it's in a separate module so that running IPython doesn't trigger
6 those imports."""
6 those imports."""
7 import sys
7
8
8 def skip_doctest(f):
9 def skip_doctest(f):
9 """Decorator - mark a function or method for skipping its doctest.
10 """Decorator - mark a function or method for skipping its doctest.
10
11
11 This decorator allows you to mark a function whose docstring you wish to
12 This decorator allows you to mark a function whose docstring you wish to
12 omit from testing, while preserving the docstring for introspection, help,
13 omit from testing, while preserving the docstring for introspection, help,
13 etc."""
14 etc."""
14 f.skip_doctest = True
15 f.skip_doctest = True
15 return f
16 return f
17
18 def skip_doctest_py3(f):
19 """Decorator - skip the doctest under Python 3."""
20 f.skip_doctest = (sys.version_info[0] >= 3)
21 return f
General Comments 0
You need to be logged in to leave comments. Login now