aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/ipython/py3/IPython/core/oinspect.py
diff options
context:
space:
mode:
authorshadchin <shadchin@yandex-team.ru>2022-02-10 16:44:39 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:44:39 +0300
commite9656aae26e0358d5378e5b63dcac5c8dbe0e4d0 (patch)
tree64175d5cadab313b3e7039ebaa06c5bc3295e274 /contrib/python/ipython/py3/IPython/core/oinspect.py
parent2598ef1d0aee359b4b6d5fdd1758916d5907d04f (diff)
downloadydb-e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0.tar.gz
Restoring authorship annotation for <shadchin@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/python/ipython/py3/IPython/core/oinspect.py')
-rw-r--r--contrib/python/ipython/py3/IPython/core/oinspect.py2062
1 files changed, 1031 insertions, 1031 deletions
diff --git a/contrib/python/ipython/py3/IPython/core/oinspect.py b/contrib/python/ipython/py3/IPython/core/oinspect.py
index e6297b98df..272916c966 100644
--- a/contrib/python/ipython/py3/IPython/core/oinspect.py
+++ b/contrib/python/ipython/py3/IPython/core/oinspect.py
@@ -1,1031 +1,1031 @@
-# -*- 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.
-"""
-
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-
-__all__ = ['Inspector','InspectColors']
-
-# stdlib modules
-import ast
-import inspect
-from inspect import signature
-import linecache
-import warnings
-import os
-from textwrap import dedent
-import types
-import io as stdlib_io
-
-from typing import Union
-
-# IPython's own
-from IPython.core import page
-from IPython.lib.pretty import pretty
-from IPython.testing.skipdoctest import skip_doctest
-from IPython.utils import PyColorize
-from IPython.utils import openpy
-from IPython.utils import py3compat
-from IPython.utils.dir2 import safe_hasattr
-from IPython.utils.path import compress_user
-from IPython.utils.text import indent
-from IPython.utils.wildcard import list_namespace
-from IPython.utils.wildcard import typestr2type
-from IPython.utils.coloransi import TermColors, ColorScheme, ColorSchemeTable
-from IPython.utils.py3compat import cast_unicode
-from IPython.utils.colorable import Colorable
-from IPython.utils.decorators import undoc
-
-from pygments import highlight
-from pygments.lexers import PythonLexer
-from pygments.formatters import HtmlFormatter
-
-def pylight(code):
- return highlight(code, PythonLexer(), HtmlFormatter(noclasses=True))
-
-# builtin docstrings to ignore
-_func_call_docstring = types.FunctionType.__call__.__doc__
-_object_init_docstring = object.__init__.__doc__
-_builtin_type_docstrings = {
- inspect.getdoc(t) for t in (types.ModuleType, types.MethodType,
- types.FunctionType, property)
-}
-
-_builtin_func_type = type(all)
-_builtin_meth_type = type(str.upper) # Bound methods have the same type as builtin functions
-#****************************************************************************
-# Builtin color schemes
-
-Colors = TermColors # just a shorthand
-
-InspectColors = PyColorize.ANSICodeColors
-
-#****************************************************************************
-# 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
- 'ismagic', 'isalias', 'isclass', 'found', 'name'
- ]
-
-
-def object_info(**kw):
- """Make an object info dict with all fields present."""
- infodict = {k:None for k in info_fields}
- infodict.update(kw)
- return infodict
-
-
-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.
- with stdlib_io.open(ofile, 'rb') as buffer: # Tweaked to use io.open for Python 2
- encoding, lines = openpy.detect_encoding(buffer.readline)
- return encoding
-
-def getdoc(obj) -> Union[str,None]:
- """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:
- ds = obj.getdoc()
- except Exception:
- pass
- else:
- if isinstance(ds, str):
- return inspect.cleandoc(ds)
- docstr = inspect.getdoc(obj)
- return docstr
-
-
-def getsource(obj, oname='') -> Union[str,None]:
- """Wrapper around inspect.getsource.
-
- This can be modified by other projects to provide customized source
- extraction.
-
- Parameters
- ----------
- obj : object
- an object whose source code we will attempt to extract
- oname : str
- (optional) a name under which the object is known
-
- Returns
- -------
- src : unicode or None
-
- """
-
- 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(''.join(('# ', oname_prefix, attrname)))
- 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(
- '%s%s = %s\n' % (oname_prefix, attrname, pretty(fn))
- )
- if sources:
- return '\n'.join(sources)
- else:
- return None
-
- else:
- # Get source for non-property objects.
-
- obj = _get_wrapped(obj)
-
- try:
- src = inspect.getsource(obj)
- except TypeError:
- # The object itself provided no meaningful source, try looking for
- # its class definition instead.
- try:
- src = inspect.getsource(obj.__class__)
- except (OSError, TypeError):
- return None
- except OSError:
- return None
-
- return src
-
-
-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))
-
-@undoc
-def getargspec(obj):
- """Wrapper around :func:`inspect.getfullargspec`
-
- In addition to functions and methods, this can also handle objects with a
- ``__call__`` attribute.
-
- DEPRECATED: Deprecated since 7.10. Do not use, will be removed.
- """
-
- warnings.warn('`getargspec` function is deprecated as of IPython 7.10'
- 'and will be removed in future versions.', DeprecationWarning, stacklevel=2)
-
- if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
- obj = obj.__call__
-
- return inspect.getfullargspec(obj)
-
-@undoc
-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.
-
- DEPRECATED: Do not use; will be removed in future versions.
- """
-
- warnings.warn('`format_argspec` function is deprecated as of IPython 7.10'
- 'and will be removed in future versions.', DeprecationWarning, stacklevel=2)
-
-
- return inspect.formatargspec(argspec['args'], argspec['varargs'],
- argspec['varkw'], argspec['defaults'])
-
-@undoc
-def call_tip(oinfo, format_call=True):
- """DEPRECATED. Extract call tip data from an oinfo dict.
- """
- warnings.warn('`call_tip` function is deprecated as of IPython 6.0'
- 'and will be removed in future versions.', DeprecationWarning, stacklevel=2)
- # Get call definition
- argspec = oinfo.get('argspec')
- 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:]
-
- call_line = oinfo['name']+format_argspec(argspec)
-
- # Now get docstring.
- # The priority is: call docstring, constructor docstring, main one.
- doc = oinfo.get('call_docstring')
- if doc is None:
- doc = oinfo.get('init_docstring')
- if doc is None:
- doc = oinfo.get('docstring','')
-
- return call_line, doc
-
-
-def _get_wrapped(obj):
- """Get the original object if wrapped in one or more @decorators
-
- Some objects automatically construct similar objects on any unrecognised
- attribute access (e.g. unittest.mock.call). To protect against infinite loops,
- this will arbitrarily cut off after 100 levels of obj.__wrapped__
- attribute access. --TK, Jan 2016
- """
- orig_obj = obj
- i = 0
- while safe_hasattr(obj, '__wrapped__'):
- obj = obj.__wrapped__
- i += 1
- if i > 100:
- # __wrapped__ is probably a lie, so return the thing we started with
- return orig_obj
- return obj
-
-def find_file(obj) -> str:
- """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.
- """
- obj = _get_wrapped(obj)
-
- fname = None
- try:
- fname = inspect.getabsfile(obj)
- except TypeError:
- # For an instance, the file that matters is where its class was
- # declared.
- try:
- fname = inspect.getabsfile(obj.__class__)
- except (OSError, TypeError):
- # Can happen for builtins
- pass
- except OSError:
- pass
-
- return cast_unicode(fname)
-
-
-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.
- """
- obj = _get_wrapped(obj)
-
- try:
- lineno = inspect.getsourcelines(obj)[1]
- except TypeError:
- # For instances, try the class object like getsource() does
- try:
- lineno = inspect.getsourcelines(obj.__class__)[1]
- except (OSError, TypeError):
- return None
- except OSError:
- return None
-
- return lineno
-
-class Inspector(Colorable):
-
- def __init__(self, color_table=InspectColors,
- code_color_table=PyColorize.ANSICodeColors,
- scheme=None,
- str_detail_level=0,
- parent=None, config=None):
- super(Inspector, self).__init__(parent=parent, config=config)
- self.color_table = color_table
- self.parser = PyColorize.Parser(out='str', parent=self, style=scheme)
- self.format = self.parser.format
- self.str_detail_level = str_detail_level
- self.set_active_scheme(scheme)
-
- def _getdef(self,obj,oname='') -> Union[str,None]:
- """Return the call signature for any callable object.
-
- If any exception is generated, None is returned instead and the
- exception is suppressed."""
- try:
- return _render_signature(signature(obj), oname)
- except:
- return None
-
- def __head(self,h) -> str:
- """Return a header string with proper colors."""
- return '%s%s%s' % (self.color_table.active_colors.header,h,
- self.color_table.active_colors.normal)
-
- def set_active_scheme(self, scheme):
- if scheme is not None:
- self.color_table.set_active_scheme(scheme)
- self.parser.color_table.set_active_scheme(scheme)
-
- def noinfo(self, msg, oname):
- """Generic message when no information is found."""
- print('No %s found' % msg, end=' ')
- if oname:
- print('for %s' % oname)
- else:
- print()
-
- def pdef(self, obj, oname=''):
- """Print the call signature for any callable object.
-
- If the object is a class, print the constructor information."""
-
- if not callable(obj):
- print('Object is not callable.')
- return
-
- header = ''
-
- if inspect.isclass(obj):
- header = self.__head('Class constructor information:\n')
-
-
- output = self._getdef(obj,oname)
- if output is None:
- self.noinfo('definition header',oname)
- else:
- print(header,self.format(output), end=' ')
-
- # In Python 3, all classes are new-style, so they all have __init__.
- @skip_doctest
- def pdoc(self, obj, oname='', formatter=None):
- """Print the docstring for any object.
-
- Optional:
- -formatter: a function to run the docstring through for specially
- formatted docstrings.
-
- Examples
- --------
-
- In [1]: class NoInit:
- ...: pass
-
- In [2]: class NoDoc:
- ...: def __init__(self):
- ...: pass
-
- In [3]: %pdoc NoDoc
- No documentation found for NoDoc
-
- In [4]: %pdoc NoInit
- No documentation found for NoInit
-
- In [5]: obj = NoInit()
-
- In [6]: %pdoc obj
- No documentation found for obj
-
- In [5]: obj2 = NoDoc()
-
- In [6]: %pdoc obj2
- No documentation found for obj2
- """
-
- head = self.__head # For convenience
- lines = []
- ds = getdoc(obj)
- if formatter:
- ds = formatter(ds).get('plain/text', ds)
- if ds:
- lines.append(head("Class docstring:"))
- lines.append(indent(ds))
- if inspect.isclass(obj) and hasattr(obj, '__init__'):
- init_ds = getdoc(obj.__init__)
- if init_ds is not None:
- lines.append(head("Init docstring:"))
- lines.append(indent(init_ds))
- elif hasattr(obj,'__call__'):
- call_ds = getdoc(obj.__call__)
- if call_ds:
- lines.append(head("Call docstring:"))
- lines.append(indent(call_ds))
-
- if not lines:
- self.noinfo('documentation',oname)
- else:
- page.page('\n'.join(lines))
-
- def psource(self, obj, oname=''):
- """Print the source code for an object."""
-
- # Flush the source cache because inspect can return out-of-date source
- linecache.checkcache()
- try:
- src = getsource(obj, oname=oname)
- except Exception:
- src = None
-
- if src is None:
- self.noinfo('source', oname)
- else:
- page.page(self.format(src))
-
- def pfile(self, obj, oname=''):
- """Show the whole file where an object was defined."""
-
- lineno = find_source_lines(obj)
- if lineno is None:
- self.noinfo('file', oname)
- return
-
- 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.endswith(('.so', '.dll', '.pyd')):
- print('File %r is binary, not printing.' % ofile)
- elif not os.path.isfile(ofile):
- print('File %r does not exist, not printing.' % ofile)
- 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.
- page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1)
-
-
- def _mime_format(self, text:str, formatter=None) -> dict:
- """Return a mime bundle representation of the input text.
-
- - if `formatter` is None, the returned mime bundle has
- a `text/plain` field, with the input text.
- a `text/html` field with a `<pre>` tag containing the input text.
-
- - if `formatter` is not None, it must be a callable transforming the
- input text into a mime bundle. Default values for `text/plain` and
- `text/html` representations are the ones described above.
-
- Note:
-
- Formatters returning strings are supported but this behavior is deprecated.
-
- """
- defaults = {
- 'text/plain': text,
- 'text/html': '<pre>' + text + '</pre>'
- }
-
- if formatter is None:
- return defaults
- else:
- formatted = formatter(text)
-
- if not isinstance(formatted, dict):
- # Handle the deprecated behavior of a formatter returning
- # a string instead of a mime bundle.
- return {
- 'text/plain': formatted,
- 'text/html': '<pre>' + formatted + '</pre>'
- }
-
- else:
- return dict(defaults, **formatted)
-
-
- def format_mime(self, bundle):
-
- text_plain = bundle['text/plain']
-
- text = ''
- heads, bodies = list(zip(*text_plain))
- _len = max(len(h) for h in heads)
-
- for head, body in zip(heads, bodies):
- body = body.strip('\n')
- delim = '\n' if '\n' in body else ' '
- text += self.__head(head+':') + (_len - len(head))*' ' +delim + body +'\n'
-
- bundle['text/plain'] = text
- return bundle
-
- def _get_info(self, obj, oname='', formatter=None, info=None, detail_level=0):
- """Retrieve an info dict and format it.
-
- Parameters
- ==========
-
- obj: any
- Object to inspect and return info from
- oname: str (default: ''):
- Name of the variable pointing to `obj`.
- formatter: callable
- info:
- already computed information
- detail_level: integer
- Granularity of detail level, if set to 1, give more information.
- """
-
- info = self._info(obj, oname=oname, info=info, detail_level=detail_level)
-
- _mime = {
- 'text/plain': [],
- 'text/html': '',
- }
-
- def append_field(bundle, title:str, key:str, formatter=None):
- field = info[key]
- if field is not None:
- formatted_field = self._mime_format(field, formatter)
- bundle['text/plain'].append((title, formatted_field['text/plain']))
- bundle['text/html'] += '<h1>' + title + '</h1>\n' + formatted_field['text/html'] + '\n'
-
- def code_formatter(text):
- return {
- 'text/plain': self.format(text),
- 'text/html': pylight(text)
- }
-
- if info['isalias']:
- append_field(_mime, 'Repr', 'string_form')
-
- elif info['ismagic']:
- if detail_level > 0:
- append_field(_mime, 'Source', 'source', code_formatter)
- else:
- append_field(_mime, 'Docstring', 'docstring', formatter)
- append_field(_mime, 'File', 'file')
-
- elif info['isclass'] or is_simple_callable(obj):
- # Functions, methods, classes
- append_field(_mime, 'Signature', 'definition', code_formatter)
- append_field(_mime, 'Init signature', 'init_definition', code_formatter)
- append_field(_mime, 'Docstring', 'docstring', formatter)
- if detail_level > 0 and info['source']:
- append_field(_mime, 'Source', 'source', code_formatter)
- else:
- append_field(_mime, 'Init docstring', 'init_docstring', formatter)
-
- append_field(_mime, 'File', 'file')
- append_field(_mime, 'Type', 'type_name')
- append_field(_mime, 'Subclasses', 'subclasses')
-
- else:
- # General Python objects
- append_field(_mime, 'Signature', 'definition', code_formatter)
- append_field(_mime, 'Call signature', 'call_def', code_formatter)
- append_field(_mime, 'Type', 'type_name')
- append_field(_mime, 'String form', 'string_form')
-
- # Namespace
- if info['namespace'] != 'Interactive':
- append_field(_mime, 'Namespace', 'namespace')
-
- append_field(_mime, 'Length', 'length')
- append_field(_mime, 'File', 'file')
-
- # Source or docstring, depending on detail level and whether
- # source found.
- if detail_level > 0 and info['source']:
- append_field(_mime, 'Source', 'source', code_formatter)
- else:
- append_field(_mime, 'Docstring', 'docstring', formatter)
-
- append_field(_mime, 'Class docstring', 'class_docstring', formatter)
- append_field(_mime, 'Init docstring', 'init_docstring', formatter)
- append_field(_mime, 'Call docstring', 'call_docstring', formatter)
-
-
- return self.format_mime(_mime)
-
- def pinfo(self, obj, oname='', formatter=None, info=None, detail_level=0, enable_html_pager=True):
- """Show detailed information about an object.
-
- Optional arguments:
-
- - oname: name of the variable pointing to the object.
-
- - formatter: callable (optional)
- A special formatter for docstrings.
-
- The formatter is a callable that takes a string as an input
- and returns either a formatted string or a mime type bundle
- in the form of a dictionary.
-
- Although the support of custom formatter returning a string
- instead of a mime type bundle is deprecated.
-
- - info: a structure with some information fields which may have been
- precomputed already.
-
- - detail_level: if set to 1, more information is given.
- """
- info = self._get_info(obj, oname, formatter, info, detail_level)
- if not enable_html_pager:
- del info['text/html']
- page.page(info)
-
- def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
- """DEPRECATED. Compute a dict with detailed information about an object.
- """
- if formatter is not None:
- warnings.warn('The `formatter` keyword argument to `Inspector.info`'
- 'is deprecated as of IPython 5.0 and will have no effects.',
- DeprecationWarning, stacklevel=2)
- return self._info(obj, oname=oname, info=info, detail_level=detail_level)
-
- def _info(self, obj, oname='', info=None, detail_level=0) -> dict:
- """Compute a dict with detailed information about an object.
-
- Parameters
- ==========
-
- obj: any
- An object to find information about
- oname: str (default: ''):
- Name of the variable pointing to `obj`.
- info: (default: None)
- A struct (dict like with attr access) with some information fields
- which may have been precomputed already.
- detail_level: int (default:0)
- If set to 1, more information is given.
-
- Returns
- =======
-
- An object info dict with known fields from `info_fields`. Keys are
- strings, values are string or None.
- """
-
- if info is None:
- ismagic = False
- isalias = False
- ospace = ''
- else:
- ismagic = info.ismagic
- isalias = info.isalias
- ospace = info.namespace
-
- # 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>'
-
- # 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, subclasses=None)
-
- string_max = 200 # max size of strings to show (snipped if longer)
- shalf = int((string_max - 5) / 2)
-
- if ismagic:
- out['type_name'] = 'Magic function'
- elif isalias:
- out['type_name'] = 'System alias'
- else:
- out['type_name'] = type(obj).__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())).\
- join(q.strip() for q in ostr.split("\n"))
- out[str_head] = ostr
- except:
- pass
-
- if ospace:
- out['namespace'] = ospace
-
- # Length (for strings and lists)
- try:
- out['length'] = str(len(obj))
- except Exception:
- pass
-
- # Filename where object was defined
- binary_file = False
- fname = find_file(obj)
- if fname is None:
- # if anything goes wrong, we don't want to show source, so it's as
- # if the file was binary
- binary_file = True
- else:
- if fname.endswith(('.so', '.dll', '.pyd')):
- binary_file = True
- elif fname.endswith('<string>'):
- fname = 'Dynamically generated function. No source code available.'
- out['file'] = compress_user(fname)
-
- # Original source code for a callable, class or property.
- if detail_level:
- # Flush the source cache because inspect can return out-of-date
- # source
- linecache.checkcache()
- try:
- if isinstance(obj, property) or not binary_file:
- src = getsource(obj, oname)
- if src is not None:
- src = src.rstrip()
- out['source'] = src
-
- except Exception:
- pass
-
- # Add docstring only if no source is to be shown (avoid repetitions).
- if ds and not self._source_contains_docstring(out.get('source'), ds):
- out['docstring'] = ds
-
- # Constructor docstring for classes
- if inspect.isclass(obj):
- out['isclass'] = True
-
- # get the init signature:
- try:
- init_def = self._getdef(obj, oname)
- except AttributeError:
- init_def = None
-
- # get the __init__ docstring
- try:
- obj_init = obj.__init__
- except AttributeError:
- init_ds = None
- else:
- if init_def is None:
- # Get signature from init if top-level sig failed.
- # Can happen for built-in types (list, etc.).
- try:
- init_def = self._getdef(obj_init, oname)
- except AttributeError:
- pass
- init_ds = getdoc(obj_init)
- # Skip Python's auto-generated docstrings
- if init_ds == _object_init_docstring:
- init_ds = None
-
- if init_def:
- out['init_definition'] = init_def
-
- if init_ds:
- out['init_docstring'] = init_ds
-
- names = [sub.__name__ for sub in type.__subclasses__(obj)]
- if len(names) < 10:
- all_names = ', '.join(names)
- else:
- all_names = ', '.join(names[:10]+['...'])
- out['subclasses'] = all_names
- # and class docstring for instances:
- else:
- # reconstruct the function definition and print it:
- defln = self._getdef(obj, oname)
- if defln:
- out['definition'] = defln
-
- # 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
- if class_ds in _builtin_type_docstrings:
- class_ds = None
- if class_ds and ds != class_ds:
- out['class_docstring'] = class_ds
-
- # Next, try to show constructor docstrings
- try:
- init_ds = getdoc(obj.__init__)
- # Skip Python's auto-generated docstrings
- if init_ds == _object_init_docstring:
- init_ds = None
- except AttributeError:
- init_ds = None
- if init_ds:
- out['init_docstring'] = init_ds
-
- # Call form docstring for callable instances
- if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
- call_def = self._getdef(obj.__call__, oname)
- if call_def and (call_def != out.get('definition')):
- # it may never be the case that call def and definition differ,
- # but don't include the same signature twice
- out['call_def'] = call_def
- call_ds = getdoc(obj.__call__)
- # Skip Python's auto-generated docstrings
- if call_ds == _func_call_docstring:
- call_ds = None
- if call_ds:
- out['call_docstring'] = call_ds
-
- return object_info(**out)
-
- @staticmethod
- def _source_contains_docstring(src, doc):
- """
- Check whether the source *src* contains the docstring *doc*.
-
- This is is helper function to skip displaying the docstring if the
- source already contains it, avoiding repetition of information.
- """
- try:
- def_node, = ast.parse(dedent(src)).body
- return ast.get_docstring(def_node) == doc
- except Exception:
- # The source can become invalid or even non-existent (because it
- # is re-fetched from the source file) so the above code fail in
- # arbitrary ways.
- return False
-
- def psearch(self,pattern,ns_table,ns_search=[],
- ignore_case=False,show_all=False, *, list_types=False):
- """Search namespaces with wildcards for objects.
-
- Arguments:
-
- - pattern: string containing shell-like wildcards to use in namespace
- searches and optionally a type specification to narrow the search to
- objects of that type.
-
- - ns_table: dict of name->namespaces for search.
-
- Optional arguments:
-
- - 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
- underscores.
-
- - list_types(False): list all available object types for object matching.
- """
- #print 'ps pattern:<%r>' % pattern # dbg
-
- # defaults
- type_pattern = 'all'
- filter = ''
-
- # list all object types
- if list_types:
- page.page('\n'.join(sorted(typestr2type)))
- return
-
- 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
- search_result, namespaces_seen = set(), set()
- for ns_name in ns_search:
- ns = ns_table[ns_name]
- # 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)))
-
-
-def _render_signature(obj_signature, obj_name) -> str:
- """
- This was mostly taken from inspect.Signature.__str__.
- Look there for the comments.
- The only change is to add linebreaks when this gets too long.
- """
- result = []
- pos_only = False
- kw_only = True
- for param in obj_signature.parameters.values():
- if param.kind == inspect._POSITIONAL_ONLY:
- pos_only = True
- elif pos_only:
- result.append('/')
- pos_only = False
-
- if param.kind == inspect._VAR_POSITIONAL:
- kw_only = False
- elif param.kind == inspect._KEYWORD_ONLY and kw_only:
- result.append('*')
- kw_only = False
-
- result.append(str(param))
-
- if pos_only:
- result.append('/')
-
- # add up name, parameters, braces (2), and commas
- if len(obj_name) + sum(len(r) + 2 for r in result) > 75:
- # This doesn’t fit behind “Signature: ” in an inspect window.
- rendered = '{}(\n{})'.format(obj_name, ''.join(
- ' {},\n'.format(r) for r in result)
- )
- else:
- rendered = '{}({})'.format(obj_name, ', '.join(result))
-
- if obj_signature.return_annotation is not inspect._empty:
- anno = inspect.formatannotation(obj_signature.return_annotation)
- rendered += ' -> {}'.format(anno)
-
- return rendered
+# -*- 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.
+"""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+__all__ = ['Inspector','InspectColors']
+
+# stdlib modules
+import ast
+import inspect
+from inspect import signature
+import linecache
+import warnings
+import os
+from textwrap import dedent
+import types
+import io as stdlib_io
+
+from typing import Union
+
+# IPython's own
+from IPython.core import page
+from IPython.lib.pretty import pretty
+from IPython.testing.skipdoctest import skip_doctest
+from IPython.utils import PyColorize
+from IPython.utils import openpy
+from IPython.utils import py3compat
+from IPython.utils.dir2 import safe_hasattr
+from IPython.utils.path import compress_user
+from IPython.utils.text import indent
+from IPython.utils.wildcard import list_namespace
+from IPython.utils.wildcard import typestr2type
+from IPython.utils.coloransi import TermColors, ColorScheme, ColorSchemeTable
+from IPython.utils.py3compat import cast_unicode
+from IPython.utils.colorable import Colorable
+from IPython.utils.decorators import undoc
+
+from pygments import highlight
+from pygments.lexers import PythonLexer
+from pygments.formatters import HtmlFormatter
+
+def pylight(code):
+ return highlight(code, PythonLexer(), HtmlFormatter(noclasses=True))
+
+# builtin docstrings to ignore
+_func_call_docstring = types.FunctionType.__call__.__doc__
+_object_init_docstring = object.__init__.__doc__
+_builtin_type_docstrings = {
+ inspect.getdoc(t) for t in (types.ModuleType, types.MethodType,
+ types.FunctionType, property)
+}
+
+_builtin_func_type = type(all)
+_builtin_meth_type = type(str.upper) # Bound methods have the same type as builtin functions
+#****************************************************************************
+# Builtin color schemes
+
+Colors = TermColors # just a shorthand
+
+InspectColors = PyColorize.ANSICodeColors
+
+#****************************************************************************
+# 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
+ 'ismagic', 'isalias', 'isclass', 'found', 'name'
+ ]
+
+
+def object_info(**kw):
+ """Make an object info dict with all fields present."""
+ infodict = {k:None for k in info_fields}
+ infodict.update(kw)
+ return infodict
+
+
+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.
+ with stdlib_io.open(ofile, 'rb') as buffer: # Tweaked to use io.open for Python 2
+ encoding, lines = openpy.detect_encoding(buffer.readline)
+ return encoding
+
+def getdoc(obj) -> Union[str,None]:
+ """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:
+ ds = obj.getdoc()
+ except Exception:
+ pass
+ else:
+ if isinstance(ds, str):
+ return inspect.cleandoc(ds)
+ docstr = inspect.getdoc(obj)
+ return docstr
+
+
+def getsource(obj, oname='') -> Union[str,None]:
+ """Wrapper around inspect.getsource.
+
+ This can be modified by other projects to provide customized source
+ extraction.
+
+ Parameters
+ ----------
+ obj : object
+ an object whose source code we will attempt to extract
+ oname : str
+ (optional) a name under which the object is known
+
+ Returns
+ -------
+ src : unicode or None
+
+ """
+
+ 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(''.join(('# ', oname_prefix, attrname)))
+ 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(
+ '%s%s = %s\n' % (oname_prefix, attrname, pretty(fn))
+ )
+ if sources:
+ return '\n'.join(sources)
+ else:
+ return None
+
+ else:
+ # Get source for non-property objects.
+
+ obj = _get_wrapped(obj)
+
+ try:
+ src = inspect.getsource(obj)
+ except TypeError:
+ # The object itself provided no meaningful source, try looking for
+ # its class definition instead.
+ try:
+ src = inspect.getsource(obj.__class__)
+ except (OSError, TypeError):
+ return None
+ except OSError:
+ return None
+
+ return src
+
+
+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))
+
+@undoc
+def getargspec(obj):
+ """Wrapper around :func:`inspect.getfullargspec`
+
+ In addition to functions and methods, this can also handle objects with a
+ ``__call__`` attribute.
+
+ DEPRECATED: Deprecated since 7.10. Do not use, will be removed.
+ """
+
+ warnings.warn('`getargspec` function is deprecated as of IPython 7.10'
+ 'and will be removed in future versions.', DeprecationWarning, stacklevel=2)
+
+ if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
+ obj = obj.__call__
+
+ return inspect.getfullargspec(obj)
+
+@undoc
+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.
+
+ DEPRECATED: Do not use; will be removed in future versions.
+ """
+
+ warnings.warn('`format_argspec` function is deprecated as of IPython 7.10'
+ 'and will be removed in future versions.', DeprecationWarning, stacklevel=2)
+
+
+ return inspect.formatargspec(argspec['args'], argspec['varargs'],
+ argspec['varkw'], argspec['defaults'])
+
+@undoc
+def call_tip(oinfo, format_call=True):
+ """DEPRECATED. Extract call tip data from an oinfo dict.
+ """
+ warnings.warn('`call_tip` function is deprecated as of IPython 6.0'
+ 'and will be removed in future versions.', DeprecationWarning, stacklevel=2)
+ # Get call definition
+ argspec = oinfo.get('argspec')
+ 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:]
+
+ call_line = oinfo['name']+format_argspec(argspec)
+
+ # Now get docstring.
+ # The priority is: call docstring, constructor docstring, main one.
+ doc = oinfo.get('call_docstring')
+ if doc is None:
+ doc = oinfo.get('init_docstring')
+ if doc is None:
+ doc = oinfo.get('docstring','')
+
+ return call_line, doc
+
+
+def _get_wrapped(obj):
+ """Get the original object if wrapped in one or more @decorators
+
+ Some objects automatically construct similar objects on any unrecognised
+ attribute access (e.g. unittest.mock.call). To protect against infinite loops,
+ this will arbitrarily cut off after 100 levels of obj.__wrapped__
+ attribute access. --TK, Jan 2016
+ """
+ orig_obj = obj
+ i = 0
+ while safe_hasattr(obj, '__wrapped__'):
+ obj = obj.__wrapped__
+ i += 1
+ if i > 100:
+ # __wrapped__ is probably a lie, so return the thing we started with
+ return orig_obj
+ return obj
+
+def find_file(obj) -> str:
+ """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.
+ """
+ obj = _get_wrapped(obj)
+
+ fname = None
+ try:
+ fname = inspect.getabsfile(obj)
+ except TypeError:
+ # For an instance, the file that matters is where its class was
+ # declared.
+ try:
+ fname = inspect.getabsfile(obj.__class__)
+ except (OSError, TypeError):
+ # Can happen for builtins
+ pass
+ except OSError:
+ pass
+
+ return cast_unicode(fname)
+
+
+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.
+ """
+ obj = _get_wrapped(obj)
+
+ try:
+ lineno = inspect.getsourcelines(obj)[1]
+ except TypeError:
+ # For instances, try the class object like getsource() does
+ try:
+ lineno = inspect.getsourcelines(obj.__class__)[1]
+ except (OSError, TypeError):
+ return None
+ except OSError:
+ return None
+
+ return lineno
+
+class Inspector(Colorable):
+
+ def __init__(self, color_table=InspectColors,
+ code_color_table=PyColorize.ANSICodeColors,
+ scheme=None,
+ str_detail_level=0,
+ parent=None, config=None):
+ super(Inspector, self).__init__(parent=parent, config=config)
+ self.color_table = color_table
+ self.parser = PyColorize.Parser(out='str', parent=self, style=scheme)
+ self.format = self.parser.format
+ self.str_detail_level = str_detail_level
+ self.set_active_scheme(scheme)
+
+ def _getdef(self,obj,oname='') -> Union[str,None]:
+ """Return the call signature for any callable object.
+
+ If any exception is generated, None is returned instead and the
+ exception is suppressed."""
+ try:
+ return _render_signature(signature(obj), oname)
+ except:
+ return None
+
+ def __head(self,h) -> str:
+ """Return a header string with proper colors."""
+ return '%s%s%s' % (self.color_table.active_colors.header,h,
+ self.color_table.active_colors.normal)
+
+ def set_active_scheme(self, scheme):
+ if scheme is not None:
+ self.color_table.set_active_scheme(scheme)
+ self.parser.color_table.set_active_scheme(scheme)
+
+ def noinfo(self, msg, oname):
+ """Generic message when no information is found."""
+ print('No %s found' % msg, end=' ')
+ if oname:
+ print('for %s' % oname)
+ else:
+ print()
+
+ def pdef(self, obj, oname=''):
+ """Print the call signature for any callable object.
+
+ If the object is a class, print the constructor information."""
+
+ if not callable(obj):
+ print('Object is not callable.')
+ return
+
+ header = ''
+
+ if inspect.isclass(obj):
+ header = self.__head('Class constructor information:\n')
+
+
+ output = self._getdef(obj,oname)
+ if output is None:
+ self.noinfo('definition header',oname)
+ else:
+ print(header,self.format(output), end=' ')
+
+ # In Python 3, all classes are new-style, so they all have __init__.
+ @skip_doctest
+ def pdoc(self, obj, oname='', formatter=None):
+ """Print the docstring for any object.
+
+ Optional:
+ -formatter: a function to run the docstring through for specially
+ formatted docstrings.
+
+ Examples
+ --------
+
+ In [1]: class NoInit:
+ ...: pass
+
+ In [2]: class NoDoc:
+ ...: def __init__(self):
+ ...: pass
+
+ In [3]: %pdoc NoDoc
+ No documentation found for NoDoc
+
+ In [4]: %pdoc NoInit
+ No documentation found for NoInit
+
+ In [5]: obj = NoInit()
+
+ In [6]: %pdoc obj
+ No documentation found for obj
+
+ In [5]: obj2 = NoDoc()
+
+ In [6]: %pdoc obj2
+ No documentation found for obj2
+ """
+
+ head = self.__head # For convenience
+ lines = []
+ ds = getdoc(obj)
+ if formatter:
+ ds = formatter(ds).get('plain/text', ds)
+ if ds:
+ lines.append(head("Class docstring:"))
+ lines.append(indent(ds))
+ if inspect.isclass(obj) and hasattr(obj, '__init__'):
+ init_ds = getdoc(obj.__init__)
+ if init_ds is not None:
+ lines.append(head("Init docstring:"))
+ lines.append(indent(init_ds))
+ elif hasattr(obj,'__call__'):
+ call_ds = getdoc(obj.__call__)
+ if call_ds:
+ lines.append(head("Call docstring:"))
+ lines.append(indent(call_ds))
+
+ if not lines:
+ self.noinfo('documentation',oname)
+ else:
+ page.page('\n'.join(lines))
+
+ def psource(self, obj, oname=''):
+ """Print the source code for an object."""
+
+ # Flush the source cache because inspect can return out-of-date source
+ linecache.checkcache()
+ try:
+ src = getsource(obj, oname=oname)
+ except Exception:
+ src = None
+
+ if src is None:
+ self.noinfo('source', oname)
+ else:
+ page.page(self.format(src))
+
+ def pfile(self, obj, oname=''):
+ """Show the whole file where an object was defined."""
+
+ lineno = find_source_lines(obj)
+ if lineno is None:
+ self.noinfo('file', oname)
+ return
+
+ 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.endswith(('.so', '.dll', '.pyd')):
+ print('File %r is binary, not printing.' % ofile)
+ elif not os.path.isfile(ofile):
+ print('File %r does not exist, not printing.' % ofile)
+ 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.
+ page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1)
+
+
+ def _mime_format(self, text:str, formatter=None) -> dict:
+ """Return a mime bundle representation of the input text.
+
+ - if `formatter` is None, the returned mime bundle has
+ a `text/plain` field, with the input text.
+ a `text/html` field with a `<pre>` tag containing the input text.
+
+ - if `formatter` is not None, it must be a callable transforming the
+ input text into a mime bundle. Default values for `text/plain` and
+ `text/html` representations are the ones described above.
+
+ Note:
+
+ Formatters returning strings are supported but this behavior is deprecated.
+
+ """
+ defaults = {
+ 'text/plain': text,
+ 'text/html': '<pre>' + text + '</pre>'
+ }
+
+ if formatter is None:
+ return defaults
+ else:
+ formatted = formatter(text)
+
+ if not isinstance(formatted, dict):
+ # Handle the deprecated behavior of a formatter returning
+ # a string instead of a mime bundle.
+ return {
+ 'text/plain': formatted,
+ 'text/html': '<pre>' + formatted + '</pre>'
+ }
+
+ else:
+ return dict(defaults, **formatted)
+
+
+ def format_mime(self, bundle):
+
+ text_plain = bundle['text/plain']
+
+ text = ''
+ heads, bodies = list(zip(*text_plain))
+ _len = max(len(h) for h in heads)
+
+ for head, body in zip(heads, bodies):
+ body = body.strip('\n')
+ delim = '\n' if '\n' in body else ' '
+ text += self.__head(head+':') + (_len - len(head))*' ' +delim + body +'\n'
+
+ bundle['text/plain'] = text
+ return bundle
+
+ def _get_info(self, obj, oname='', formatter=None, info=None, detail_level=0):
+ """Retrieve an info dict and format it.
+
+ Parameters
+ ==========
+
+ obj: any
+ Object to inspect and return info from
+ oname: str (default: ''):
+ Name of the variable pointing to `obj`.
+ formatter: callable
+ info:
+ already computed information
+ detail_level: integer
+ Granularity of detail level, if set to 1, give more information.
+ """
+
+ info = self._info(obj, oname=oname, info=info, detail_level=detail_level)
+
+ _mime = {
+ 'text/plain': [],
+ 'text/html': '',
+ }
+
+ def append_field(bundle, title:str, key:str, formatter=None):
+ field = info[key]
+ if field is not None:
+ formatted_field = self._mime_format(field, formatter)
+ bundle['text/plain'].append((title, formatted_field['text/plain']))
+ bundle['text/html'] += '<h1>' + title + '</h1>\n' + formatted_field['text/html'] + '\n'
+
+ def code_formatter(text):
+ return {
+ 'text/plain': self.format(text),
+ 'text/html': pylight(text)
+ }
+
+ if info['isalias']:
+ append_field(_mime, 'Repr', 'string_form')
+
+ elif info['ismagic']:
+ if detail_level > 0:
+ append_field(_mime, 'Source', 'source', code_formatter)
+ else:
+ append_field(_mime, 'Docstring', 'docstring', formatter)
+ append_field(_mime, 'File', 'file')
+
+ elif info['isclass'] or is_simple_callable(obj):
+ # Functions, methods, classes
+ append_field(_mime, 'Signature', 'definition', code_formatter)
+ append_field(_mime, 'Init signature', 'init_definition', code_formatter)
+ append_field(_mime, 'Docstring', 'docstring', formatter)
+ if detail_level > 0 and info['source']:
+ append_field(_mime, 'Source', 'source', code_formatter)
+ else:
+ append_field(_mime, 'Init docstring', 'init_docstring', formatter)
+
+ append_field(_mime, 'File', 'file')
+ append_field(_mime, 'Type', 'type_name')
+ append_field(_mime, 'Subclasses', 'subclasses')
+
+ else:
+ # General Python objects
+ append_field(_mime, 'Signature', 'definition', code_formatter)
+ append_field(_mime, 'Call signature', 'call_def', code_formatter)
+ append_field(_mime, 'Type', 'type_name')
+ append_field(_mime, 'String form', 'string_form')
+
+ # Namespace
+ if info['namespace'] != 'Interactive':
+ append_field(_mime, 'Namespace', 'namespace')
+
+ append_field(_mime, 'Length', 'length')
+ append_field(_mime, 'File', 'file')
+
+ # Source or docstring, depending on detail level and whether
+ # source found.
+ if detail_level > 0 and info['source']:
+ append_field(_mime, 'Source', 'source', code_formatter)
+ else:
+ append_field(_mime, 'Docstring', 'docstring', formatter)
+
+ append_field(_mime, 'Class docstring', 'class_docstring', formatter)
+ append_field(_mime, 'Init docstring', 'init_docstring', formatter)
+ append_field(_mime, 'Call docstring', 'call_docstring', formatter)
+
+
+ return self.format_mime(_mime)
+
+ def pinfo(self, obj, oname='', formatter=None, info=None, detail_level=0, enable_html_pager=True):
+ """Show detailed information about an object.
+
+ Optional arguments:
+
+ - oname: name of the variable pointing to the object.
+
+ - formatter: callable (optional)
+ A special formatter for docstrings.
+
+ The formatter is a callable that takes a string as an input
+ and returns either a formatted string or a mime type bundle
+ in the form of a dictionary.
+
+ Although the support of custom formatter returning a string
+ instead of a mime type bundle is deprecated.
+
+ - info: a structure with some information fields which may have been
+ precomputed already.
+
+ - detail_level: if set to 1, more information is given.
+ """
+ info = self._get_info(obj, oname, formatter, info, detail_level)
+ if not enable_html_pager:
+ del info['text/html']
+ page.page(info)
+
+ def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
+ """DEPRECATED. Compute a dict with detailed information about an object.
+ """
+ if formatter is not None:
+ warnings.warn('The `formatter` keyword argument to `Inspector.info`'
+ 'is deprecated as of IPython 5.0 and will have no effects.',
+ DeprecationWarning, stacklevel=2)
+ return self._info(obj, oname=oname, info=info, detail_level=detail_level)
+
+ def _info(self, obj, oname='', info=None, detail_level=0) -> dict:
+ """Compute a dict with detailed information about an object.
+
+ Parameters
+ ==========
+
+ obj: any
+ An object to find information about
+ oname: str (default: ''):
+ Name of the variable pointing to `obj`.
+ info: (default: None)
+ A struct (dict like with attr access) with some information fields
+ which may have been precomputed already.
+ detail_level: int (default:0)
+ If set to 1, more information is given.
+
+ Returns
+ =======
+
+ An object info dict with known fields from `info_fields`. Keys are
+ strings, values are string or None.
+ """
+
+ if info is None:
+ ismagic = False
+ isalias = False
+ ospace = ''
+ else:
+ ismagic = info.ismagic
+ isalias = info.isalias
+ ospace = info.namespace
+
+ # 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>'
+
+ # 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, subclasses=None)
+
+ string_max = 200 # max size of strings to show (snipped if longer)
+ shalf = int((string_max - 5) / 2)
+
+ if ismagic:
+ out['type_name'] = 'Magic function'
+ elif isalias:
+ out['type_name'] = 'System alias'
+ else:
+ out['type_name'] = type(obj).__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())).\
+ join(q.strip() for q in ostr.split("\n"))
+ out[str_head] = ostr
+ except:
+ pass
+
+ if ospace:
+ out['namespace'] = ospace
+
+ # Length (for strings and lists)
+ try:
+ out['length'] = str(len(obj))
+ except Exception:
+ pass
+
+ # Filename where object was defined
+ binary_file = False
+ fname = find_file(obj)
+ if fname is None:
+ # if anything goes wrong, we don't want to show source, so it's as
+ # if the file was binary
+ binary_file = True
+ else:
+ if fname.endswith(('.so', '.dll', '.pyd')):
+ binary_file = True
+ elif fname.endswith('<string>'):
+ fname = 'Dynamically generated function. No source code available.'
+ out['file'] = compress_user(fname)
+
+ # Original source code for a callable, class or property.
+ if detail_level:
+ # Flush the source cache because inspect can return out-of-date
+ # source
+ linecache.checkcache()
+ try:
+ if isinstance(obj, property) or not binary_file:
+ src = getsource(obj, oname)
+ if src is not None:
+ src = src.rstrip()
+ out['source'] = src
+
+ except Exception:
+ pass
+
+ # Add docstring only if no source is to be shown (avoid repetitions).
+ if ds and not self._source_contains_docstring(out.get('source'), ds):
+ out['docstring'] = ds
+
+ # Constructor docstring for classes
+ if inspect.isclass(obj):
+ out['isclass'] = True
+
+ # get the init signature:
+ try:
+ init_def = self._getdef(obj, oname)
+ except AttributeError:
+ init_def = None
+
+ # get the __init__ docstring
+ try:
+ obj_init = obj.__init__
+ except AttributeError:
+ init_ds = None
+ else:
+ if init_def is None:
+ # Get signature from init if top-level sig failed.
+ # Can happen for built-in types (list, etc.).
+ try:
+ init_def = self._getdef(obj_init, oname)
+ except AttributeError:
+ pass
+ init_ds = getdoc(obj_init)
+ # Skip Python's auto-generated docstrings
+ if init_ds == _object_init_docstring:
+ init_ds = None
+
+ if init_def:
+ out['init_definition'] = init_def
+
+ if init_ds:
+ out['init_docstring'] = init_ds
+
+ names = [sub.__name__ for sub in type.__subclasses__(obj)]
+ if len(names) < 10:
+ all_names = ', '.join(names)
+ else:
+ all_names = ', '.join(names[:10]+['...'])
+ out['subclasses'] = all_names
+ # and class docstring for instances:
+ else:
+ # reconstruct the function definition and print it:
+ defln = self._getdef(obj, oname)
+ if defln:
+ out['definition'] = defln
+
+ # 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
+ if class_ds in _builtin_type_docstrings:
+ class_ds = None
+ if class_ds and ds != class_ds:
+ out['class_docstring'] = class_ds
+
+ # Next, try to show constructor docstrings
+ try:
+ init_ds = getdoc(obj.__init__)
+ # Skip Python's auto-generated docstrings
+ if init_ds == _object_init_docstring:
+ init_ds = None
+ except AttributeError:
+ init_ds = None
+ if init_ds:
+ out['init_docstring'] = init_ds
+
+ # Call form docstring for callable instances
+ if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
+ call_def = self._getdef(obj.__call__, oname)
+ if call_def and (call_def != out.get('definition')):
+ # it may never be the case that call def and definition differ,
+ # but don't include the same signature twice
+ out['call_def'] = call_def
+ call_ds = getdoc(obj.__call__)
+ # Skip Python's auto-generated docstrings
+ if call_ds == _func_call_docstring:
+ call_ds = None
+ if call_ds:
+ out['call_docstring'] = call_ds
+
+ return object_info(**out)
+
+ @staticmethod
+ def _source_contains_docstring(src, doc):
+ """
+ Check whether the source *src* contains the docstring *doc*.
+
+ This is is helper function to skip displaying the docstring if the
+ source already contains it, avoiding repetition of information.
+ """
+ try:
+ def_node, = ast.parse(dedent(src)).body
+ return ast.get_docstring(def_node) == doc
+ except Exception:
+ # The source can become invalid or even non-existent (because it
+ # is re-fetched from the source file) so the above code fail in
+ # arbitrary ways.
+ return False
+
+ def psearch(self,pattern,ns_table,ns_search=[],
+ ignore_case=False,show_all=False, *, list_types=False):
+ """Search namespaces with wildcards for objects.
+
+ Arguments:
+
+ - pattern: string containing shell-like wildcards to use in namespace
+ searches and optionally a type specification to narrow the search to
+ objects of that type.
+
+ - ns_table: dict of name->namespaces for search.
+
+ Optional arguments:
+
+ - 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
+ underscores.
+
+ - list_types(False): list all available object types for object matching.
+ """
+ #print 'ps pattern:<%r>' % pattern # dbg
+
+ # defaults
+ type_pattern = 'all'
+ filter = ''
+
+ # list all object types
+ if list_types:
+ page.page('\n'.join(sorted(typestr2type)))
+ return
+
+ 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
+ search_result, namespaces_seen = set(), set()
+ for ns_name in ns_search:
+ ns = ns_table[ns_name]
+ # 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)))
+
+
+def _render_signature(obj_signature, obj_name) -> str:
+ """
+ This was mostly taken from inspect.Signature.__str__.
+ Look there for the comments.
+ The only change is to add linebreaks when this gets too long.
+ """
+ result = []
+ pos_only = False
+ kw_only = True
+ for param in obj_signature.parameters.values():
+ if param.kind == inspect._POSITIONAL_ONLY:
+ pos_only = True
+ elif pos_only:
+ result.append('/')
+ pos_only = False
+
+ if param.kind == inspect._VAR_POSITIONAL:
+ kw_only = False
+ elif param.kind == inspect._KEYWORD_ONLY and kw_only:
+ result.append('*')
+ kw_only = False
+
+ result.append(str(param))
+
+ if pos_only:
+ result.append('/')
+
+ # add up name, parameters, braces (2), and commas
+ if len(obj_name) + sum(len(r) + 2 for r in result) > 75:
+ # This doesn’t fit behind “Signature: ” in an inspect window.
+ rendered = '{}(\n{})'.format(obj_name, ''.join(
+ ' {},\n'.format(r) for r in result)
+ )
+ else:
+ rendered = '{}({})'.format(obj_name, ', '.join(result))
+
+ if obj_signature.return_annotation is not inspect._empty:
+ anno = inspect.formatannotation(obj_signature.return_annotation)
+ rendered += ' -> {}'.format(anno)
+
+ return rendered