docstr: Compute a dict with detailed information about an object.
  
  Optional arguments:
  
  - oname: name of the variable pointing to the object.
  
  - info: a structure with some information fields which may have been
    precomputed already.
  
  - detail_level: if set to 1, more information is given.
code: def _info(self, obj, oname="", info=None, detail_level=0):
      """Compute a dict with detailed information about an object.
  
      Optional arguments:
  
      - oname: name of the variable pointing to the object.
  
      - info: a structure with some information fields which may have been
        precomputed already.
  
      - 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
  
      # 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
      )
  
      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())).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:
          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 out.get("source", None) is None:
          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
  
      # 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
  
      # 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.
  
      if inspect.isclass(obj):
          # Old-style classes need not have an __init__
          callable_obj = getattr(obj, "__init__", None)
      elif callable(obj):
          callable_obj = obj
      else:
          callable_obj = None
  
      if callable_obj is not None:
          try:
              argspec = getargspec(callable_obj)
          except (TypeError, AttributeError):
              # For extensions/builtins we can't retrieve the argspec
              pass
          else:
              # 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")
  
      return object_info(**out)
  
imports: from __future__ import print_function
  import inspect
  import linecache
  import warnings
  import os
  from textwrap import dedent
  import types
  import io
  from IPython.core import page
  from IPython.lib.pretty import pretty
  from IPython.testing.skipdoctest import skip_doctest_py3
  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.coloransi import TermColors, ColorScheme, ColorSchemeTable
  from IPython.utils.py3compat import cast_unicode, string_types, PY3
  from IPython.utils.signatures import signature
  from IPython.utils.colorable import Colorable
  from pygments import highlight
  from pygments.formatters import HtmlFormatter
label_desc: Use image node_id 16 for calling the Inspector obj's underlying member method code with example usage: obj._info(obj, oname, info, detail_level) and returns: object_info