Show More
@@ -18,7 +18,6 b" __all__ = ['Inspector','InspectColors']" | |||
|
18 | 18 | |
|
19 | 19 | # stdlib modules |
|
20 | 20 | import __builtin__ |
|
21 | import StringIO | |
|
22 | 21 | import inspect |
|
23 | 22 | import linecache |
|
24 | 23 | import os |
@@ -75,7 +74,7 b" info_fields = ['type_name', 'base_class', 'string_form', 'namespace'," | |||
|
75 | 74 | 'call_def', 'call_docstring', |
|
76 | 75 | # These won't be printed but will be used to determine how to |
|
77 | 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 | 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 | 230 | class Inspector: |
|
242 | 231 | def __init__(self, color_table=InspectColors, |
@@ -377,7 +366,40 b' class Inspector:' | |||
|
377 | 366 | # getsourcelines returns lineno with 1-offset and page() uses |
|
378 | 367 | # 0-offset, so we must adjust. |
|
379 | 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 = [("Constructor Docstring","init_docstring"), | |
|
400 | ("Call def", "call_def"), | |
|
401 | ("Call docstring", "call_docstring")] | |
|
402 | ||
|
381 | 403 | def pinfo(self,obj,oname='',formatter=None,info=None,detail_level=0): |
|
382 | 404 | """Show detailed information about an object. |
|
383 | 405 | |
@@ -392,215 +414,42 b' class Inspector:' | |||
|
392 | 414 | |
|
393 | 415 | - detail_level: if set to 1, more information is given. |
|
394 | 416 | """ |
|
395 | ||
|
396 | obj_type = type(obj) | |
|
397 | ||
|
398 | header = self.__head | |
|
399 | if info is None: | |
|
400 | ismagic = 0 | |
|
401 | isalias = 0 | |
|
402 | ospace = '' | |
|
403 | else: | |
|
404 | ismagic = info.ismagic | |
|
405 | isalias = info.isalias | |
|
406 | ospace = info.namespace | |
|
407 | # Get docstring, special-casing aliases: | |
|
408 | if isalias: | |
|
409 | if not callable(obj): | |
|
410 | try: | |
|
411 | ds = "Alias to the system command:\n %s" % obj[1] | |
|
412 | except: | |
|
413 | ds = "Alias: " + str(obj) | |
|
414 | else: | |
|
415 | ds = "Alias to " + str(obj) | |
|
416 | if obj.__doc__: | |
|
417 | ds += "\nDocstring:\n" + obj.__doc__ | |
|
418 | else: | |
|
419 | ds = getdoc(obj) | |
|
420 | if ds is None: | |
|
421 | ds = '<no docstring>' | |
|
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() | |
|
417 | info = self.info(obj, oname=oname, formatter=formatter, | |
|
418 | info=info, detail_level=detail_level) | |
|
419 | displayfields = [] | |
|
420 | for title, key in self.pinfo_fields1: | |
|
421 | field = info[key] | |
|
422 | if field is not None: | |
|
423 | displayfields.append((title, field.rstrip())) | |
|
427 | 424 | |
|
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' | |
|
425 | # Source or docstring, depending on detail level and whether | |
|
426 | # source found. | |
|
427 | if detail_level > 0 and info['source'] is not None: | |
|
428 | displayfields.append(("Source", info['source'])) | |
|
429 | elif info['docstring'] is not None: | |
|
430 | displayfields.append(("Docstring", info["docstring"])) | |
|
431 | ||
|
432 | # Constructor info for classes | |
|
433 | if info['isclass']: | |
|
434 | if info['init_definition'] or info['init_docstring']: | |
|
435 | displayfields.append(("Constructor information", "")) | |
|
436 | if info['init_definition'] is not None: | |
|
437 | displayfields.append((" Definition", | |
|
438 | info['init_definition'].rstrip())) | |
|
439 | if info['init_docstring'] is not None: | |
|
440 | displayfields.append((" Docstring", | |
|
441 | indent(info['init_docstring']))) | |
|
442 | ||
|
443 | # Info for objects: | |
|
435 | 444 | 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: | |
|
559 | class_ds = getdoc(cls) | |
|
560 | # Skip Python's auto-generated docstrings | |
|
561 | if class_ds and \ | |
|
562 | (class_ds.startswith('function(code, globals[,') or \ | |
|
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 | ||
|
570 | # Next, try to show constructor docstrings | |
|
571 | try: | |
|
572 | init_ds = getdoc(obj.__init__) | |
|
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 | |
|
445 | for title, key in self.pinfo_fields_obj: | |
|
446 | field = info[key] | |
|
447 | if field is not None: | |
|
448 | displayfields.append((title, field.rstrip())) | |
|
449 | ||
|
450 | # Finally send to printer/pager: | |
|
451 | if displayfields: | |
|
452 | page.page(self._format_fields(displayfields)) | |
|
604 | 453 | |
|
605 | 454 | def info(self, obj, oname='', formatter=None, info=None, detail_level=0): |
|
606 | 455 | """Compute a dict with detailed information about an object. |
@@ -675,11 +524,6 b' class Inspector:' | |||
|
675 | 524 | ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:] |
|
676 | 525 | ostr = ("\n" + " " * len(str_head.expandtabs())).\ |
|
677 | 526 | 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 | 527 | out[str_head] = ostr |
|
684 | 528 | except: |
|
685 | 529 | pass |
@@ -738,11 +582,14 b' class Inspector:' | |||
|
738 | 582 | source = self.format(src) |
|
739 | 583 | out['source'] = source.rstrip() |
|
740 | 584 | source_success = True |
|
741 |
except Exception |
|
|
742 |
|
|
|
585 | except Exception: | |
|
586 | if ds: | |
|
587 | out['docstring'] = ds | |
|
588 | ||
|
743 | 589 | |
|
744 | 590 | # Constructor docstring for classes |
|
745 | 591 | if inspect.isclass(obj): |
|
592 | out['isclass'] = True | |
|
746 | 593 | # reconstruct the function definition and print it: |
|
747 | 594 | try: |
|
748 | 595 | obj_init = obj.__init__ |
@@ -763,8 +610,7 b' class Inspector:' | |||
|
763 | 610 | out['init_docstring'] = init_ds |
|
764 | 611 | |
|
765 | 612 | # and class docstring for instances: |
|
766 | elif obj_type is types.InstanceType or \ | |
|
767 | isinstance(obj, object): | |
|
613 | else: | |
|
768 | 614 | # First, check whether the instance docstring is identical to the |
|
769 | 615 | # class one, and print it separately if they don't coincide. In |
|
770 | 616 | # most cases they will, but it's nice to print all the info for |
General Comments 0
You need to be logged in to leave comments.
Login now