oinspect.py 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171
  1. # -*- coding: utf-8 -*-
  2. """Tools for inspecting Python objects.
  3. Uses syntax highlighting for presenting the various information elements.
  4. Similar in spirit to the inspect module, but all calls take a name argument to
  5. reference the name under which an object is being read.
  6. """
  7. # Copyright (c) IPython Development Team.
  8. # Distributed under the terms of the Modified BSD License.
  9. __all__ = ['Inspector','InspectColors']
  10. # stdlib modules
  11. from dataclasses import dataclass
  12. from inspect import signature
  13. from textwrap import dedent
  14. import ast
  15. import html
  16. import inspect
  17. import io as stdlib_io
  18. import linecache
  19. import os
  20. import sys
  21. import types
  22. import warnings
  23. from typing import Any, Optional, Dict, Union, List, Tuple
  24. if sys.version_info <= (3, 10):
  25. from typing_extensions import TypeAlias
  26. else:
  27. from typing import TypeAlias
  28. # IPython's own
  29. from IPython.core import page
  30. from IPython.lib.pretty import pretty
  31. from IPython.testing.skipdoctest import skip_doctest
  32. from IPython.utils import PyColorize
  33. from IPython.utils import openpy
  34. from IPython.utils.dir2 import safe_hasattr
  35. from IPython.utils.path import compress_user
  36. from IPython.utils.text import indent
  37. from IPython.utils.wildcard import list_namespace
  38. from IPython.utils.wildcard import typestr2type
  39. from IPython.utils.coloransi import TermColors, ColorScheme, ColorSchemeTable
  40. from IPython.utils.py3compat import cast_unicode
  41. from IPython.utils.colorable import Colorable
  42. from IPython.utils.decorators import undoc
  43. from pygments import highlight
  44. from pygments.lexers import PythonLexer
  45. from pygments.formatters import HtmlFormatter
  46. HOOK_NAME = "__custom_documentations__"
  47. UnformattedBundle: TypeAlias = Dict[str, List[Tuple[str, str]]] # List of (title, body)
  48. Bundle: TypeAlias = Dict[str, str]
  49. @dataclass
  50. class OInfo:
  51. ismagic: bool
  52. isalias: bool
  53. found: bool
  54. namespace: Optional[str]
  55. parent: Any
  56. obj: Any
  57. def get(self, field):
  58. """Get a field from the object for backward compatibility with before 8.12
  59. see https://github.com/h5py/h5py/issues/2253
  60. """
  61. # We need to deprecate this at some point, but the warning will show in completion.
  62. # Let's comment this for now and uncomment end of 2023 ish
  63. # warnings.warn(
  64. # f"OInfo dataclass with fields access since IPython 8.12 please use OInfo.{field} instead."
  65. # "OInfo used to be a dict but a dataclass provide static fields verification with mypy."
  66. # "This warning and backward compatibility `get()` method were added in 8.13.",
  67. # DeprecationWarning,
  68. # stacklevel=2,
  69. # )
  70. return getattr(self, field)
  71. def pylight(code):
  72. return highlight(code, PythonLexer(), HtmlFormatter(noclasses=True))
  73. # builtin docstrings to ignore
  74. _func_call_docstring = types.FunctionType.__call__.__doc__
  75. _object_init_docstring = object.__init__.__doc__
  76. _builtin_type_docstrings = {
  77. inspect.getdoc(t) for t in (types.ModuleType, types.MethodType,
  78. types.FunctionType, property)
  79. }
  80. _builtin_func_type = type(all)
  81. _builtin_meth_type = type(str.upper) # Bound methods have the same type as builtin functions
  82. #****************************************************************************
  83. # Builtin color schemes
  84. Colors = TermColors # just a shorthand
  85. InspectColors = PyColorize.ANSICodeColors
  86. #****************************************************************************
  87. # Auxiliary functions and objects
  88. # See the messaging spec for the definition of all these fields. This list
  89. # effectively defines the order of display
  90. info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
  91. 'length', 'file', 'definition', 'docstring', 'source',
  92. 'init_definition', 'class_docstring', 'init_docstring',
  93. 'call_def', 'call_docstring',
  94. # These won't be printed but will be used to determine how to
  95. # format the object
  96. 'ismagic', 'isalias', 'isclass', 'found', 'name'
  97. ]
  98. def object_info(**kw):
  99. """Make an object info dict with all fields present."""
  100. infodict = {k:None for k in info_fields}
  101. infodict.update(kw)
  102. return infodict
  103. def get_encoding(obj):
  104. """Get encoding for python source file defining obj
  105. Returns None if obj is not defined in a sourcefile.
  106. """
  107. ofile = find_file(obj)
  108. # run contents of file through pager starting at line where the object
  109. # is defined, as long as the file isn't binary and is actually on the
  110. # filesystem.
  111. if ofile is None:
  112. return None
  113. elif ofile.endswith(('.so', '.dll', '.pyd')):
  114. return None
  115. elif not os.path.isfile(ofile):
  116. return None
  117. else:
  118. # Print only text files, not extension binaries. Note that
  119. # getsourcelines returns lineno with 1-offset and page() uses
  120. # 0-offset, so we must adjust.
  121. with stdlib_io.open(ofile, 'rb') as buffer: # Tweaked to use io.open for Python 2
  122. encoding, lines = openpy.detect_encoding(buffer.readline)
  123. return encoding
  124. def getdoc(obj) -> Union[str,None]:
  125. """Stable wrapper around inspect.getdoc.
  126. This can't crash because of attribute problems.
  127. It also attempts to call a getdoc() method on the given object. This
  128. allows objects which provide their docstrings via non-standard mechanisms
  129. (like Pyro proxies) to still be inspected by ipython's ? system.
  130. """
  131. # Allow objects to offer customized documentation via a getdoc method:
  132. try:
  133. ds = obj.getdoc()
  134. except Exception:
  135. pass
  136. else:
  137. if isinstance(ds, str):
  138. return inspect.cleandoc(ds)
  139. docstr = inspect.getdoc(obj)
  140. return docstr
  141. def getsource(obj, oname='') -> Union[str,None]:
  142. """Wrapper around inspect.getsource.
  143. This can be modified by other projects to provide customized source
  144. extraction.
  145. Parameters
  146. ----------
  147. obj : object
  148. an object whose source code we will attempt to extract
  149. oname : str
  150. (optional) a name under which the object is known
  151. Returns
  152. -------
  153. src : unicode or None
  154. """
  155. if isinstance(obj, property):
  156. sources = []
  157. for attrname in ['fget', 'fset', 'fdel']:
  158. fn = getattr(obj, attrname)
  159. if fn is not None:
  160. encoding = get_encoding(fn)
  161. oname_prefix = ('%s.' % oname) if oname else ''
  162. sources.append(''.join(('# ', oname_prefix, attrname)))
  163. if inspect.isfunction(fn):
  164. _src = getsource(fn)
  165. if _src:
  166. # assert _src is not None, "please mypy"
  167. sources.append(dedent(_src))
  168. else:
  169. # Default str/repr only prints function name,
  170. # pretty.pretty prints module name too.
  171. sources.append(
  172. '%s%s = %s\n' % (oname_prefix, attrname, pretty(fn))
  173. )
  174. if sources:
  175. return '\n'.join(sources)
  176. else:
  177. return None
  178. else:
  179. # Get source for non-property objects.
  180. obj = _get_wrapped(obj)
  181. try:
  182. src = inspect.getsource(obj)
  183. except TypeError:
  184. # The object itself provided no meaningful source, try looking for
  185. # its class definition instead.
  186. try:
  187. src = inspect.getsource(obj.__class__)
  188. except (OSError, TypeError):
  189. return None
  190. except OSError:
  191. return None
  192. return src
  193. def is_simple_callable(obj):
  194. """True if obj is a function ()"""
  195. return (inspect.isfunction(obj) or inspect.ismethod(obj) or \
  196. isinstance(obj, _builtin_func_type) or isinstance(obj, _builtin_meth_type))
  197. @undoc
  198. def getargspec(obj):
  199. """Wrapper around :func:`inspect.getfullargspec`
  200. In addition to functions and methods, this can also handle objects with a
  201. ``__call__`` attribute.
  202. DEPRECATED: Deprecated since 7.10. Do not use, will be removed.
  203. """
  204. warnings.warn('`getargspec` function is deprecated as of IPython 7.10'
  205. 'and will be removed in future versions.', DeprecationWarning, stacklevel=2)
  206. if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
  207. obj = obj.__call__
  208. return inspect.getfullargspec(obj)
  209. @undoc
  210. def format_argspec(argspec):
  211. """Format argspect, convenience wrapper around inspect's.
  212. This takes a dict instead of ordered arguments and calls
  213. inspect.format_argspec with the arguments in the necessary order.
  214. DEPRECATED (since 7.10): Do not use; will be removed in future versions.
  215. """
  216. warnings.warn('`format_argspec` function is deprecated as of IPython 7.10'
  217. 'and will be removed in future versions.', DeprecationWarning, stacklevel=2)
  218. return inspect.formatargspec(argspec['args'], argspec['varargs'],
  219. argspec['varkw'], argspec['defaults'])
  220. @undoc
  221. def call_tip(oinfo, format_call=True):
  222. """DEPRECATED since 6.0. Extract call tip data from an oinfo dict."""
  223. warnings.warn(
  224. "`call_tip` function is deprecated as of IPython 6.0"
  225. "and will be removed in future versions.",
  226. DeprecationWarning,
  227. stacklevel=2,
  228. )
  229. # Get call definition
  230. argspec = oinfo.get('argspec')
  231. if argspec is None:
  232. call_line = None
  233. else:
  234. # Callable objects will have 'self' as their first argument, prune
  235. # it out if it's there for clarity (since users do *not* pass an
  236. # extra first argument explicitly).
  237. try:
  238. has_self = argspec['args'][0] == 'self'
  239. except (KeyError, IndexError):
  240. pass
  241. else:
  242. if has_self:
  243. argspec['args'] = argspec['args'][1:]
  244. call_line = oinfo['name']+format_argspec(argspec)
  245. # Now get docstring.
  246. # The priority is: call docstring, constructor docstring, main one.
  247. doc = oinfo.get('call_docstring')
  248. if doc is None:
  249. doc = oinfo.get('init_docstring')
  250. if doc is None:
  251. doc = oinfo.get('docstring','')
  252. return call_line, doc
  253. def _get_wrapped(obj):
  254. """Get the original object if wrapped in one or more @decorators
  255. Some objects automatically construct similar objects on any unrecognised
  256. attribute access (e.g. unittest.mock.call). To protect against infinite loops,
  257. this will arbitrarily cut off after 100 levels of obj.__wrapped__
  258. attribute access. --TK, Jan 2016
  259. """
  260. orig_obj = obj
  261. i = 0
  262. while safe_hasattr(obj, '__wrapped__'):
  263. obj = obj.__wrapped__
  264. i += 1
  265. if i > 100:
  266. # __wrapped__ is probably a lie, so return the thing we started with
  267. return orig_obj
  268. return obj
  269. def find_file(obj) -> str:
  270. """Find the absolute path to the file where an object was defined.
  271. This is essentially a robust wrapper around `inspect.getabsfile`.
  272. Returns None if no file can be found.
  273. Parameters
  274. ----------
  275. obj : any Python object
  276. Returns
  277. -------
  278. fname : str
  279. The absolute path to the file where the object was defined.
  280. """
  281. obj = _get_wrapped(obj)
  282. fname = None
  283. try:
  284. fname = inspect.getabsfile(obj)
  285. except TypeError:
  286. # For an instance, the file that matters is where its class was
  287. # declared.
  288. try:
  289. fname = inspect.getabsfile(obj.__class__)
  290. except (OSError, TypeError):
  291. # Can happen for builtins
  292. pass
  293. except OSError:
  294. pass
  295. return cast_unicode(fname)
  296. def find_source_lines(obj):
  297. """Find the line number in a file where an object was defined.
  298. This is essentially a robust wrapper around `inspect.getsourcelines`.
  299. Returns None if no file can be found.
  300. Parameters
  301. ----------
  302. obj : any Python object
  303. Returns
  304. -------
  305. lineno : int
  306. The line number where the object definition starts.
  307. """
  308. obj = _get_wrapped(obj)
  309. try:
  310. lineno = inspect.getsourcelines(obj)[1]
  311. except TypeError:
  312. # For instances, try the class object like getsource() does
  313. try:
  314. lineno = inspect.getsourcelines(obj.__class__)[1]
  315. except (OSError, TypeError):
  316. return None
  317. except OSError:
  318. return None
  319. return lineno
  320. class Inspector(Colorable):
  321. def __init__(self, color_table=InspectColors,
  322. code_color_table=PyColorize.ANSICodeColors,
  323. scheme=None,
  324. str_detail_level=0,
  325. parent=None, config=None):
  326. super(Inspector, self).__init__(parent=parent, config=config)
  327. self.color_table = color_table
  328. self.parser = PyColorize.Parser(out='str', parent=self, style=scheme)
  329. self.format = self.parser.format
  330. self.str_detail_level = str_detail_level
  331. self.set_active_scheme(scheme)
  332. def _getdef(self,obj,oname='') -> Union[str,None]:
  333. """Return the call signature for any callable object.
  334. If any exception is generated, None is returned instead and the
  335. exception is suppressed."""
  336. try:
  337. return _render_signature(signature(obj), oname)
  338. except:
  339. return None
  340. def __head(self,h) -> str:
  341. """Return a header string with proper colors."""
  342. return '%s%s%s' % (self.color_table.active_colors.header,h,
  343. self.color_table.active_colors.normal)
  344. def set_active_scheme(self, scheme):
  345. if scheme is not None:
  346. self.color_table.set_active_scheme(scheme)
  347. self.parser.color_table.set_active_scheme(scheme)
  348. def noinfo(self, msg, oname):
  349. """Generic message when no information is found."""
  350. print('No %s found' % msg, end=' ')
  351. if oname:
  352. print('for %s' % oname)
  353. else:
  354. print()
  355. def pdef(self, obj, oname=''):
  356. """Print the call signature for any callable object.
  357. If the object is a class, print the constructor information."""
  358. if not callable(obj):
  359. print('Object is not callable.')
  360. return
  361. header = ''
  362. if inspect.isclass(obj):
  363. header = self.__head('Class constructor information:\n')
  364. output = self._getdef(obj,oname)
  365. if output is None:
  366. self.noinfo('definition header',oname)
  367. else:
  368. print(header,self.format(output), end=' ')
  369. # In Python 3, all classes are new-style, so they all have __init__.
  370. @skip_doctest
  371. def pdoc(self, obj, oname='', formatter=None):
  372. """Print the docstring for any object.
  373. Optional:
  374. -formatter: a function to run the docstring through for specially
  375. formatted docstrings.
  376. Examples
  377. --------
  378. In [1]: class NoInit:
  379. ...: pass
  380. In [2]: class NoDoc:
  381. ...: def __init__(self):
  382. ...: pass
  383. In [3]: %pdoc NoDoc
  384. No documentation found for NoDoc
  385. In [4]: %pdoc NoInit
  386. No documentation found for NoInit
  387. In [5]: obj = NoInit()
  388. In [6]: %pdoc obj
  389. No documentation found for obj
  390. In [5]: obj2 = NoDoc()
  391. In [6]: %pdoc obj2
  392. No documentation found for obj2
  393. """
  394. head = self.__head # For convenience
  395. lines = []
  396. ds = getdoc(obj)
  397. if formatter:
  398. ds = formatter(ds).get('plain/text', ds)
  399. if ds:
  400. lines.append(head("Class docstring:"))
  401. lines.append(indent(ds))
  402. if inspect.isclass(obj) and hasattr(obj, '__init__'):
  403. init_ds = getdoc(obj.__init__)
  404. if init_ds is not None:
  405. lines.append(head("Init docstring:"))
  406. lines.append(indent(init_ds))
  407. elif hasattr(obj,'__call__'):
  408. call_ds = getdoc(obj.__call__)
  409. if call_ds:
  410. lines.append(head("Call docstring:"))
  411. lines.append(indent(call_ds))
  412. if not lines:
  413. self.noinfo('documentation',oname)
  414. else:
  415. page.page('\n'.join(lines))
  416. def psource(self, obj, oname=''):
  417. """Print the source code for an object."""
  418. # Flush the source cache because inspect can return out-of-date source
  419. linecache.checkcache()
  420. try:
  421. src = getsource(obj, oname=oname)
  422. except Exception:
  423. src = None
  424. if src is None:
  425. self.noinfo('source', oname)
  426. else:
  427. page.page(self.format(src))
  428. def pfile(self, obj, oname=''):
  429. """Show the whole file where an object was defined."""
  430. lineno = find_source_lines(obj)
  431. if lineno is None:
  432. self.noinfo('file', oname)
  433. return
  434. ofile = find_file(obj)
  435. # run contents of file through pager starting at line where the object
  436. # is defined, as long as the file isn't binary and is actually on the
  437. # filesystem.
  438. if ofile.endswith(('.so', '.dll', '.pyd')):
  439. print('File %r is binary, not printing.' % ofile)
  440. elif not os.path.isfile(ofile):
  441. print('File %r does not exist, not printing.' % ofile)
  442. else:
  443. # Print only text files, not extension binaries. Note that
  444. # getsourcelines returns lineno with 1-offset and page() uses
  445. # 0-offset, so we must adjust.
  446. page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1)
  447. def _mime_format(self, text:str, formatter=None) -> dict:
  448. """Return a mime bundle representation of the input text.
  449. - if `formatter` is None, the returned mime bundle has
  450. a ``text/plain`` field, with the input text.
  451. a ``text/html`` field with a ``<pre>`` tag containing the input text.
  452. - if ``formatter`` is not None, it must be a callable transforming the
  453. input text into a mime bundle. Default values for ``text/plain`` and
  454. ``text/html`` representations are the ones described above.
  455. Note:
  456. Formatters returning strings are supported but this behavior is deprecated.
  457. """
  458. defaults = {
  459. "text/plain": text,
  460. "text/html": f"<pre>{html.escape(text)}</pre>",
  461. }
  462. if formatter is None:
  463. return defaults
  464. else:
  465. formatted = formatter(text)
  466. if not isinstance(formatted, dict):
  467. # Handle the deprecated behavior of a formatter returning
  468. # a string instead of a mime bundle.
  469. return {"text/plain": formatted, "text/html": f"<pre>{formatted}</pre>"}
  470. else:
  471. return dict(defaults, **formatted)
  472. def format_mime(self, bundle: UnformattedBundle) -> Bundle:
  473. """Format a mimebundle being created by _make_info_unformatted into a real mimebundle"""
  474. # Format text/plain mimetype
  475. assert isinstance(bundle["text/plain"], list)
  476. for item in bundle["text/plain"]:
  477. assert isinstance(item, tuple)
  478. new_b: Bundle = {}
  479. lines = []
  480. _len = max(len(h) for h, _ in bundle["text/plain"])
  481. for head, body in bundle["text/plain"]:
  482. body = body.strip("\n")
  483. delim = "\n" if "\n" in body else " "
  484. lines.append(
  485. f"{self.__head(head+':')}{(_len - len(head))*' '}{delim}{body}"
  486. )
  487. new_b["text/plain"] = "\n".join(lines)
  488. if "text/html" in bundle:
  489. assert isinstance(bundle["text/html"], list)
  490. for item in bundle["text/html"]:
  491. assert isinstance(item, tuple)
  492. # Format the text/html mimetype
  493. if isinstance(bundle["text/html"], (list, tuple)):
  494. # bundle['text/html'] is a list of (head, formatted body) pairs
  495. new_b["text/html"] = "\n".join(
  496. (f"<h1>{head}</h1>\n{body}" for (head, body) in bundle["text/html"])
  497. )
  498. for k in bundle.keys():
  499. if k in ("text/html", "text/plain"):
  500. continue
  501. else:
  502. new_b = bundle[k] # type:ignore
  503. return new_b
  504. def _append_info_field(
  505. self,
  506. bundle: UnformattedBundle,
  507. title: str,
  508. key: str,
  509. info,
  510. omit_sections,
  511. formatter,
  512. ):
  513. """Append an info value to the unformatted mimebundle being constructed by _make_info_unformatted"""
  514. if title in omit_sections or key in omit_sections:
  515. return
  516. field = info[key]
  517. if field is not None:
  518. formatted_field = self._mime_format(field, formatter)
  519. bundle["text/plain"].append((title, formatted_field["text/plain"]))
  520. bundle["text/html"].append((title, formatted_field["text/html"]))
  521. def _make_info_unformatted(
  522. self, obj, info, formatter, detail_level, omit_sections
  523. ) -> UnformattedBundle:
  524. """Assemble the mimebundle as unformatted lists of information"""
  525. bundle: UnformattedBundle = {
  526. "text/plain": [],
  527. "text/html": [],
  528. }
  529. # A convenience function to simplify calls below
  530. def append_field(
  531. bundle: UnformattedBundle, title: str, key: str, formatter=None
  532. ):
  533. self._append_info_field(
  534. bundle,
  535. title=title,
  536. key=key,
  537. info=info,
  538. omit_sections=omit_sections,
  539. formatter=formatter,
  540. )
  541. def code_formatter(text) -> Bundle:
  542. return {
  543. 'text/plain': self.format(text),
  544. 'text/html': pylight(text)
  545. }
  546. if info["isalias"]:
  547. append_field(bundle, "Repr", "string_form")
  548. elif info['ismagic']:
  549. if detail_level > 0:
  550. append_field(bundle, "Source", "source", code_formatter)
  551. else:
  552. append_field(bundle, "Docstring", "docstring", formatter)
  553. append_field(bundle, "File", "file")
  554. elif info['isclass'] or is_simple_callable(obj):
  555. # Functions, methods, classes
  556. append_field(bundle, "Signature", "definition", code_formatter)
  557. append_field(bundle, "Init signature", "init_definition", code_formatter)
  558. append_field(bundle, "Docstring", "docstring", formatter)
  559. if detail_level > 0 and info["source"]:
  560. append_field(bundle, "Source", "source", code_formatter)
  561. else:
  562. append_field(bundle, "Init docstring", "init_docstring", formatter)
  563. append_field(bundle, "File", "file")
  564. append_field(bundle, "Type", "type_name")
  565. append_field(bundle, "Subclasses", "subclasses")
  566. else:
  567. # General Python objects
  568. append_field(bundle, "Signature", "definition", code_formatter)
  569. append_field(bundle, "Call signature", "call_def", code_formatter)
  570. append_field(bundle, "Type", "type_name")
  571. append_field(bundle, "String form", "string_form")
  572. # Namespace
  573. if info["namespace"] != "Interactive":
  574. append_field(bundle, "Namespace", "namespace")
  575. append_field(bundle, "Length", "length")
  576. append_field(bundle, "File", "file")
  577. # Source or docstring, depending on detail level and whether
  578. # source found.
  579. if detail_level > 0 and info["source"]:
  580. append_field(bundle, "Source", "source", code_formatter)
  581. else:
  582. append_field(bundle, "Docstring", "docstring", formatter)
  583. append_field(bundle, "Class docstring", "class_docstring", formatter)
  584. append_field(bundle, "Init docstring", "init_docstring", formatter)
  585. append_field(bundle, "Call docstring", "call_docstring", formatter)
  586. return bundle
  587. def _get_info(
  588. self,
  589. obj: Any,
  590. oname: str = "",
  591. formatter=None,
  592. info: Optional[OInfo] = None,
  593. detail_level=0,
  594. omit_sections=(),
  595. ) -> Bundle:
  596. """Retrieve an info dict and format it.
  597. Parameters
  598. ----------
  599. obj : any
  600. Object to inspect and return info from
  601. oname : str (default: ''):
  602. Name of the variable pointing to `obj`.
  603. formatter : callable
  604. info
  605. already computed information
  606. detail_level : integer
  607. Granularity of detail level, if set to 1, give more information.
  608. omit_sections : container[str]
  609. Titles or keys to omit from output (can be set, tuple, etc., anything supporting `in`)
  610. """
  611. info_dict = self.info(obj, oname=oname, info=info, detail_level=detail_level)
  612. bundle = self._make_info_unformatted(
  613. obj,
  614. info_dict,
  615. formatter,
  616. detail_level=detail_level,
  617. omit_sections=omit_sections,
  618. )
  619. return self.format_mime(bundle)
  620. def pinfo(
  621. self,
  622. obj,
  623. oname="",
  624. formatter=None,
  625. info: Optional[OInfo] = None,
  626. detail_level=0,
  627. enable_html_pager=True,
  628. omit_sections=(),
  629. ):
  630. """Show detailed information about an object.
  631. Optional arguments:
  632. - oname: name of the variable pointing to the object.
  633. - formatter: callable (optional)
  634. A special formatter for docstrings.
  635. The formatter is a callable that takes a string as an input
  636. and returns either a formatted string or a mime type bundle
  637. in the form of a dictionary.
  638. Although the support of custom formatter returning a string
  639. instead of a mime type bundle is deprecated.
  640. - info: a structure with some information fields which may have been
  641. precomputed already.
  642. - detail_level: if set to 1, more information is given.
  643. - omit_sections: set of section keys and titles to omit
  644. """
  645. assert info is not None
  646. info_b: Bundle = self._get_info(
  647. obj, oname, formatter, info, detail_level, omit_sections=omit_sections
  648. )
  649. if not enable_html_pager:
  650. del info_b["text/html"]
  651. page.page(info_b)
  652. def _info(self, obj, oname="", info=None, detail_level=0):
  653. """
  654. Inspector.info() was likely improperly marked as deprecated
  655. while only a parameter was deprecated. We "un-deprecate" it.
  656. """
  657. warnings.warn(
  658. "The `Inspector.info()` method has been un-deprecated as of 8.0 "
  659. "and the `formatter=` keyword removed. `Inspector._info` is now "
  660. "an alias, and you can just call `.info()` directly.",
  661. DeprecationWarning,
  662. stacklevel=2,
  663. )
  664. return self.info(obj, oname=oname, info=info, detail_level=detail_level)
  665. def info(self, obj, oname="", info=None, detail_level=0) -> Dict[str, Any]:
  666. """Compute a dict with detailed information about an object.
  667. Parameters
  668. ----------
  669. obj : any
  670. An object to find information about
  671. oname : str (default: '')
  672. Name of the variable pointing to `obj`.
  673. info : (default: None)
  674. A struct (dict like with attr access) with some information fields
  675. which may have been precomputed already.
  676. detail_level : int (default:0)
  677. If set to 1, more information is given.
  678. Returns
  679. -------
  680. An object info dict with known fields from `info_fields`. Keys are
  681. strings, values are string or None.
  682. """
  683. if info is None:
  684. ismagic = False
  685. isalias = False
  686. ospace = ''
  687. else:
  688. ismagic = info.ismagic
  689. isalias = info.isalias
  690. ospace = info.namespace
  691. # Get docstring, special-casing aliases:
  692. att_name = oname.split(".")[-1]
  693. parents_docs = None
  694. prelude = ""
  695. if info and info.parent is not None and hasattr(info.parent, HOOK_NAME):
  696. parents_docs_dict = getattr(info.parent, HOOK_NAME)
  697. parents_docs = parents_docs_dict.get(att_name, None)
  698. out = dict(
  699. name=oname, found=True, isalias=isalias, ismagic=ismagic, subclasses=None
  700. )
  701. if parents_docs:
  702. ds = parents_docs
  703. elif isalias:
  704. if not callable(obj):
  705. try:
  706. ds = "Alias to the system command:\n %s" % obj[1]
  707. except:
  708. ds = "Alias: " + str(obj)
  709. else:
  710. ds = "Alias to " + str(obj)
  711. if obj.__doc__:
  712. ds += "\nDocstring:\n" + obj.__doc__
  713. else:
  714. ds_or_None = getdoc(obj)
  715. if ds_or_None is None:
  716. ds = '<no docstring>'
  717. else:
  718. ds = ds_or_None
  719. ds = prelude + ds
  720. # store output in a dict, we initialize it here and fill it as we go
  721. string_max = 200 # max size of strings to show (snipped if longer)
  722. shalf = int((string_max - 5) / 2)
  723. if ismagic:
  724. out['type_name'] = 'Magic function'
  725. elif isalias:
  726. out['type_name'] = 'System alias'
  727. else:
  728. out['type_name'] = type(obj).__name__
  729. try:
  730. bclass = obj.__class__
  731. out['base_class'] = str(bclass)
  732. except:
  733. pass
  734. # String form, but snip if too long in ? form (full in ??)
  735. if detail_level >= self.str_detail_level:
  736. try:
  737. ostr = str(obj)
  738. str_head = 'string_form'
  739. if not detail_level and len(ostr)>string_max:
  740. ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
  741. ostr = ("\n" + " " * len(str_head.expandtabs())).\
  742. join(q.strip() for q in ostr.split("\n"))
  743. out[str_head] = ostr
  744. except:
  745. pass
  746. if ospace:
  747. out['namespace'] = ospace
  748. # Length (for strings and lists)
  749. try:
  750. out['length'] = str(len(obj))
  751. except Exception:
  752. pass
  753. # Filename where object was defined
  754. binary_file = False
  755. fname = find_file(obj)
  756. if fname is None:
  757. # if anything goes wrong, we don't want to show source, so it's as
  758. # if the file was binary
  759. binary_file = True
  760. else:
  761. if fname.endswith(('.so', '.dll', '.pyd')):
  762. binary_file = True
  763. elif fname.endswith('<string>'):
  764. fname = 'Dynamically generated function. No source code available.'
  765. out['file'] = compress_user(fname)
  766. # Original source code for a callable, class or property.
  767. if detail_level:
  768. # Flush the source cache because inspect can return out-of-date
  769. # source
  770. linecache.checkcache()
  771. try:
  772. if isinstance(obj, property) or not binary_file:
  773. src = getsource(obj, oname)
  774. if src is not None:
  775. src = src.rstrip()
  776. out['source'] = src
  777. except Exception:
  778. pass
  779. # Add docstring only if no source is to be shown (avoid repetitions).
  780. if ds and not self._source_contains_docstring(out.get('source'), ds):
  781. out['docstring'] = ds
  782. # Constructor docstring for classes
  783. if inspect.isclass(obj):
  784. out['isclass'] = True
  785. # get the init signature:
  786. try:
  787. init_def = self._getdef(obj, oname)
  788. except AttributeError:
  789. init_def = None
  790. # get the __init__ docstring
  791. try:
  792. obj_init = obj.__init__
  793. except AttributeError:
  794. init_ds = None
  795. else:
  796. if init_def is None:
  797. # Get signature from init if top-level sig failed.
  798. # Can happen for built-in types (list, etc.).
  799. try:
  800. init_def = self._getdef(obj_init, oname)
  801. except AttributeError:
  802. pass
  803. init_ds = getdoc(obj_init)
  804. # Skip Python's auto-generated docstrings
  805. if init_ds == _object_init_docstring:
  806. init_ds = None
  807. if init_def:
  808. out['init_definition'] = init_def
  809. if init_ds:
  810. out['init_docstring'] = init_ds
  811. names = [sub.__name__ for sub in type.__subclasses__(obj)]
  812. if len(names) < 10:
  813. all_names = ', '.join(names)
  814. else:
  815. all_names = ', '.join(names[:10]+['...'])
  816. out['subclasses'] = all_names
  817. # and class docstring for instances:
  818. else:
  819. # reconstruct the function definition and print it:
  820. defln = self._getdef(obj, oname)
  821. if defln:
  822. out['definition'] = defln
  823. # First, check whether the instance docstring is identical to the
  824. # class one, and print it separately if they don't coincide. In
  825. # most cases they will, but it's nice to print all the info for
  826. # objects which use instance-customized docstrings.
  827. if ds:
  828. try:
  829. cls = getattr(obj,'__class__')
  830. except:
  831. class_ds = None
  832. else:
  833. class_ds = getdoc(cls)
  834. # Skip Python's auto-generated docstrings
  835. if class_ds in _builtin_type_docstrings:
  836. class_ds = None
  837. if class_ds and ds != class_ds:
  838. out['class_docstring'] = class_ds
  839. # Next, try to show constructor docstrings
  840. try:
  841. init_ds = getdoc(obj.__init__)
  842. # Skip Python's auto-generated docstrings
  843. if init_ds == _object_init_docstring:
  844. init_ds = None
  845. except AttributeError:
  846. init_ds = None
  847. if init_ds:
  848. out['init_docstring'] = init_ds
  849. # Call form docstring for callable instances
  850. if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
  851. call_def = self._getdef(obj.__call__, oname)
  852. if call_def and (call_def != out.get('definition')):
  853. # it may never be the case that call def and definition differ,
  854. # but don't include the same signature twice
  855. out['call_def'] = call_def
  856. call_ds = getdoc(obj.__call__)
  857. # Skip Python's auto-generated docstrings
  858. if call_ds == _func_call_docstring:
  859. call_ds = None
  860. if call_ds:
  861. out['call_docstring'] = call_ds
  862. return object_info(**out)
  863. @staticmethod
  864. def _source_contains_docstring(src, doc):
  865. """
  866. Check whether the source *src* contains the docstring *doc*.
  867. This is is helper function to skip displaying the docstring if the
  868. source already contains it, avoiding repetition of information.
  869. """
  870. try:
  871. (def_node,) = ast.parse(dedent(src)).body
  872. return ast.get_docstring(def_node) == doc # type: ignore[arg-type]
  873. except Exception:
  874. # The source can become invalid or even non-existent (because it
  875. # is re-fetched from the source file) so the above code fail in
  876. # arbitrary ways.
  877. return False
  878. def psearch(self,pattern,ns_table,ns_search=[],
  879. ignore_case=False,show_all=False, *, list_types=False):
  880. """Search namespaces with wildcards for objects.
  881. Arguments:
  882. - pattern: string containing shell-like wildcards to use in namespace
  883. searches and optionally a type specification to narrow the search to
  884. objects of that type.
  885. - ns_table: dict of name->namespaces for search.
  886. Optional arguments:
  887. - ns_search: list of namespace names to include in search.
  888. - ignore_case(False): make the search case-insensitive.
  889. - show_all(False): show all names, including those starting with
  890. underscores.
  891. - list_types(False): list all available object types for object matching.
  892. """
  893. #print 'ps pattern:<%r>' % pattern # dbg
  894. # defaults
  895. type_pattern = 'all'
  896. filter = ''
  897. # list all object types
  898. if list_types:
  899. page.page('\n'.join(sorted(typestr2type)))
  900. return
  901. cmds = pattern.split()
  902. len_cmds = len(cmds)
  903. if len_cmds == 1:
  904. # Only filter pattern given
  905. filter = cmds[0]
  906. elif len_cmds == 2:
  907. # Both filter and type specified
  908. filter,type_pattern = cmds
  909. else:
  910. raise ValueError('invalid argument string for psearch: <%s>' %
  911. pattern)
  912. # filter search namespaces
  913. for name in ns_search:
  914. if name not in ns_table:
  915. raise ValueError('invalid namespace <%s>. Valid names: %s' %
  916. (name,ns_table.keys()))
  917. #print 'type_pattern:',type_pattern # dbg
  918. search_result, namespaces_seen = set(), set()
  919. for ns_name in ns_search:
  920. ns = ns_table[ns_name]
  921. # Normally, locals and globals are the same, so we just check one.
  922. if id(ns) in namespaces_seen:
  923. continue
  924. namespaces_seen.add(id(ns))
  925. tmp_res = list_namespace(ns, type_pattern, filter,
  926. ignore_case=ignore_case, show_all=show_all)
  927. search_result.update(tmp_res)
  928. page.page('\n'.join(sorted(search_result)))
  929. def _render_signature(obj_signature, obj_name) -> str:
  930. """
  931. This was mostly taken from inspect.Signature.__str__.
  932. Look there for the comments.
  933. The only change is to add linebreaks when this gets too long.
  934. """
  935. result = []
  936. pos_only = False
  937. kw_only = True
  938. for param in obj_signature.parameters.values():
  939. if param.kind == inspect.Parameter.POSITIONAL_ONLY:
  940. pos_only = True
  941. elif pos_only:
  942. result.append('/')
  943. pos_only = False
  944. if param.kind == inspect.Parameter.VAR_POSITIONAL:
  945. kw_only = False
  946. elif param.kind == inspect.Parameter.KEYWORD_ONLY and kw_only:
  947. result.append('*')
  948. kw_only = False
  949. result.append(str(param))
  950. if pos_only:
  951. result.append('/')
  952. # add up name, parameters, braces (2), and commas
  953. if len(obj_name) + sum(len(r) + 2 for r in result) > 75:
  954. # This doesn’t fit behind “Signature: ” in an inspect window.
  955. rendered = '{}(\n{})'.format(obj_name, ''.join(
  956. ' {},\n'.format(r) for r in result)
  957. )
  958. else:
  959. rendered = '{}({})'.format(obj_name, ', '.join(result))
  960. if obj_signature.return_annotation is not inspect._empty:
  961. anno = inspect.formatannotation(obj_signature.return_annotation)
  962. rendered += ' -> {}'.format(anno)
  963. return rendered