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