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 |
|
|
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 |
|
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 |
|
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