oinspect.py
919 lines
| 30.7 KiB
| text/x-python
|
PythonLexer
Ville M. Vainio
|
r1032 | # -*- coding: utf-8 -*- | ||
"""Tools for inspecting Python objects. | ||||
Uses syntax highlighting for presenting the various information elements. | ||||
Similar in spirit to the inspect module, but all calls take a name argument to | ||||
reference the name under which an object is being read. | ||||
""" | ||||
MinRK
|
r16579 | # Copyright (c) IPython Development Team. | ||
# Distributed under the terms of the Modified BSD License. | ||||
Matthias BUSSONNIER
|
r7817 | from __future__ import print_function | ||
Ville M. Vainio
|
r1032 | |||
__all__ = ['Inspector','InspectColors'] | ||||
# stdlib modules | ||||
import inspect | ||||
import linecache | ||||
import os | ||||
immerrr
|
r17023 | from textwrap import dedent | ||
Fernando Perez
|
r1413 | import types | ||
Jörgen Stenarson
|
r8322 | import io as stdlib_io | ||
Thomas Kluyver
|
r4744 | try: | ||
from itertools import izip_longest | ||||
except ImportError: | ||||
from itertools import zip_longest as izip_longest | ||||
Fernando Perez
|
r1413 | |||
Ville M. Vainio
|
r1032 | # IPython's own | ||
Brian Granger
|
r2830 | from IPython.core import page | ||
immerrr
|
r17023 | from IPython.lib.pretty import pretty | ||
Thomas Kluyver
|
r5204 | from IPython.testing.skipdoctest import skip_doctest_py3 | ||
Brian Granger
|
r2498 | from IPython.utils import PyColorize | ||
MinRK
|
r3800 | from IPython.utils import io | ||
Jörgen Stenarson
|
r8304 | from IPython.utils import openpy | ||
Thomas Kluyver
|
r4744 | from IPython.utils import py3compat | ||
Jeffrey Tratner
|
r12965 | from IPython.utils.dir2 import safe_hasattr | ||
Brian Granger
|
r2498 | from IPython.utils.text import indent | ||
Brian Granger
|
r2051 | from IPython.utils.wildcard import list_namespace | ||
Thomas Kluyver
|
r15335 | from IPython.utils.coloransi import TermColors, ColorScheme, ColorSchemeTable | ||
from IPython.utils.py3compat import cast_unicode, string_types, PY3 | ||||
Erik M. Bray
|
r17712 | from IPython.utils.signatures import signature | ||
Ville M. Vainio
|
r1032 | |||
MinRK
|
r14835 | # builtin docstrings to ignore | ||
_func_call_docstring = types.FunctionType.__call__.__doc__ | ||||
_object_init_docstring = object.__init__.__doc__ | ||||
_builtin_type_docstrings = { | ||||
immerrr
|
r17025 | inspect.getdoc(t) for t in (types.ModuleType, types.MethodType, | ||
types.FunctionType, property) | ||||
MinRK
|
r14835 | } | ||
Thomas Kluyver
|
r15362 | |||
_builtin_func_type = type(all) | ||||
_builtin_meth_type = type(str.upper) # Bound methods have the same type as builtin functions | ||||
Ville M. Vainio
|
r1032 | #**************************************************************************** | ||
# Builtin color schemes | ||||
Colors = TermColors # just a shorthand | ||||
# Build a few color schemes | ||||
NoColor = ColorScheme( | ||||
'NoColor',{ | ||||
'header' : Colors.NoColor, | ||||
'normal' : Colors.NoColor # color off (usu. Colors.Normal) | ||||
} ) | ||||
LinuxColors = ColorScheme( | ||||
'Linux',{ | ||||
'header' : Colors.LightRed, | ||||
'normal' : Colors.Normal # color off (usu. Colors.Normal) | ||||
} ) | ||||
LightBGColors = ColorScheme( | ||||
'LightBG',{ | ||||
'header' : Colors.Red, | ||||
'normal' : Colors.Normal # color off (usu. Colors.Normal) | ||||
} ) | ||||
# Build table of color schemes (needed by the parser) | ||||
InspectColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors], | ||||
'Linux') | ||||
#**************************************************************************** | ||||
Fernando Perez
|
r2931 | # Auxiliary functions and objects | ||
# See the messaging spec for the definition of all these fields. This list | ||||
# effectively defines the order of display | ||||
info_fields = ['type_name', 'base_class', 'string_form', 'namespace', | ||||
'length', 'file', 'definition', 'docstring', 'source', | ||||
'init_definition', 'class_docstring', 'init_docstring', | ||||
'call_def', 'call_docstring', | ||||
# These won't be printed but will be used to determine how to | ||||
# format the object | ||||
Thomas Kluyver
|
r3856 | 'ismagic', 'isalias', 'isclass', 'argspec', 'found', 'name' | ||
Fernando Perez
|
r2931 | ] | ||
Fernando Perez
|
r3051 | def object_info(**kw): | ||
"""Make an object info dict with all fields present.""" | ||||
Fernando Perez
|
r2931 | infodict = dict(izip_longest(info_fields, [None])) | ||
infodict.update(kw) | ||||
Fernando Perez
|
r3051 | return infodict | ||
Fernando Perez
|
r2931 | |||
Jörgen Stenarson
|
r8322 | def get_encoding(obj): | ||
"""Get encoding for python source file defining obj | ||||
Returns None if obj is not defined in a sourcefile. | ||||
""" | ||||
ofile = find_file(obj) | ||||
# run contents of file through pager starting at line where the object | ||||
# is defined, as long as the file isn't binary and is actually on the | ||||
# filesystem. | ||||
if ofile is None: | ||||
return None | ||||
elif ofile.endswith(('.so', '.dll', '.pyd')): | ||||
return None | ||||
elif not os.path.isfile(ofile): | ||||
return None | ||||
else: | ||||
# Print only text files, not extension binaries. Note that | ||||
# getsourcelines returns lineno with 1-offset and page() uses | ||||
# 0-offset, so we must adjust. | ||||
Thomas Kluyver
|
r15467 | with stdlib_io.open(ofile, 'rb') as buffer: # Tweaked to use io.open for Python 2 | ||
encoding, lines = openpy.detect_encoding(buffer.readline) | ||||
Jörgen Stenarson
|
r8322 | return encoding | ||
Ville M. Vainio
|
r1032 | def getdoc(obj): | ||
"""Stable wrapper around inspect.getdoc. | ||||
This can't crash because of attribute problems. | ||||
It also attempts to call a getdoc() method on the given object. This | ||||
allows objects which provide their docstrings via non-standard mechanisms | ||||
(like Pyro proxies) to still be inspected by ipython's ? system.""" | ||||
# Allow objects to offer customized documentation via a getdoc method: | ||||
try: | ||||
Thomas Kluyver
|
r5535 | ds = obj.getdoc() | ||
except Exception: | ||||
Ville M. Vainio
|
r1032 | pass | ||
else: | ||||
# if we get extra info, we add it to the normal docstring. | ||||
Thomas Kluyver
|
r13353 | if isinstance(ds, string_types): | ||
Thomas Kluyver
|
r5536 | return inspect.cleandoc(ds) | ||
Thomas Kluyver
|
r5535 | |||
try: | ||||
Jörgen Stenarson
|
r8322 | docstr = inspect.getdoc(obj) | ||
encoding = get_encoding(obj) | ||||
Jörgen Stenarson
|
r8323 | return py3compat.cast_unicode(docstr, encoding=encoding) | ||
Thomas Kluyver
|
r5535 | except Exception: | ||
# Harden against an inspect failure, which can occur with | ||||
# SWIG-wrapped extensions. | ||||
Jörgen Stenarson
|
r8322 | raise | ||
Thomas Kluyver
|
r5535 | return None | ||
Ville M. Vainio
|
r1032 | |||
Fernando Perez
|
r1413 | |||
immerrr
|
r17023 | def getsource(obj, oname=''): | ||
Ville M. Vainio
|
r1032 | """Wrapper around inspect.getsource. | ||
This can be modified by other projects to provide customized source | ||||
extraction. | ||||
immerrr
|
r17023 | Parameters | ||
---------- | ||||
obj : object | ||||
an object whose source code we will attempt to extract | ||||
oname : str | ||||
(optional) a name under which the object is known | ||||
Ville M. Vainio
|
r1032 | |||
immerrr
|
r17023 | Returns | ||
------- | ||||
src : unicode or None | ||||
Ville M. Vainio
|
r1032 | |||
immerrr
|
r17023 | """ | ||
Ville M. Vainio
|
r1032 | |||
immerrr
|
r17023 | if isinstance(obj, property): | ||
sources = [] | ||||
for attrname in ['fget', 'fset', 'fdel']: | ||||
fn = getattr(obj, attrname) | ||||
if fn is not None: | ||||
encoding = get_encoding(fn) | ||||
oname_prefix = ('%s.' % oname) if oname else '' | ||||
sources.append(cast_unicode( | ||||
''.join(('# ', oname_prefix, attrname)), | ||||
encoding=encoding)) | ||||
if inspect.isfunction(fn): | ||||
sources.append(dedent(getsource(fn))) | ||||
else: | ||||
# Default str/repr only prints function name, | ||||
# pretty.pretty prints module name too. | ||||
sources.append(cast_unicode( | ||||
'%s%s = %s\n' % ( | ||||
oname_prefix, attrname, pretty(fn)), | ||||
encoding=encoding)) | ||||
if sources: | ||||
return '\n'.join(sources) | ||||
else: | ||||
return None | ||||
Ben Edwards
|
r4266 | |||
Ville M. Vainio
|
r1032 | else: | ||
immerrr
|
r17023 | # Get source for non-property objects. | ||
# '__wrapped__' attribute is used by some decorators (e.g. ones defined | ||||
# functools) to provide access to the decorated function. | ||||
if hasattr(obj, "__wrapped__"): | ||||
Ben Edwards
|
r4266 | obj = obj.__wrapped__ | ||
immerrr
|
r17023 | |||
Fernando Perez
|
r1228 | try: | ||
src = inspect.getsource(obj) | ||||
except TypeError: | ||||
immerrr
|
r17023 | # The object itself provided no meaningful source, try looking for | ||
# its class definition instead. | ||||
if hasattr(obj, '__class__'): | ||||
try: | ||||
src = inspect.getsource(obj.__class__) | ||||
except TypeError: | ||||
return None | ||||
Jörgen Stenarson
|
r8323 | encoding = get_encoding(obj) | ||
return cast_unicode(src, encoding=encoding) | ||||
Ville M. Vainio
|
r1032 | |||
Thomas Kluyver
|
r15362 | |||
def is_simple_callable(obj): | ||||
"""True if obj is a function ()""" | ||||
return (inspect.isfunction(obj) or inspect.ismethod(obj) or \ | ||||
isinstance(obj, _builtin_func_type) or isinstance(obj, _builtin_meth_type)) | ||||
Fernando Perez
|
r1413 | def getargspec(obj): | ||
Thomas Kluyver
|
r15335 | """Wrapper around :func:`inspect.getfullargspec` on Python 3, and | ||
:func:inspect.getargspec` on Python 2. | ||||
In addition to functions and methods, this can also handle objects with a | ||||
``__call__`` attribute. | ||||
""" | ||||
Thomas Kluyver
|
r15362 | if safe_hasattr(obj, '__call__') and not is_simple_callable(obj): | ||
Thomas Kluyver
|
r15335 | obj = obj.__call__ | ||
return inspect.getfullargspec(obj) if PY3 else inspect.getargspec(obj) | ||||
Fernando Perez
|
r1413 | |||
Fernando Perez
|
r3051 | |||
def format_argspec(argspec): | ||||
"""Format argspect, convenience wrapper around inspect's. | ||||
This takes a dict instead of ordered arguments and calls | ||||
inspect.format_argspec with the arguments in the necessary order. | ||||
""" | ||||
return inspect.formatargspec(argspec['args'], argspec['varargs'], | ||||
argspec['varkw'], argspec['defaults']) | ||||
def call_tip(oinfo, format_call=True): | ||||
"""Extract call tip data from an oinfo dict. | ||||
Parameters | ||||
---------- | ||||
oinfo : dict | ||||
format_call : bool, optional | ||||
If True, the call line is formatted and returned as a string. If not, a | ||||
tuple of (name, argspec) is returned. | ||||
Returns | ||||
------- | ||||
call_info : None, str or (str, dict) tuple. | ||||
When format_call is True, the whole call information is formattted as a | ||||
single string. Otherwise, the object's name and its argspec dict are | ||||
returned. If no call information is available, None is returned. | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r3051 | docstring : str or None | ||
The most relevant docstring for calling purposes is returned, if | ||||
available. The priority is: call docstring for callable instances, then | ||||
constructor docstring for classes, then main object's docstring otherwise | ||||
(regular functions). | ||||
""" | ||||
# Get call definition | ||||
MinRK
|
r3934 | argspec = oinfo.get('argspec') | ||
Fernando Perez
|
r3051 | if argspec is None: | ||
call_line = None | ||||
else: | ||||
# Callable objects will have 'self' as their first argument, prune | ||||
# it out if it's there for clarity (since users do *not* pass an | ||||
# extra first argument explicitly). | ||||
try: | ||||
has_self = argspec['args'][0] == 'self' | ||||
except (KeyError, IndexError): | ||||
pass | ||||
else: | ||||
if has_self: | ||||
argspec['args'] = argspec['args'][1:] | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r3051 | call_line = oinfo['name']+format_argspec(argspec) | ||
# Now get docstring. | ||||
# The priority is: call docstring, constructor docstring, main one. | ||||
MinRK
|
r3934 | doc = oinfo.get('call_docstring') | ||
Fernando Perez
|
r3051 | if doc is None: | ||
MinRK
|
r3934 | doc = oinfo.get('init_docstring') | ||
Fernando Perez
|
r3051 | if doc is None: | ||
MinRK
|
r3934 | doc = oinfo.get('docstring','') | ||
Fernando Perez
|
r3051 | |||
return call_line, doc | ||||
Fernando Perez
|
r1413 | |||
Fernando Perez
|
r7290 | def find_file(obj): | ||
"""Find the absolute path to the file where an object was defined. | ||||
This is essentially a robust wrapper around `inspect.getabsfile`. | ||||
Returns None if no file can be found. | ||||
Parameters | ||||
---------- | ||||
obj : any Python object | ||||
Returns | ||||
------- | ||||
fname : str | ||||
The absolute path to the file where the object was defined. | ||||
""" | ||||
Fernando Perez
|
r7431 | # get source if obj was decorated with @decorator | ||
Thomas Kluyver
|
r11058 | if safe_hasattr(obj, '__wrapped__'): | ||
Fernando Perez
|
r7431 | obj = obj.__wrapped__ | ||
Bradley M. Froehle
|
r7520 | |||
fname = None | ||||
Fernando Perez
|
r7290 | try: | ||
fname = inspect.getabsfile(obj) | ||||
except TypeError: | ||||
# For an instance, the file that matters is where its class was | ||||
# declared. | ||||
Fernando Perez
|
r7431 | if hasattr(obj, '__class__'): | ||
Fernando Perez
|
r7290 | try: | ||
fname = inspect.getabsfile(obj.__class__) | ||||
except TypeError: | ||||
# Can happen for builtins | ||||
Bradley M. Froehle
|
r7520 | pass | ||
Fernando Perez
|
r7290 | except: | ||
Bradley M. Froehle
|
r7520 | pass | ||
MinRK
|
r8546 | return cast_unicode(fname) | ||
Fernando Perez
|
r7290 | |||
def find_source_lines(obj): | ||||
"""Find the line number in a file where an object was defined. | ||||
This is essentially a robust wrapper around `inspect.getsourcelines`. | ||||
Returns None if no file can be found. | ||||
Parameters | ||||
---------- | ||||
obj : any Python object | ||||
Returns | ||||
------- | ||||
lineno : int | ||||
The line number where the object definition starts. | ||||
""" | ||||
Fernando Perez
|
r7431 | # get source if obj was decorated with @decorator | ||
Thomas Kluyver
|
r11058 | if safe_hasattr(obj, '__wrapped__'): | ||
Fernando Perez
|
r7431 | obj = obj.__wrapped__ | ||
Fernando Perez
|
r7290 | try: | ||
try: | ||||
lineno = inspect.getsourcelines(obj)[1] | ||||
except TypeError: | ||||
# For instances, try the class object like getsource() does | ||||
Fernando Perez
|
r7431 | if hasattr(obj, '__class__'): | ||
Fernando Perez
|
r7290 | lineno = inspect.getsourcelines(obj.__class__)[1] | ||
MinRK
|
r9020 | else: | ||
lineno = None | ||||
Fernando Perez
|
r7290 | except: | ||
return None | ||||
return lineno | ||||
Ville M. Vainio
|
r1032 | class Inspector: | ||
Fernando Perez
|
r3051 | def __init__(self, color_table=InspectColors, | ||
code_color_table=PyColorize.ANSICodeColors, | ||||
scheme='NoColor', | ||||
Ville M. Vainio
|
r1032 | str_detail_level=0): | ||
self.color_table = color_table | ||||
self.parser = PyColorize.Parser(code_color_table,out='str') | ||||
self.format = self.parser.format | ||||
self.str_detail_level = str_detail_level | ||||
self.set_active_scheme(scheme) | ||||
Fernando Perez
|
r2929 | def _getdef(self,obj,oname=''): | ||
Bradley M. Froehle
|
r8707 | """Return the call signature for any callable object. | ||
Ville M. Vainio
|
r1032 | |||
If any exception is generated, None is returned instead and the | ||||
exception is suppressed.""" | ||||
try: | ||||
Erik M. Bray
|
r17712 | hdef = oname + str(signature(obj)) | ||
jstenar
|
r8312 | return cast_unicode(hdef) | ||
Ville M. Vainio
|
r1032 | except: | ||
return None | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1032 | def __head(self,h): | ||
"""Return a header string with proper colors.""" | ||||
return '%s%s%s' % (self.color_table.active_colors.header,h, | ||||
self.color_table.active_colors.normal) | ||||
Fernando Perez
|
r7290 | def set_active_scheme(self, scheme): | ||
Ville M. Vainio
|
r1032 | self.color_table.set_active_scheme(scheme) | ||
self.parser.color_table.set_active_scheme(scheme) | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r7290 | def noinfo(self, msg, oname): | ||
Ville M. Vainio
|
r1032 | """Generic message when no information is found.""" | ||
Matthias BUSSONNIER
|
r7817 | print('No %s found' % msg, end=' ') | ||
Ville M. Vainio
|
r1032 | if oname: | ||
Matthias BUSSONNIER
|
r7817 | print('for %s' % oname) | ||
Ville M. Vainio
|
r1032 | else: | ||
Matthias BUSSONNIER
|
r7817 | print() | ||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r7290 | def pdef(self, obj, oname=''): | ||
Bradley M. Froehle
|
r8707 | """Print the call signature for any callable object. | ||
Ville M. Vainio
|
r1032 | |||
If the object is a class, print the constructor information.""" | ||||
if not callable(obj): | ||||
Matthias BUSSONNIER
|
r7817 | print('Object is not callable.') | ||
Ville M. Vainio
|
r1032 | return | ||
header = '' | ||||
if inspect.isclass(obj): | ||||
header = self.__head('Class constructor information:\n') | ||||
obj = obj.__init__ | ||||
Thomas Kluyver
|
r7461 | elif (not py3compat.PY3) and type(obj) is types.InstanceType: | ||
Ville M. Vainio
|
r1032 | obj = obj.__call__ | ||
Fernando Perez
|
r2929 | output = self._getdef(obj,oname) | ||
Ville M. Vainio
|
r1032 | if output is None: | ||
self.noinfo('definition header',oname) | ||||
else: | ||||
Matthias BUSSONNIER
|
r7817 | print(header,self.format(output), end=' ', file=io.stdout) | ||
Ville M. Vainio
|
r1032 | |||
Thomas Kluyver
|
r5204 | # In Python 3, all classes are new-style, so they all have __init__. | ||
@skip_doctest_py3 | ||||
Ville M. Vainio
|
r1032 | def pdoc(self,obj,oname='',formatter = None): | ||
"""Print the docstring for any object. | ||||
Optional: | ||||
-formatter: a function to run the docstring through for specially | ||||
MinRK
|
r4249 | formatted docstrings. | ||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4249 | Examples | ||
-------- | ||||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4249 | In [1]: class NoInit: | ||
...: pass | ||||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4249 | In [2]: class NoDoc: | ||
...: def __init__(self): | ||||
...: pass | ||||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4249 | In [3]: %pdoc NoDoc | ||
No documentation found for NoDoc | ||||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4249 | In [4]: %pdoc NoInit | ||
No documentation found for NoInit | ||||
In [5]: obj = NoInit() | ||||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4249 | In [6]: %pdoc obj | ||
No documentation found for obj | ||||
In [5]: obj2 = NoDoc() | ||||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4249 | In [6]: %pdoc obj2 | ||
No documentation found for obj2 | ||||
""" | ||||
Ville M. Vainio
|
r1032 | |||
Thomas Kluyver
|
r4052 | head = self.__head # For convenience | ||
MinRK
|
r4249 | lines = [] | ||
Ville M. Vainio
|
r1032 | ds = getdoc(obj) | ||
if formatter: | ||||
ds = formatter(ds) | ||||
MinRK
|
r4249 | if ds: | ||
MinRK
|
r15712 | lines.append(head("Class docstring:")) | ||
MinRK
|
r4249 | lines.append(indent(ds)) | ||
if inspect.isclass(obj) and hasattr(obj, '__init__'): | ||||
Ville M. Vainio
|
r1032 | init_ds = getdoc(obj.__init__) | ||
MinRK
|
r4249 | if init_ds is not None: | ||
MinRK
|
r15712 | lines.append(head("Init docstring:")) | ||
MinRK
|
r4249 | lines.append(indent(init_ds)) | ||
Thomas Kluyver
|
r4896 | elif hasattr(obj,'__call__'): | ||
Ville M. Vainio
|
r1032 | call_ds = getdoc(obj.__call__) | ||
if call_ds: | ||||
MinRK
|
r15712 | lines.append(head("Call docstring:")) | ||
MinRK
|
r4249 | lines.append(indent(call_ds)) | ||
if not lines: | ||||
Ville M. Vainio
|
r1032 | self.noinfo('documentation',oname) | ||
MinRK
|
r4249 | else: | ||
page.page('\n'.join(lines)) | ||||
Bernardo B. Marques
|
r4872 | |||
immerrr
|
r17023 | def psource(self, obj, oname=''): | ||
Ville M. Vainio
|
r1032 | """Print the source code for an object.""" | ||
# Flush the source cache because inspect can return out-of-date source | ||||
linecache.checkcache() | ||||
try: | ||||
immerrr
|
r17023 | src = getsource(obj, oname=oname) | ||
except Exception: | ||||
src = None | ||||
if src is None: | ||||
self.noinfo('source', oname) | ||||
Ville M. Vainio
|
r1032 | else: | ||
jstenar
|
r8312 | page.page(self.format(src)) | ||
Ville M. Vainio
|
r1032 | |||
Fernando Perez
|
r7290 | def pfile(self, obj, oname=''): | ||
Ville M. Vainio
|
r1032 | """Show the whole file where an object was defined.""" | ||
Fernando Perez
|
r7290 | |||
lineno = find_source_lines(obj) | ||||
if lineno is None: | ||||
self.noinfo('file', oname) | ||||
Fernando Perez
|
r1228 | return | ||
Fernando Perez
|
r7290 | ofile = find_file(obj) | ||
# run contents of file through pager starting at line where the object | ||||
# is defined, as long as the file isn't binary and is actually on the | ||||
# filesystem. | ||||
Thomas Kluyver
|
r3929 | if ofile.endswith(('.so', '.dll', '.pyd')): | ||
Matthias BUSSONNIER
|
r7817 | print('File %r is binary, not printing.' % ofile) | ||
Fernando Perez
|
r1228 | elif not os.path.isfile(ofile): | ||
Matthias BUSSONNIER
|
r7817 | print('File %r does not exist, not printing.' % ofile) | ||
Ville M. Vainio
|
r1032 | else: | ||
Fernando Perez
|
r1228 | # Print only text files, not extension binaries. Note that | ||
# getsourcelines returns lineno with 1-offset and page() uses | ||||
# 0-offset, so we must adjust. | ||||
Jörgen Stenarson
|
r8304 | page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1) | ||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r15711 | def _format_fields(self, fields, title_width=0): | ||
Thomas Kluyver
|
r3856 | """Formats a list of fields for display. | ||
Bernardo B. Marques
|
r4872 | |||
Thomas Kluyver
|
r3856 | Parameters | ||
---------- | ||||
fields : list | ||||
A list of 2-tuples: (field_title, field_content) | ||||
title_width : int | ||||
MinRK
|
r15711 | How many characters to pad titles to. Default to longest title. | ||
Thomas Kluyver
|
r3856 | """ | ||
out = [] | ||||
header = self.__head | ||||
MinRK
|
r15711 | if title_width == 0: | ||
MinRK
|
r15735 | title_width = max(len(title) + 2 for title, _ in fields) | ||
Thomas Kluyver
|
r3856 | for title, content in fields: | ||
if len(content.splitlines()) > 1: | ||||
title = header(title + ":") + "\n" | ||||
else: | ||||
title = header((title+":").ljust(title_width)) | ||||
jstenar
|
r8312 | out.append(cast_unicode(title) + cast_unicode(content)) | ||
Thomas Kluyver
|
r3856 | return "\n".join(out) | ||
# The fields to be displayed by pinfo: (fancy_name, key_in_info_dict) | ||||
pinfo_fields1 = [("Type", "type_name"), | ||||
Thomas Kluyver
|
r7684 | ] | ||
MinRK
|
r15712 | pinfo_fields2 = [("String form", "string_form"), | ||
Thomas Kluyver
|
r7684 | ] | ||
pinfo_fields3 = [("Length", "length"), | ||||
Thomas Kluyver
|
r3856 | ("File", "file"), | ||
Thomas Kluyver
|
r7684 | ("Definition", "definition"), | ||
] | ||||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r15712 | pinfo_fields_obj = [("Class docstring", "class_docstring"), | ||
("Init docstring", "init_docstring"), | ||||
Thomas Kluyver
|
r3856 | ("Call def", "call_def"), | ||
("Call docstring", "call_docstring")] | ||||
MinRK
|
r16579 | |||
def _format_info(self, obj, oname='', formatter=None, info=None, detail_level=0): | ||||
"""Format an info dict as text""" | ||||
Thomas Kluyver
|
r3856 | info = self.info(obj, oname=oname, formatter=formatter, | ||
info=info, detail_level=detail_level) | ||||
displayfields = [] | ||||
Thomas Kluyver
|
r7684 | def add_fields(fields): | ||
for title, key in fields: | ||||
field = info[key] | ||||
if field is not None: | ||||
displayfields.append((title, field.rstrip())) | ||||
add_fields(self.pinfo_fields1) | ||||
# Base class for old-style instances | ||||
if (not py3compat.PY3) and isinstance(obj, types.InstanceType) and info['base_class']: | ||||
Thomas Kluyver
|
r7685 | displayfields.append(("Base Class", info['base_class'].rstrip())) | ||
Thomas Kluyver
|
r7684 | |||
add_fields(self.pinfo_fields2) | ||||
# Namespace | ||||
if info['namespace'] != 'Interactive': | ||||
Thomas Kluyver
|
r7685 | displayfields.append(("Namespace", info['namespace'].rstrip())) | ||
Bernardo B. Marques
|
r4872 | |||
Thomas Kluyver
|
r7684 | add_fields(self.pinfo_fields3) | ||
MinRK
|
r15711 | if info['isclass'] and info['init_definition']: | ||
MinRK
|
r15712 | displayfields.append(("Init definition", | ||
MinRK
|
r15711 | info['init_definition'].rstrip())) | ||
Thomas Kluyver
|
r7684 | |||
Thomas Kluyver
|
r3856 | # Source or docstring, depending on detail level and whether | ||
# source found. | ||||
if detail_level > 0 and info['source'] is not None: | ||||
jstenar
|
r8312 | displayfields.append(("Source", | ||
self.format(cast_unicode(info['source'])))) | ||||
Thomas Kluyver
|
r3856 | elif info['docstring'] is not None: | ||
displayfields.append(("Docstring", info["docstring"])) | ||||
Bernardo B. Marques
|
r4872 | |||
Thomas Kluyver
|
r3856 | # Constructor info for classes | ||
if info['isclass']: | ||||
MinRK
|
r15711 | if info['init_docstring'] is not None: | ||
MinRK
|
r15712 | displayfields.append(("Init docstring", | ||
MinRK
|
r15711 | info['init_docstring'])) | ||
Bernardo B. Marques
|
r4872 | |||
Thomas Kluyver
|
r3856 | # Info for objects: | ||
Ville M. Vainio
|
r1032 | else: | ||
Thomas Kluyver
|
r7684 | add_fields(self.pinfo_fields_obj) | ||
MinRK
|
r16579 | |||
Thomas Kluyver
|
r3856 | if displayfields: | ||
MinRK
|
r16579 | return self._format_fields(displayfields) | ||
else: | ||||
return u'' | ||||
def pinfo(self, obj, oname='', formatter=None, info=None, detail_level=0): | ||||
"""Show detailed information about an object. | ||||
Optional arguments: | ||||
- oname: name of the variable pointing to the object. | ||||
Ville M. Vainio
|
r1032 | |||
MinRK
|
r16579 | - formatter: special formatter for docstrings (see pdoc) | ||
- info: a structure with some information fields which may have been | ||||
precomputed already. | ||||
- detail_level: if set to 1, more information is given. | ||||
""" | ||||
text = self._format_info(obj, oname, formatter, info, detail_level) | ||||
if text: | ||||
page.page(text) | ||||
Fernando Perez
|
r2931 | def info(self, obj, oname='', formatter=None, info=None, detail_level=0): | ||
"""Compute a dict with detailed information about an object. | ||||
Optional arguments: | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2931 | - oname: name of the variable pointing to the object. | ||
- formatter: special formatter for docstrings (see pdoc) | ||||
- info: a structure with some information fields which may have been | ||||
Thomas Kluyver
|
r12553 | precomputed already. | ||
Fernando Perez
|
r2931 | |||
- detail_level: if set to 1, more information is given. | ||||
""" | ||||
obj_type = type(obj) | ||||
if info is None: | ||||
ismagic = 0 | ||||
isalias = 0 | ||||
ospace = '' | ||||
else: | ||||
ismagic = info.ismagic | ||||
isalias = info.isalias | ||||
ospace = info.namespace | ||||
Fernando Perez
|
r3051 | |||
Fernando Perez
|
r2931 | # Get docstring, special-casing aliases: | ||
if isalias: | ||||
if not callable(obj): | ||||
try: | ||||
ds = "Alias to the system command:\n %s" % obj[1] | ||||
except: | ||||
ds = "Alias: " + str(obj) | ||||
else: | ||||
ds = "Alias to " + str(obj) | ||||
if obj.__doc__: | ||||
ds += "\nDocstring:\n" + obj.__doc__ | ||||
else: | ||||
ds = getdoc(obj) | ||||
if ds is None: | ||||
ds = '<no docstring>' | ||||
if formatter is not None: | ||||
ds = formatter(ds) | ||||
Fernando Perez
|
r3051 | # store output in a dict, we initialize it here and fill it as we go | ||
out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic) | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2931 | string_max = 200 # max size of strings to show (snipped if longer) | ||
shalf = int((string_max -5)/2) | ||||
if ismagic: | ||||
obj_type_name = 'Magic function' | ||||
elif isalias: | ||||
obj_type_name = 'System alias' | ||||
else: | ||||
obj_type_name = obj_type.__name__ | ||||
out['type_name'] = obj_type_name | ||||
try: | ||||
bclass = obj.__class__ | ||||
out['base_class'] = str(bclass) | ||||
except: pass | ||||
# String form, but snip if too long in ? form (full in ??) | ||||
if detail_level >= self.str_detail_level: | ||||
try: | ||||
ostr = str(obj) | ||||
str_head = 'string_form' | ||||
if not detail_level and len(ostr)>string_max: | ||||
ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:] | ||||
ostr = ("\n" + " " * len(str_head.expandtabs())).\ | ||||
Thomas Kluyver
|
r3160 | join(q.strip() for q in ostr.split("\n")) | ||
Fernando Perez
|
r2931 | out[str_head] = ostr | ||
except: | ||||
pass | ||||
if ospace: | ||||
out['namespace'] = ospace | ||||
# Length (for strings and lists) | ||||
try: | ||||
out['length'] = str(len(obj)) | ||||
except: pass | ||||
# Filename where object was defined | ||||
binary_file = False | ||||
Fernando Perez
|
r7290 | fname = find_file(obj) | ||
if fname is None: | ||||
Fernando Perez
|
r2931 | # if anything goes wrong, we don't want to show source, so it's as | ||
# if the file was binary | ||||
binary_file = True | ||||
Fernando Perez
|
r7290 | else: | ||
if fname.endswith(('.so', '.dll', '.pyd')): | ||||
binary_file = True | ||||
elif fname.endswith('<string>'): | ||||
fname = 'Dynamically generated function. No source code available.' | ||||
out['file'] = fname | ||||
Bernardo B. Marques
|
r4872 | |||
immerrr
|
r17023 | # Original source code for a callable, class or property. | ||
Fernando Perez
|
r2931 | if detail_level: | ||
# Flush the source cache because inspect can return out-of-date | ||||
# source | ||||
linecache.checkcache() | ||||
try: | ||||
immerrr
|
r17023 | if isinstance(obj, property) or not binary_file: | ||
src = getsource(obj, oname) | ||||
if src is not None: | ||||
src = src.rstrip() | ||||
out['source'] = src | ||||
Thomas Kluyver
|
r3856 | except Exception: | ||
Thomas Kluyver
|
r3857 | pass | ||
Bernardo B. Marques
|
r4872 | |||
immerrr
|
r17023 | # Add docstring only if no source is to be shown (avoid repetitions). | ||
if ds and out.get('source', None) is None: | ||||
out['docstring'] = ds | ||||
Fernando Perez
|
r2931 | |||
# Constructor docstring for classes | ||||
if inspect.isclass(obj): | ||||
Thomas Kluyver
|
r3856 | out['isclass'] = True | ||
Fernando Perez
|
r2931 | # reconstruct the function definition and print it: | ||
try: | ||||
obj_init = obj.__init__ | ||||
except AttributeError: | ||||
init_def = init_ds = None | ||||
else: | ||||
init_def = self._getdef(obj_init,oname) | ||||
init_ds = getdoc(obj_init) | ||||
# Skip Python's auto-generated docstrings | ||||
MinRK
|
r14835 | if init_ds == _object_init_docstring: | ||
Fernando Perez
|
r2931 | init_ds = None | ||
if init_def or init_ds: | ||||
if init_def: | ||||
out['init_definition'] = self.format(init_def) | ||||
if init_ds: | ||||
Fernando Perez
|
r3051 | out['init_docstring'] = init_ds | ||
Fernando Perez
|
r2931 | # and class docstring for instances: | ||
Thomas Kluyver
|
r3856 | else: | ||
MinRK
|
r15711 | # reconstruct the function definition and print it: | ||
defln = self._getdef(obj, oname) | ||||
if defln: | ||||
out['definition'] = self.format(defln) | ||||
Fernando Perez
|
r2931 | # First, check whether the instance docstring is identical to the | ||
# class one, and print it separately if they don't coincide. In | ||||
# most cases they will, but it's nice to print all the info for | ||||
# objects which use instance-customized docstrings. | ||||
if ds: | ||||
try: | ||||
cls = getattr(obj,'__class__') | ||||
except: | ||||
class_ds = None | ||||
else: | ||||
class_ds = getdoc(cls) | ||||
# Skip Python's auto-generated docstrings | ||||
MinRK
|
r14835 | if class_ds in _builtin_type_docstrings: | ||
Fernando Perez
|
r2931 | class_ds = None | ||
if class_ds and ds != class_ds: | ||||
Fernando Perez
|
r3051 | out['class_docstring'] = class_ds | ||
Fernando Perez
|
r2931 | |||
# Next, try to show constructor docstrings | ||||
try: | ||||
init_ds = getdoc(obj.__init__) | ||||
# Skip Python's auto-generated docstrings | ||||
MinRK
|
r14835 | if init_ds == _object_init_docstring: | ||
Fernando Perez
|
r2931 | init_ds = None | ||
except AttributeError: | ||||
init_ds = None | ||||
if init_ds: | ||||
Fernando Perez
|
r3051 | out['init_docstring'] = init_ds | ||
Fernando Perez
|
r2931 | |||
# Call form docstring for callable instances | ||||
Thomas Kluyver
|
r15362 | if safe_hasattr(obj, '__call__') and not is_simple_callable(obj): | ||
Fernando Perez
|
r3051 | call_def = self._getdef(obj.__call__, oname) | ||
MinRK
|
r15711 | if call_def: | ||
call_def = self.format(call_def) | ||||
# it may never be the case that call def and definition differ, | ||||
# but don't include the same signature twice | ||||
if call_def != out.get('definition'): | ||||
out['call_def'] = call_def | ||||
Fernando Perez
|
r2931 | call_ds = getdoc(obj.__call__) | ||
# Skip Python's auto-generated docstrings | ||||
MinRK
|
r14835 | if call_ds == _func_call_docstring: | ||
Fernando Perez
|
r2931 | call_ds = None | ||
if call_ds: | ||||
Fernando Perez
|
r3051 | out['call_docstring'] = call_ds | ||
# Compute the object's argspec as a callable. The key is to decide | ||||
# whether to pull it from the object itself, from its __init__ or | ||||
# from its __call__ method. | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r3051 | if inspect.isclass(obj): | ||
Thomas Kluyver
|
r3868 | # Old-style classes need not have an __init__ | ||
callable_obj = getattr(obj, "__init__", None) | ||||
Fernando Perez
|
r3051 | elif callable(obj): | ||
callable_obj = obj | ||||
else: | ||||
callable_obj = None | ||||
if callable_obj: | ||||
try: | ||||
Thomas Kluyver
|
r15335 | argspec = getargspec(callable_obj) | ||
Fernando Perez
|
r3051 | except (TypeError, AttributeError): | ||
# For extensions/builtins we can't retrieve the argspec | ||||
pass | ||||
else: | ||||
Thomas Kluyver
|
r15335 | # named tuples' _asdict() method returns an OrderedDict, but we | ||
# we want a normal | ||||
out['argspec'] = argspec_dict = dict(argspec._asdict()) | ||||
# We called this varkw before argspec became a named tuple. | ||||
# With getfullargspec it's also called varkw. | ||||
if 'varkw' not in argspec_dict: | ||||
argspec_dict['varkw'] = argspec_dict.pop('keywords') | ||||
Fernando Perez
|
r2931 | |||
Fernando Perez
|
r3051 | return object_info(**out) | ||
Fernando Perez
|
r2931 | |||
Ville M. Vainio
|
r1032 | def psearch(self,pattern,ns_table,ns_search=[], | ||
ignore_case=False,show_all=False): | ||||
"""Search namespaces with wildcards for objects. | ||||
Arguments: | ||||
- pattern: string containing shell-like wildcards to use in namespace | ||||
Thomas Kluyver
|
r12553 | searches and optionally a type specification to narrow the search to | ||
objects of that type. | ||||
Ville M. Vainio
|
r1032 | |||
- ns_table: dict of name->namespaces for search. | ||||
Optional arguments: | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1032 | - ns_search: list of namespace names to include in search. | ||
- ignore_case(False): make the search case-insensitive. | ||||
- show_all(False): show all names, including those starting with | ||||
Thomas Kluyver
|
r12553 | underscores. | ||
Ville M. Vainio
|
r1032 | """ | ||
#print 'ps pattern:<%r>' % pattern # dbg | ||||
Bernardo B. Marques
|
r4872 | |||
Ville M. Vainio
|
r1032 | # defaults | ||
type_pattern = 'all' | ||||
filter = '' | ||||
cmds = pattern.split() | ||||
len_cmds = len(cmds) | ||||
if len_cmds == 1: | ||||
# Only filter pattern given | ||||
filter = cmds[0] | ||||
elif len_cmds == 2: | ||||
# Both filter and type specified | ||||
filter,type_pattern = cmds | ||||
else: | ||||
raise ValueError('invalid argument string for psearch: <%s>' % | ||||
pattern) | ||||
# filter search namespaces | ||||
for name in ns_search: | ||||
if name not in ns_table: | ||||
raise ValueError('invalid namespace <%s>. Valid names: %s' % | ||||
(name,ns_table.keys())) | ||||
#print 'type_pattern:',type_pattern # dbg | ||||
Thomas Kluyver
|
r5550 | search_result, namespaces_seen = set(), set() | ||
Ville M. Vainio
|
r1032 | for ns_name in ns_search: | ||
ns = ns_table[ns_name] | ||||
Thomas Kluyver
|
r5550 | # Normally, locals and globals are the same, so we just check one. | ||
if id(ns) in namespaces_seen: | ||||
continue | ||||
namespaces_seen.add(id(ns)) | ||||
tmp_res = list_namespace(ns, type_pattern, filter, | ||||
ignore_case=ignore_case, show_all=show_all) | ||||
search_result.update(tmp_res) | ||||
page.page('\n'.join(sorted(search_result))) | ||||