##// END OF EJS Templates
Merge branch 'oinspect' into takluyver-oinspect
Thomas Kluyver -
r3863:065ce78f merge
parent child Browse files
Show More
@@ -18,7 +18,6 b" __all__ = ['Inspector','InspectColors']"
18
18
19 # stdlib modules
19 # stdlib modules
20 import __builtin__
20 import __builtin__
21 import StringIO
22 import inspect
21 import inspect
23 import linecache
22 import linecache
24 import os
23 import os
@@ -75,7 +74,7 b" info_fields = ['type_name', 'base_class', 'string_form', 'namespace',"
75 'call_def', 'call_docstring',
74 'call_def', 'call_docstring',
76 # These won't be printed but will be used to determine how to
75 # These won't be printed but will be used to determine how to
77 # format the object
76 # format the object
78 'ismagic', 'isalias', 'argspec', 'found', 'name',
77 'ismagic', 'isalias', 'isclass', 'argspec', 'found', 'name'
79 ]
78 ]
80
79
81
80
@@ -227,16 +226,6 b' def call_tip(oinfo, format_call=True):'
227
226
228 return call_line, doc
227 return call_line, doc
229
228
230 #****************************************************************************
231 # Class definitions
232
233 class myStringIO(StringIO.StringIO):
234 """Adds a writeln method to normal StringIO."""
235 def writeln(self,*arg,**kw):
236 """Does a write() and then a write('\n')"""
237 self.write(*arg,**kw)
238 self.write('\n')
239
240
229
241 class Inspector:
230 class Inspector:
242 def __init__(self, color_table=InspectColors,
231 def __init__(self, color_table=InspectColors,
@@ -378,6 +367,40 b' class Inspector:'
378 # 0-offset, so we must adjust.
367 # 0-offset, so we must adjust.
379 page.page(self.format(open(ofile).read()),lineno-1)
368 page.page(self.format(open(ofile).read()),lineno-1)
380
369
370 def _format_fields(self, fields, title_width=12):
371 """Formats a list of fields for display.
372
373 Parameters
374 ----------
375 fields : list
376 A list of 2-tuples: (field_title, field_content)
377 title_width : int
378 How many characters to pad titles to. Default 12.
379 """
380 out = []
381 header = self.__head
382 for title, content in fields:
383 if len(content.splitlines()) > 1:
384 title = header(title + ":") + "\n"
385 else:
386 title = header((title+":").ljust(title_width))
387 out.append(title + content)
388 return "\n".join(out)
389
390 # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict)
391 pinfo_fields1 = [("Type", "type_name"),
392 ("Base Class", "base_class"),
393 ("String Form", "string_form"),
394 ("Namespace", "namespace"),
395 ("Length", "length"),
396 ("File", "file"),
397 ("Definition", "definition")]
398
399 pinfo_fields_obj = [("Class Docstring", "class_docstring"),
400 ("Constructor Docstring","init_docstring"),
401 ("Call def", "call_def"),
402 ("Call docstring", "call_docstring")]
403
381 def pinfo(self,obj,oname='',formatter=None,info=None,detail_level=0):
404 def pinfo(self,obj,oname='',formatter=None,info=None,detail_level=0):
382 """Show detailed information about an object.
405 """Show detailed information about an object.
383
406
@@ -392,215 +415,42 b' class Inspector:'
392
415
393 - detail_level: if set to 1, more information is given.
416 - detail_level: if set to 1, more information is given.
394 """
417 """
395
418 info = self.info(obj, oname=oname, formatter=formatter,
396 obj_type = type(obj)
419 info=info, detail_level=detail_level)
397
420 displayfields = []
398 header = self.__head
421 for title, key in self.pinfo_fields1:
399 if info is None:
422 field = info[key]
400 ismagic = 0
423 if field is not None:
401 isalias = 0
424 displayfields.append((title, field.rstrip()))
402 ospace = ''
425
403 else:
426 # Source or docstring, depending on detail level and whether
404 ismagic = info.ismagic
427 # source found.
405 isalias = info.isalias
428 if detail_level > 0 and info['source'] is not None:
406 ospace = info.namespace
429 displayfields.append(("Source", info['source']))
407 # Get docstring, special-casing aliases:
430 elif info['docstring'] is not None:
408 if isalias:
431 displayfields.append(("Docstring", info["docstring"]))
409 if not callable(obj):
432
410 try:
433 # Constructor info for classes
411 ds = "Alias to the system command:\n %s" % obj[1]
434 if info['isclass']:
412 except:
435 if info['init_definition'] or info['init_docstring']:
413 ds = "Alias: " + str(obj)
436 displayfields.append(("Constructor information", ""))
414 else:
437 if info['init_definition'] is not None:
415 ds = "Alias to " + str(obj)
438 displayfields.append((" Definition",
416 if obj.__doc__:
439 info['init_definition'].rstrip()))
417 ds += "\nDocstring:\n" + obj.__doc__
440 if info['init_docstring'] is not None:
418 else:
441 displayfields.append((" Docstring",
419 ds = getdoc(obj)
442 indent(info['init_docstring'])))
420 if ds is None:
443
421 ds = '<no docstring>'
444 # Info for objects:
422 if formatter is not None:
423 ds = formatter(ds)
424
425 # store output in a list which gets joined with \n at the end.
426 out = myStringIO()
427
428 string_max = 200 # max size of strings to show (snipped if longer)
429 shalf = int((string_max -5)/2)
430
431 if ismagic:
432 obj_type_name = 'Magic function'
433 elif isalias:
434 obj_type_name = 'System alias'
435 else:
436 obj_type_name = obj_type.__name__
437 out.writeln(header('Type:\t\t')+obj_type_name)
438
439 try:
440 bclass = obj.__class__
441 out.writeln(header('Base Class:\t')+str(bclass))
442 except: pass
443
444 # String form, but snip if too long in ? form (full in ??)
445 if detail_level >= self.str_detail_level:
446 try:
447 ostr = str(obj)
448 str_head = 'String Form:'
449 if not detail_level and len(ostr)>string_max:
450 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
451 ostr = ("\n" + " " * len(str_head.expandtabs())).\
452 join(q.strip() for q in ostr.split("\n"))
453 if ostr.find('\n') > -1:
454 # Print multi-line strings starting at the next line.
455 str_sep = '\n'
456 else:
457 str_sep = '\t'
458 out.writeln("%s%s%s" % (header(str_head),str_sep,ostr))
459 except:
460 pass
461
462 if ospace:
463 out.writeln(header('Namespace:\t')+ospace)
464
465 # Length (for strings and lists)
466 try:
467 length = str(len(obj))
468 out.writeln(header('Length:\t\t')+length)
469 except: pass
470
471 # Filename where object was defined
472 binary_file = False
473 try:
474 try:
475 fname = inspect.getabsfile(obj)
476 except TypeError:
477 # For an instance, the file that matters is where its class was
478 # declared.
479 if hasattr(obj,'__class__'):
480 fname = inspect.getabsfile(obj.__class__)
481 if fname.endswith('<string>'):
482 fname = 'Dynamically generated function. No source code available.'
483 if (fname.endswith('.so') or fname.endswith('.dll')):
484 binary_file = True
485 out.writeln(header('File:\t\t')+fname)
486 except:
487 # if anything goes wrong, we don't want to show source, so it's as
488 # if the file was binary
489 binary_file = True
490
491 # reconstruct the function definition and print it:
492 defln = self._getdef(obj,oname)
493 if defln:
494 out.write(header('Definition:\t')+self.format(defln))
495
496 # Docstrings only in detail 0 mode, since source contains them (we
497 # avoid repetitions). If source fails, we add them back, see below.
498 if ds and detail_level == 0:
499 out.writeln(header('Docstring:\n') + indent(ds))
500
501 # Original source code for any callable
502 if detail_level:
503 # Flush the source cache because inspect can return out-of-date
504 # source
505 linecache.checkcache()
506 source_success = False
507 try:
508 try:
509 src = getsource(obj,binary_file)
510 except TypeError:
511 if hasattr(obj,'__class__'):
512 src = getsource(obj.__class__,binary_file)
513 if src is not None:
514 source = self.format(src)
515 out.write(header('Source:\n')+source.rstrip()+'\n')
516 source_success = True
517 except Exception, msg:
518 pass
519
520 if ds and not source_success:
521 out.writeln(header('Docstring [source file open failed]:\n')
522 + indent(ds))
523
524 # Constructor docstring for classes
525 if inspect.isclass(obj):
526 # reconstruct the function definition and print it:
527 try:
528 obj_init = obj.__init__
529 except AttributeError:
530 init_def = init_ds = None
531 else:
532 init_def = self._getdef(obj_init,oname)
533 init_ds = getdoc(obj_init)
534 # Skip Python's auto-generated docstrings
535 if init_ds and \
536 init_ds.startswith('x.__init__(...) initializes'):
537 init_ds = None
538
539 if init_def or init_ds:
540 out.writeln(header('Constructor information:'))
541 if init_def:
542 out.write(header('Definition:\t')+ self.format(init_def))
543 if init_ds:
544 out.writeln(header('Docstring:\n') + indent(init_ds))
545 # and class docstring for instances:
546 elif obj_type is types.InstanceType or \
547 isinstance(obj,object):
548
549 # First, check whether the instance docstring is identical to the
550 # class one, and print it separately if they don't coincide. In
551 # most cases they will, but it's nice to print all the info for
552 # objects which use instance-customized docstrings.
553 if ds:
554 try:
555 cls = getattr(obj,'__class__')
556 except:
557 class_ds = None
558 else:
445 else:
559 class_ds = getdoc(cls)
446 for title, key in self.pinfo_fields_obj:
560 # Skip Python's auto-generated docstrings
447 field = info[key]
561 if class_ds and \
448 if field is not None:
562 (class_ds.startswith('function(code, globals[,') or \
449 displayfields.append((title, field.rstrip()))
563 class_ds.startswith('instancemethod(function, instance,') or \
564 class_ds.startswith('module(name[,') ):
565 class_ds = None
566 if class_ds and ds != class_ds:
567 out.writeln(header('Class Docstring:\n') +
568 indent(class_ds))
569
450
570 # Next, try to show constructor docstrings
451 # Finally send to printer/pager:
571 try:
452 if displayfields:
572 init_ds = getdoc(obj.__init__)
453 page.page(self._format_fields(displayfields))
573 # Skip Python's auto-generated docstrings
574 if init_ds and \
575 init_ds.startswith('x.__init__(...) initializes'):
576 init_ds = None
577 except AttributeError:
578 init_ds = None
579 if init_ds:
580 out.writeln(header('Constructor Docstring:\n') +
581 indent(init_ds))
582
583 # Call form docstring for callable instances
584 if hasattr(obj,'__call__'):
585 #out.writeln(header('Callable:\t')+'Yes')
586 call_def = self._getdef(obj.__call__,oname)
587 #if call_def is None:
588 # out.writeln(header('Call def:\t')+
589 # 'Calling definition not available.')
590 if call_def is not None:
591 out.writeln(header('Call def:\t')+self.format(call_def))
592 call_ds = getdoc(obj.__call__)
593 # Skip Python's auto-generated docstrings
594 if call_ds and call_ds.startswith('x.__call__(...) <==> x(...)'):
595 call_ds = None
596 if call_ds:
597 out.writeln(header('Call docstring:\n') + indent(call_ds))
598
599 # Finally send to printer/pager
600 output = out.getvalue()
601 if output:
602 page.page(output)
603 # end pinfo
604
454
605 def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
455 def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
606 """Compute a dict with detailed information about an object.
456 """Compute a dict with detailed information about an object.
@@ -675,11 +525,6 b' class Inspector:'
675 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
525 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
676 ostr = ("\n" + " " * len(str_head.expandtabs())).\
526 ostr = ("\n" + " " * len(str_head.expandtabs())).\
677 join(q.strip() for q in ostr.split("\n"))
527 join(q.strip() for q in ostr.split("\n"))
678 if ostr.find('\n') > -1:
679 # Print multi-line strings starting at the next line.
680 str_sep = '\n'
681 else:
682 str_sep = '\t'
683 out[str_head] = ostr
528 out[str_head] = ostr
684 except:
529 except:
685 pass
530 pass
@@ -727,7 +572,7 b' class Inspector:'
727 # Flush the source cache because inspect can return out-of-date
572 # Flush the source cache because inspect can return out-of-date
728 # source
573 # source
729 linecache.checkcache()
574 linecache.checkcache()
730 source_success = False
575 source = None
731 try:
576 try:
732 try:
577 try:
733 src = getsource(obj,binary_file)
578 src = getsource(obj,binary_file)
@@ -737,12 +582,16 b' class Inspector:'
737 if src is not None:
582 if src is not None:
738 source = self.format(src)
583 source = self.format(src)
739 out['source'] = source.rstrip()
584 out['source'] = source.rstrip()
740 source_success = True
585 except Exception:
741 except Exception, msg:
742 pass
586 pass
743
587
588 if ds and source is None:
589 out['docstring'] = ds
590
591
744 # Constructor docstring for classes
592 # Constructor docstring for classes
745 if inspect.isclass(obj):
593 if inspect.isclass(obj):
594 out['isclass'] = True
746 # reconstruct the function definition and print it:
595 # reconstruct the function definition and print it:
747 try:
596 try:
748 obj_init = obj.__init__
597 obj_init = obj.__init__
@@ -763,8 +612,7 b' class Inspector:'
763 out['init_docstring'] = init_ds
612 out['init_docstring'] = init_ds
764
613
765 # and class docstring for instances:
614 # and class docstring for instances:
766 elif obj_type is types.InstanceType or \
615 else:
767 isinstance(obj, object):
768 # First, check whether the instance docstring is identical to the
616 # First, check whether the instance docstring is identical to the
769 # class one, and print it separately if they don't coincide. In
617 # class one, and print it separately if they don't coincide. In
770 # most cases they will, but it's nice to print all the info for
618 # most cases they will, but it's nice to print all the info for
@@ -87,3 +87,33 b' def test_calltip_function2():'
87
87
88 def test_calltip_builtin():
88 def test_calltip_builtin():
89 check_calltip(sum, 'sum', None, sum.__doc__)
89 check_calltip(sum, 'sum', None, sum.__doc__)
90
91 def test_info():
92 "Check that Inspector.info fills out various fields as expected."
93 i = inspector.info(Call, oname='Call')
94 nt.assert_equal(i['type_name'], 'type')
95 nt.assert_equal(i['base_class'], "<type 'type'>")
96 nt.assert_equal(i['string_form'], "<class 'IPython.core.tests.test_oinspect.Call'>")
97 fname = __file__
98 if fname.endswith(".pyc"):
99 fname = fname[:-1]
100 nt.assert_equal(i['file'], fname)
101 nt.assert_equal(i['definition'], 'Call(self, *a, **kw)\n')
102 nt.assert_equal(i['docstring'], Call.__doc__)
103 nt.assert_is(i['source'], None)
104 nt.assert_true(i['isclass'])
105 nt.assert_equal(i['init_definition'], "Call(self, x, y=1)\n")
106 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
107
108 i = inspector.info(Call, detail_level=1)
109 nt.assert_is_not(i['source'], None)
110 nt.assert_is(i['docstring'], None)
111
112 c = Call(1)
113 c.__doc__ = "Modified instance docstring"
114 i = inspector.info(c)
115 nt.assert_equal(i['type_name'], 'Call')
116 nt.assert_equal(i['docstring'], "Modified instance docstring")
117 nt.assert_equal(i['class_docstring'], Call.__doc__)
118 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
119 nt.assert_equal(i['call_docstring'], c.__call__.__doc__)
General Comments 0
You need to be logged in to leave comments. Login now