##// END OF EJS Templates
Use safe_hasattr in dir2...
Jeffrey Tratner -
Show More
@@ -1,882 +1,873 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 from __future__ import print_function
17 17
18 18 __all__ = ['Inspector','InspectColors']
19 19
20 20 # stdlib modules
21 21 import inspect
22 22 import linecache
23 23 import os
24 24 import types
25 25 import io as stdlib_io
26 26
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 34 from IPython.testing.skipdoctest import skip_doctest_py3
35 35 from IPython.utils import PyColorize
36 36 from IPython.utils import io
37 37 from IPython.utils import openpy
38 38 from IPython.utils import py3compat
39 from IPython.utils.dir2 import safe_hasattr
39 40 from IPython.utils.text import indent
40 41 from IPython.utils.wildcard import list_namespace
41 42 from IPython.utils.coloransi import *
42 43 from IPython.utils.py3compat import cast_unicode
43 44
44 45 #****************************************************************************
45 46 # Builtin color schemes
46 47
47 48 Colors = TermColors # just a shorthand
48 49
49 50 # Build a few color schemes
50 51 NoColor = ColorScheme(
51 52 'NoColor',{
52 53 'header' : Colors.NoColor,
53 54 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
54 55 } )
55 56
56 57 LinuxColors = ColorScheme(
57 58 'Linux',{
58 59 'header' : Colors.LightRed,
59 60 'normal' : Colors.Normal # color off (usu. Colors.Normal)
60 61 } )
61 62
62 63 LightBGColors = ColorScheme(
63 64 'LightBG',{
64 65 'header' : Colors.Red,
65 66 'normal' : Colors.Normal # color off (usu. Colors.Normal)
66 67 } )
67 68
68 69 # Build table of color schemes (needed by the parser)
69 70 InspectColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
70 71 'Linux')
71 72
72 73 #****************************************************************************
73 74 # Auxiliary functions and objects
74 75
75 76 # See the messaging spec for the definition of all these fields. This list
76 77 # effectively defines the order of display
77 78 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
78 79 'length', 'file', 'definition', 'docstring', 'source',
79 80 'init_definition', 'class_docstring', 'init_docstring',
80 81 'call_def', 'call_docstring',
81 82 # These won't be printed but will be used to determine how to
82 83 # format the object
83 84 'ismagic', 'isalias', 'isclass', 'argspec', 'found', 'name'
84 85 ]
85 86
86 87
87 88 def object_info(**kw):
88 89 """Make an object info dict with all fields present."""
89 90 infodict = dict(izip_longest(info_fields, [None]))
90 91 infodict.update(kw)
91 92 return infodict
92 93
93 94
94 95 def get_encoding(obj):
95 96 """Get encoding for python source file defining obj
96 97
97 98 Returns None if obj is not defined in a sourcefile.
98 99 """
99 100 ofile = find_file(obj)
100 101 # run contents of file through pager starting at line where the object
101 102 # is defined, as long as the file isn't binary and is actually on the
102 103 # filesystem.
103 104 if ofile is None:
104 105 return None
105 106 elif ofile.endswith(('.so', '.dll', '.pyd')):
106 107 return None
107 108 elif not os.path.isfile(ofile):
108 109 return None
109 110 else:
110 111 # Print only text files, not extension binaries. Note that
111 112 # getsourcelines returns lineno with 1-offset and page() uses
112 113 # 0-offset, so we must adjust.
113 114 buffer = stdlib_io.open(ofile, 'rb') # Tweaked to use io.open for Python 2
114 115 encoding, lines = openpy.detect_encoding(buffer.readline)
115 116 return encoding
116 117
117 118 def getdoc(obj):
118 119 """Stable wrapper around inspect.getdoc.
119 120
120 121 This can't crash because of attribute problems.
121 122
122 123 It also attempts to call a getdoc() method on the given object. This
123 124 allows objects which provide their docstrings via non-standard mechanisms
124 125 (like Pyro proxies) to still be inspected by ipython's ? system."""
125 126 # Allow objects to offer customized documentation via a getdoc method:
126 127 try:
127 128 ds = obj.getdoc()
128 129 except Exception:
129 130 pass
130 131 else:
131 132 # if we get extra info, we add it to the normal docstring.
132 133 if isinstance(ds, basestring):
133 134 return inspect.cleandoc(ds)
134 135
135 136 try:
136 137 docstr = inspect.getdoc(obj)
137 138 encoding = get_encoding(obj)
138 139 return py3compat.cast_unicode(docstr, encoding=encoding)
139 140 except Exception:
140 141 # Harden against an inspect failure, which can occur with
141 142 # SWIG-wrapped extensions.
142 143 raise
143 144 return None
144 145
145 146
146 147 def getsource(obj,is_binary=False):
147 148 """Wrapper around inspect.getsource.
148 149
149 150 This can be modified by other projects to provide customized source
150 151 extraction.
151 152
152 153 Inputs:
153 154
154 155 - obj: an object whose source code we will attempt to extract.
155 156
156 157 Optional inputs:
157 158
158 159 - is_binary: whether the object is known to come from a binary source.
159 160 This implementation will skip returning any output for binary objects, but
160 161 custom extractors may know how to meaningfully process them."""
161 162
162 163 if is_binary:
163 164 return None
164 165 else:
165 166 # get source if obj was decorated with @decorator
166 167 if hasattr(obj,"__wrapped__"):
167 168 obj = obj.__wrapped__
168 169 try:
169 170 src = inspect.getsource(obj)
170 171 except TypeError:
171 172 if hasattr(obj,'__class__'):
172 173 src = inspect.getsource(obj.__class__)
173 174 encoding = get_encoding(obj)
174 175 return cast_unicode(src, encoding=encoding)
175 176
176 177 def getargspec(obj):
177 178 """Get the names and default values of a function's arguments.
178 179
179 180 A tuple of four things is returned: (args, varargs, varkw, defaults).
180 181 'args' is a list of the argument names (it may contain nested lists).
181 182 'varargs' and 'varkw' are the names of the * and ** arguments or None.
182 183 'defaults' is an n-tuple of the default values of the last n arguments.
183 184
184 185 Modified version of inspect.getargspec from the Python Standard
185 186 Library."""
186 187
187 188 if inspect.isfunction(obj):
188 189 func_obj = obj
189 190 elif inspect.ismethod(obj):
190 191 func_obj = obj.im_func
191 192 elif hasattr(obj, '__call__'):
192 193 func_obj = obj.__call__
193 194 else:
194 195 raise TypeError('arg is not a Python function')
195 196 args, varargs, varkw = inspect.getargs(func_obj.func_code)
196 197 return args, varargs, varkw, func_obj.func_defaults
197 198
198 199
199 200 def format_argspec(argspec):
200 201 """Format argspect, convenience wrapper around inspect's.
201 202
202 203 This takes a dict instead of ordered arguments and calls
203 204 inspect.format_argspec with the arguments in the necessary order.
204 205 """
205 206 return inspect.formatargspec(argspec['args'], argspec['varargs'],
206 207 argspec['varkw'], argspec['defaults'])
207 208
208 209
209 210 def call_tip(oinfo, format_call=True):
210 211 """Extract call tip data from an oinfo dict.
211 212
212 213 Parameters
213 214 ----------
214 215 oinfo : dict
215 216
216 217 format_call : bool, optional
217 218 If True, the call line is formatted and returned as a string. If not, a
218 219 tuple of (name, argspec) is returned.
219 220
220 221 Returns
221 222 -------
222 223 call_info : None, str or (str, dict) tuple.
223 224 When format_call is True, the whole call information is formattted as a
224 225 single string. Otherwise, the object's name and its argspec dict are
225 226 returned. If no call information is available, None is returned.
226 227
227 228 docstring : str or None
228 229 The most relevant docstring for calling purposes is returned, if
229 230 available. The priority is: call docstring for callable instances, then
230 231 constructor docstring for classes, then main object's docstring otherwise
231 232 (regular functions).
232 233 """
233 234 # Get call definition
234 235 argspec = oinfo.get('argspec')
235 236 if argspec is None:
236 237 call_line = None
237 238 else:
238 239 # Callable objects will have 'self' as their first argument, prune
239 240 # it out if it's there for clarity (since users do *not* pass an
240 241 # extra first argument explicitly).
241 242 try:
242 243 has_self = argspec['args'][0] == 'self'
243 244 except (KeyError, IndexError):
244 245 pass
245 246 else:
246 247 if has_self:
247 248 argspec['args'] = argspec['args'][1:]
248 249
249 250 call_line = oinfo['name']+format_argspec(argspec)
250 251
251 252 # Now get docstring.
252 253 # The priority is: call docstring, constructor docstring, main one.
253 254 doc = oinfo.get('call_docstring')
254 255 if doc is None:
255 256 doc = oinfo.get('init_docstring')
256 257 if doc is None:
257 258 doc = oinfo.get('docstring','')
258 259
259 260 return call_line, doc
260 261
261 def safe_hasattr(obj, attr):
262 """In recent versions of Python, hasattr() only catches AttributeError.
263 This catches all errors.
264 """
265 try:
266 getattr(obj, attr)
267 return True
268 except:
269 return False
270
271 262
272 263 def find_file(obj):
273 264 """Find the absolute path to the file where an object was defined.
274 265
275 266 This is essentially a robust wrapper around `inspect.getabsfile`.
276 267
277 268 Returns None if no file can be found.
278 269
279 270 Parameters
280 271 ----------
281 272 obj : any Python object
282 273
283 274 Returns
284 275 -------
285 276 fname : str
286 277 The absolute path to the file where the object was defined.
287 278 """
288 279 # get source if obj was decorated with @decorator
289 280 if safe_hasattr(obj, '__wrapped__'):
290 281 obj = obj.__wrapped__
291 282
292 283 fname = None
293 284 try:
294 285 fname = inspect.getabsfile(obj)
295 286 except TypeError:
296 287 # For an instance, the file that matters is where its class was
297 288 # declared.
298 289 if hasattr(obj, '__class__'):
299 290 try:
300 291 fname = inspect.getabsfile(obj.__class__)
301 292 except TypeError:
302 293 # Can happen for builtins
303 294 pass
304 295 except:
305 296 pass
306 297 return cast_unicode(fname)
307 298
308 299
309 300 def find_source_lines(obj):
310 301 """Find the line number in a file where an object was defined.
311 302
312 303 This is essentially a robust wrapper around `inspect.getsourcelines`.
313 304
314 305 Returns None if no file can be found.
315 306
316 307 Parameters
317 308 ----------
318 309 obj : any Python object
319 310
320 311 Returns
321 312 -------
322 313 lineno : int
323 314 The line number where the object definition starts.
324 315 """
325 316 # get source if obj was decorated with @decorator
326 317 if safe_hasattr(obj, '__wrapped__'):
327 318 obj = obj.__wrapped__
328 319
329 320 try:
330 321 try:
331 322 lineno = inspect.getsourcelines(obj)[1]
332 323 except TypeError:
333 324 # For instances, try the class object like getsource() does
334 325 if hasattr(obj, '__class__'):
335 326 lineno = inspect.getsourcelines(obj.__class__)[1]
336 327 else:
337 328 lineno = None
338 329 except:
339 330 return None
340 331
341 332 return lineno
342 333
343 334
344 335 class Inspector:
345 336 def __init__(self, color_table=InspectColors,
346 337 code_color_table=PyColorize.ANSICodeColors,
347 338 scheme='NoColor',
348 339 str_detail_level=0):
349 340 self.color_table = color_table
350 341 self.parser = PyColorize.Parser(code_color_table,out='str')
351 342 self.format = self.parser.format
352 343 self.str_detail_level = str_detail_level
353 344 self.set_active_scheme(scheme)
354 345
355 346 def _getdef(self,obj,oname=''):
356 347 """Return the call signature for any callable object.
357 348
358 349 If any exception is generated, None is returned instead and the
359 350 exception is suppressed."""
360 351
361 352 try:
362 353 hdef = oname + inspect.formatargspec(*getargspec(obj))
363 354 return cast_unicode(hdef)
364 355 except:
365 356 return None
366 357
367 358 def __head(self,h):
368 359 """Return a header string with proper colors."""
369 360 return '%s%s%s' % (self.color_table.active_colors.header,h,
370 361 self.color_table.active_colors.normal)
371 362
372 363 def set_active_scheme(self, scheme):
373 364 self.color_table.set_active_scheme(scheme)
374 365 self.parser.color_table.set_active_scheme(scheme)
375 366
376 367 def noinfo(self, msg, oname):
377 368 """Generic message when no information is found."""
378 369 print('No %s found' % msg, end=' ')
379 370 if oname:
380 371 print('for %s' % oname)
381 372 else:
382 373 print()
383 374
384 375 def pdef(self, obj, oname=''):
385 376 """Print the call signature for any callable object.
386 377
387 378 If the object is a class, print the constructor information."""
388 379
389 380 if not callable(obj):
390 381 print('Object is not callable.')
391 382 return
392 383
393 384 header = ''
394 385
395 386 if inspect.isclass(obj):
396 387 header = self.__head('Class constructor information:\n')
397 388 obj = obj.__init__
398 389 elif (not py3compat.PY3) and type(obj) is types.InstanceType:
399 390 obj = obj.__call__
400 391
401 392 output = self._getdef(obj,oname)
402 393 if output is None:
403 394 self.noinfo('definition header',oname)
404 395 else:
405 396 print(header,self.format(output), end=' ', file=io.stdout)
406 397
407 398 # In Python 3, all classes are new-style, so they all have __init__.
408 399 @skip_doctest_py3
409 400 def pdoc(self,obj,oname='',formatter = None):
410 401 """Print the docstring for any object.
411 402
412 403 Optional:
413 404 -formatter: a function to run the docstring through for specially
414 405 formatted docstrings.
415 406
416 407 Examples
417 408 --------
418 409
419 410 In [1]: class NoInit:
420 411 ...: pass
421 412
422 413 In [2]: class NoDoc:
423 414 ...: def __init__(self):
424 415 ...: pass
425 416
426 417 In [3]: %pdoc NoDoc
427 418 No documentation found for NoDoc
428 419
429 420 In [4]: %pdoc NoInit
430 421 No documentation found for NoInit
431 422
432 423 In [5]: obj = NoInit()
433 424
434 425 In [6]: %pdoc obj
435 426 No documentation found for obj
436 427
437 428 In [5]: obj2 = NoDoc()
438 429
439 430 In [6]: %pdoc obj2
440 431 No documentation found for obj2
441 432 """
442 433
443 434 head = self.__head # For convenience
444 435 lines = []
445 436 ds = getdoc(obj)
446 437 if formatter:
447 438 ds = formatter(ds)
448 439 if ds:
449 440 lines.append(head("Class Docstring:"))
450 441 lines.append(indent(ds))
451 442 if inspect.isclass(obj) and hasattr(obj, '__init__'):
452 443 init_ds = getdoc(obj.__init__)
453 444 if init_ds is not None:
454 445 lines.append(head("Constructor Docstring:"))
455 446 lines.append(indent(init_ds))
456 447 elif hasattr(obj,'__call__'):
457 448 call_ds = getdoc(obj.__call__)
458 449 if call_ds:
459 450 lines.append(head("Calling Docstring:"))
460 451 lines.append(indent(call_ds))
461 452
462 453 if not lines:
463 454 self.noinfo('documentation',oname)
464 455 else:
465 456 page.page('\n'.join(lines))
466 457
467 458 def psource(self,obj,oname=''):
468 459 """Print the source code for an object."""
469 460
470 461 # Flush the source cache because inspect can return out-of-date source
471 462 linecache.checkcache()
472 463 try:
473 464 src = getsource(obj)
474 465 except:
475 466 self.noinfo('source',oname)
476 467 else:
477 468 page.page(self.format(src))
478 469
479 470 def pfile(self, obj, oname=''):
480 471 """Show the whole file where an object was defined."""
481 472
482 473 lineno = find_source_lines(obj)
483 474 if lineno is None:
484 475 self.noinfo('file', oname)
485 476 return
486 477
487 478 ofile = find_file(obj)
488 479 # run contents of file through pager starting at line where the object
489 480 # is defined, as long as the file isn't binary and is actually on the
490 481 # filesystem.
491 482 if ofile.endswith(('.so', '.dll', '.pyd')):
492 483 print('File %r is binary, not printing.' % ofile)
493 484 elif not os.path.isfile(ofile):
494 485 print('File %r does not exist, not printing.' % ofile)
495 486 else:
496 487 # Print only text files, not extension binaries. Note that
497 488 # getsourcelines returns lineno with 1-offset and page() uses
498 489 # 0-offset, so we must adjust.
499 490 page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1)
500 491
501 492 def _format_fields(self, fields, title_width=12):
502 493 """Formats a list of fields for display.
503 494
504 495 Parameters
505 496 ----------
506 497 fields : list
507 498 A list of 2-tuples: (field_title, field_content)
508 499 title_width : int
509 500 How many characters to pad titles to. Default 12.
510 501 """
511 502 out = []
512 503 header = self.__head
513 504 for title, content in fields:
514 505 if len(content.splitlines()) > 1:
515 506 title = header(title + ":") + "\n"
516 507 else:
517 508 title = header((title+":").ljust(title_width))
518 509 out.append(cast_unicode(title) + cast_unicode(content))
519 510 return "\n".join(out)
520 511
521 512 # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict)
522 513 pinfo_fields1 = [("Type", "type_name"),
523 514 ]
524 515
525 516 pinfo_fields2 = [("String Form", "string_form"),
526 517 ]
527 518
528 519 pinfo_fields3 = [("Length", "length"),
529 520 ("File", "file"),
530 521 ("Definition", "definition"),
531 522 ]
532 523
533 524 pinfo_fields_obj = [("Class Docstring", "class_docstring"),
534 525 ("Constructor Docstring","init_docstring"),
535 526 ("Call def", "call_def"),
536 527 ("Call docstring", "call_docstring")]
537 528
538 529 def pinfo(self,obj,oname='',formatter=None,info=None,detail_level=0):
539 530 """Show detailed information about an object.
540 531
541 532 Optional arguments:
542 533
543 534 - oname: name of the variable pointing to the object.
544 535
545 536 - formatter: special formatter for docstrings (see pdoc)
546 537
547 538 - info: a structure with some information fields which may have been
548 539 precomputed already.
549 540
550 541 - detail_level: if set to 1, more information is given.
551 542 """
552 543 info = self.info(obj, oname=oname, formatter=formatter,
553 544 info=info, detail_level=detail_level)
554 545 displayfields = []
555 546 def add_fields(fields):
556 547 for title, key in fields:
557 548 field = info[key]
558 549 if field is not None:
559 550 displayfields.append((title, field.rstrip()))
560 551
561 552 add_fields(self.pinfo_fields1)
562 553
563 554 # Base class for old-style instances
564 555 if (not py3compat.PY3) and isinstance(obj, types.InstanceType) and info['base_class']:
565 556 displayfields.append(("Base Class", info['base_class'].rstrip()))
566 557
567 558 add_fields(self.pinfo_fields2)
568 559
569 560 # Namespace
570 561 if info['namespace'] != 'Interactive':
571 562 displayfields.append(("Namespace", info['namespace'].rstrip()))
572 563
573 564 add_fields(self.pinfo_fields3)
574 565
575 566 # Source or docstring, depending on detail level and whether
576 567 # source found.
577 568 if detail_level > 0 and info['source'] is not None:
578 569 displayfields.append(("Source",
579 570 self.format(cast_unicode(info['source']))))
580 571 elif info['docstring'] is not None:
581 572 displayfields.append(("Docstring", info["docstring"]))
582 573
583 574 # Constructor info for classes
584 575 if info['isclass']:
585 576 if info['init_definition'] or info['init_docstring']:
586 577 displayfields.append(("Constructor information", ""))
587 578 if info['init_definition'] is not None:
588 579 displayfields.append((" Definition",
589 580 info['init_definition'].rstrip()))
590 581 if info['init_docstring'] is not None:
591 582 displayfields.append((" Docstring",
592 583 indent(info['init_docstring'])))
593 584
594 585 # Info for objects:
595 586 else:
596 587 add_fields(self.pinfo_fields_obj)
597 588
598 589 # Finally send to printer/pager:
599 590 if displayfields:
600 591 page.page(self._format_fields(displayfields))
601 592
602 593 def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
603 594 """Compute a dict with detailed information about an object.
604 595
605 596 Optional arguments:
606 597
607 598 - oname: name of the variable pointing to the object.
608 599
609 600 - formatter: special formatter for docstrings (see pdoc)
610 601
611 602 - info: a structure with some information fields which may have been
612 603 precomputed already.
613 604
614 605 - detail_level: if set to 1, more information is given.
615 606 """
616 607
617 608 obj_type = type(obj)
618 609
619 610 header = self.__head
620 611 if info is None:
621 612 ismagic = 0
622 613 isalias = 0
623 614 ospace = ''
624 615 else:
625 616 ismagic = info.ismagic
626 617 isalias = info.isalias
627 618 ospace = info.namespace
628 619
629 620 # Get docstring, special-casing aliases:
630 621 if isalias:
631 622 if not callable(obj):
632 623 try:
633 624 ds = "Alias to the system command:\n %s" % obj[1]
634 625 except:
635 626 ds = "Alias: " + str(obj)
636 627 else:
637 628 ds = "Alias to " + str(obj)
638 629 if obj.__doc__:
639 630 ds += "\nDocstring:\n" + obj.__doc__
640 631 else:
641 632 ds = getdoc(obj)
642 633 if ds is None:
643 634 ds = '<no docstring>'
644 635 if formatter is not None:
645 636 ds = formatter(ds)
646 637
647 638 # store output in a dict, we initialize it here and fill it as we go
648 639 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
649 640
650 641 string_max = 200 # max size of strings to show (snipped if longer)
651 642 shalf = int((string_max -5)/2)
652 643
653 644 if ismagic:
654 645 obj_type_name = 'Magic function'
655 646 elif isalias:
656 647 obj_type_name = 'System alias'
657 648 else:
658 649 obj_type_name = obj_type.__name__
659 650 out['type_name'] = obj_type_name
660 651
661 652 try:
662 653 bclass = obj.__class__
663 654 out['base_class'] = str(bclass)
664 655 except: pass
665 656
666 657 # String form, but snip if too long in ? form (full in ??)
667 658 if detail_level >= self.str_detail_level:
668 659 try:
669 660 ostr = str(obj)
670 661 str_head = 'string_form'
671 662 if not detail_level and len(ostr)>string_max:
672 663 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
673 664 ostr = ("\n" + " " * len(str_head.expandtabs())).\
674 665 join(q.strip() for q in ostr.split("\n"))
675 666 out[str_head] = ostr
676 667 except:
677 668 pass
678 669
679 670 if ospace:
680 671 out['namespace'] = ospace
681 672
682 673 # Length (for strings and lists)
683 674 try:
684 675 out['length'] = str(len(obj))
685 676 except: pass
686 677
687 678 # Filename where object was defined
688 679 binary_file = False
689 680 fname = find_file(obj)
690 681 if fname is None:
691 682 # if anything goes wrong, we don't want to show source, so it's as
692 683 # if the file was binary
693 684 binary_file = True
694 685 else:
695 686 if fname.endswith(('.so', '.dll', '.pyd')):
696 687 binary_file = True
697 688 elif fname.endswith('<string>'):
698 689 fname = 'Dynamically generated function. No source code available.'
699 690 out['file'] = fname
700 691
701 692 # reconstruct the function definition and print it:
702 693 defln = self._getdef(obj, oname)
703 694 if defln:
704 695 out['definition'] = self.format(defln)
705 696
706 697 # Docstrings only in detail 0 mode, since source contains them (we
707 698 # avoid repetitions). If source fails, we add them back, see below.
708 699 if ds and detail_level == 0:
709 700 out['docstring'] = ds
710 701
711 702 # Original source code for any callable
712 703 if detail_level:
713 704 # Flush the source cache because inspect can return out-of-date
714 705 # source
715 706 linecache.checkcache()
716 707 source = None
717 708 try:
718 709 try:
719 710 source = getsource(obj, binary_file)
720 711 except TypeError:
721 712 if hasattr(obj, '__class__'):
722 713 source = getsource(obj.__class__, binary_file)
723 714 if source is not None:
724 715 out['source'] = source.rstrip()
725 716 except Exception:
726 717 pass
727 718
728 719 if ds and source is None:
729 720 out['docstring'] = ds
730 721
731 722
732 723 # Constructor docstring for classes
733 724 if inspect.isclass(obj):
734 725 out['isclass'] = True
735 726 # reconstruct the function definition and print it:
736 727 try:
737 728 obj_init = obj.__init__
738 729 except AttributeError:
739 730 init_def = init_ds = None
740 731 else:
741 732 init_def = self._getdef(obj_init,oname)
742 733 init_ds = getdoc(obj_init)
743 734 # Skip Python's auto-generated docstrings
744 735 if init_ds and \
745 736 init_ds.startswith('x.__init__(...) initializes'):
746 737 init_ds = None
747 738
748 739 if init_def or init_ds:
749 740 if init_def:
750 741 out['init_definition'] = self.format(init_def)
751 742 if init_ds:
752 743 out['init_docstring'] = init_ds
753 744
754 745 # and class docstring for instances:
755 746 else:
756 747 # First, check whether the instance docstring is identical to the
757 748 # class one, and print it separately if they don't coincide. In
758 749 # most cases they will, but it's nice to print all the info for
759 750 # objects which use instance-customized docstrings.
760 751 if ds:
761 752 try:
762 753 cls = getattr(obj,'__class__')
763 754 except:
764 755 class_ds = None
765 756 else:
766 757 class_ds = getdoc(cls)
767 758 # Skip Python's auto-generated docstrings
768 759 if class_ds and \
769 760 (class_ds.startswith('function(code, globals[,') or \
770 761 class_ds.startswith('instancemethod(function, instance,') or \
771 762 class_ds.startswith('module(name[,') ):
772 763 class_ds = None
773 764 if class_ds and ds != class_ds:
774 765 out['class_docstring'] = class_ds
775 766
776 767 # Next, try to show constructor docstrings
777 768 try:
778 769 init_ds = getdoc(obj.__init__)
779 770 # Skip Python's auto-generated docstrings
780 771 if init_ds and \
781 772 init_ds.startswith('x.__init__(...) initializes'):
782 773 init_ds = None
783 774 except AttributeError:
784 775 init_ds = None
785 776 if init_ds:
786 777 out['init_docstring'] = init_ds
787 778
788 779 # Call form docstring for callable instances
789 780 if safe_hasattr(obj, '__call__'):
790 781 call_def = self._getdef(obj.__call__, oname)
791 782 if call_def is not None:
792 783 out['call_def'] = self.format(call_def)
793 784 call_ds = getdoc(obj.__call__)
794 785 # Skip Python's auto-generated docstrings
795 786 if call_ds and call_ds.startswith('x.__call__(...) <==> x(...)'):
796 787 call_ds = None
797 788 if call_ds:
798 789 out['call_docstring'] = call_ds
799 790
800 791 # Compute the object's argspec as a callable. The key is to decide
801 792 # whether to pull it from the object itself, from its __init__ or
802 793 # from its __call__ method.
803 794
804 795 if inspect.isclass(obj):
805 796 # Old-style classes need not have an __init__
806 797 callable_obj = getattr(obj, "__init__", None)
807 798 elif callable(obj):
808 799 callable_obj = obj
809 800 else:
810 801 callable_obj = None
811 802
812 803 if callable_obj:
813 804 try:
814 805 args, varargs, varkw, defaults = getargspec(callable_obj)
815 806 except (TypeError, AttributeError):
816 807 # For extensions/builtins we can't retrieve the argspec
817 808 pass
818 809 else:
819 810 out['argspec'] = dict(args=args, varargs=varargs,
820 811 varkw=varkw, defaults=defaults)
821 812
822 813 return object_info(**out)
823 814
824 815
825 816 def psearch(self,pattern,ns_table,ns_search=[],
826 817 ignore_case=False,show_all=False):
827 818 """Search namespaces with wildcards for objects.
828 819
829 820 Arguments:
830 821
831 822 - pattern: string containing shell-like wildcards to use in namespace
832 823 searches and optionally a type specification to narrow the search to
833 824 objects of that type.
834 825
835 826 - ns_table: dict of name->namespaces for search.
836 827
837 828 Optional arguments:
838 829
839 830 - ns_search: list of namespace names to include in search.
840 831
841 832 - ignore_case(False): make the search case-insensitive.
842 833
843 834 - show_all(False): show all names, including those starting with
844 835 underscores.
845 836 """
846 837 #print 'ps pattern:<%r>' % pattern # dbg
847 838
848 839 # defaults
849 840 type_pattern = 'all'
850 841 filter = ''
851 842
852 843 cmds = pattern.split()
853 844 len_cmds = len(cmds)
854 845 if len_cmds == 1:
855 846 # Only filter pattern given
856 847 filter = cmds[0]
857 848 elif len_cmds == 2:
858 849 # Both filter and type specified
859 850 filter,type_pattern = cmds
860 851 else:
861 852 raise ValueError('invalid argument string for psearch: <%s>' %
862 853 pattern)
863 854
864 855 # filter search namespaces
865 856 for name in ns_search:
866 857 if name not in ns_table:
867 858 raise ValueError('invalid namespace <%s>. Valid names: %s' %
868 859 (name,ns_table.keys()))
869 860
870 861 #print 'type_pattern:',type_pattern # dbg
871 862 search_result, namespaces_seen = set(), set()
872 863 for ns_name in ns_search:
873 864 ns = ns_table[ns_name]
874 865 # Normally, locals and globals are the same, so we just check one.
875 866 if id(ns) in namespaces_seen:
876 867 continue
877 868 namespaces_seen.add(id(ns))
878 869 tmp_res = list_namespace(ns, type_pattern, filter,
879 870 ignore_case=ignore_case, show_all=show_all)
880 871 search_result.update(tmp_res)
881 872
882 873 page.page('\n'.join(sorted(search_result)))
@@ -1,73 +1,83 b''
1 1 # encoding: utf-8
2 2 """A fancy version of Python's builtin :func:`dir` function.
3 3 """
4 4
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (C) 2008-2011 The IPython Development Team
7 7 #
8 8 # Distributed under the terms of the BSD License. The full license is in
9 9 # the file COPYING, distributed as part of this software.
10 10 #-----------------------------------------------------------------------------
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Imports
14 14 #-----------------------------------------------------------------------------
15
16 15 #-----------------------------------------------------------------------------
17 16 # Code
18 17 #-----------------------------------------------------------------------------
19 18
19
20 def safe_hasattr(obj, attr):
21 """In recent versions of Python, hasattr() only catches AttributeError.
22 This catches all errors.
23 """
24 try:
25 getattr(obj, attr)
26 return True
27 except:
28 return False
29
30
20 31 def get_class_members(cls):
21 32 ret = dir(cls)
22 if hasattr(cls, '__bases__'):
33 if safe_hasattr(cls, '__bases__'):
23 34 try:
24 35 bases = cls.__bases__
25 36 except AttributeError:
26 37 # `obj` lied to hasattr (e.g. Pyro), ignore
27 38 pass
28 39 else:
29 40 for base in bases:
30 41 ret.extend(get_class_members(base))
31 42 return ret
32 43
33 44
34 45 def dir2(obj):
35 46 """dir2(obj) -> list of strings
36 47
37 48 Extended version of the Python builtin dir(), which does a few extra
38 49 checks, and supports common objects with unusual internals that confuse
39 50 dir(), such as Traits and PyCrust.
40 51
41 52 This version is guaranteed to return only a list of true strings, whereas
42 53 dir() returns anything that objects inject into themselves, even if they
43 54 are later not really valid for attribute access (many extension libraries
44 55 have such bugs).
45 56 """
46 57
47 58 # Start building the attribute list via dir(), and then complete it
48 59 # with a few extra special-purpose calls.
49 60
50 61 words = set(dir(obj))
51 62
52 if hasattr(obj, '__class__'):
63 if safe_hasattr(obj, '__class__'):
53 64 #words.add('__class__')
54 65 words |= set(get_class_members(obj.__class__))
55 66
56 67
57 68 # for objects with Enthought's traits, add trait_names() list
58 69 # for PyCrust-style, add _getAttributeNames() magic method list
59 70 for attr in ('trait_names', '_getAttributeNames'):
60 if hasattr(obj, attr):
61 try:
62 func = getattr(obj, attr)
63 if callable(func):
64 words |= set(func())
65 except:
66 # TypeError: obj is class not instance
67 pass
71 try:
72 func = getattr(obj, attr)
73 if callable(func):
74 words |= set(func())
75 except:
76 # TypeError: obj is class not instance
77 pass
68 78
69 79 # filter out non-string attributes which may be stuffed by dir() calls
70 80 # and poor coding in third-party modules
71 81
72 82 words = [w for w in words if isinstance(w, basestring)]
73 83 return sorted(words)
@@ -1,52 +1,80 b''
1 1 import nose.tools as nt
2 2 from IPython.utils.dir2 import dir2
3 3
4 4
5 5 class Base(object):
6 6 x = 1
7 7 z = 23
8 8
9 9
10 10 def test_base():
11 11 res = dir2(Base())
12 12 assert ('x' in res)
13 13 assert ('z' in res)
14 14 assert ('y' not in res)
15 15 assert ('__class__' in res)
16 16 nt.assert_equal(res.count('x'), 1)
17 17 nt.assert_equal(res.count('__class__'), 1)
18 18
19 19 def test_SubClass():
20 20
21 21 class SubClass(Base):
22 22 y = 2
23 23
24 24 res = dir2(SubClass())
25 25 assert ('y' in res)
26 26 nt.assert_equal(res.count('y'), 1)
27 27 nt.assert_equal(res.count('x'), 1)
28 28
29 29
30 30 def test_SubClass_with_trait_names_method():
31 31
32 32 class SubClass(Base):
33 33 y = 2
34 34 def trait_names(self):
35 35 return ['t', 'umbrella']
36 36
37 37 res = dir2(SubClass())
38 38 assert('trait_names' in res)
39 39 assert('umbrella' in res)
40 40 nt.assert_equal(res[-6:], ['t', 'trait_names','umbrella', 'x','y','z'])
41 41 nt.assert_equal(res.count('t'), 1)
42 42
43 43
44 44 def test_SubClass_with_trait_names_attr():
45 45 # usecase: trait_names is used in a class describing psychological classification
46 46
47 47 class SubClass(Base):
48 48 y = 2
49 49 trait_names = 44
50 50
51 51 res = dir2(SubClass())
52 52 assert('trait_names' in res)
53
54
55 def test_misbehaving_object_without_trait_names():
56 # dir2 shouldn't raise even when objects are dumb and raise
57 # something other than AttribteErrors on bad getattr.
58
59 class BadTraitNames(object):
60 @property
61 def trait_names(self):
62 raise KeyboardInterrupt("This should be caught")
63
64 def some_method(self):
65 pass
66
67 class MisbehavingGetattr(object):
68 def __getattr__(self):
69 raise KeyError("I should be caught")
70
71 def some_method(self):
72 pass
73
74 class SillierWithDir(MisbehavingGetattr):
75 def __dir__(self):
76 return ['some_method']
77
78 for bad_klass in (BadTraitNames, MisbehavingGetattr, SillierWithDir):
79 res = dir2(bad_klass())
80 assert('some_method' in res)
General Comments 0
You need to be logged in to leave comments. Login now