oinspect.py 42 KB

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