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