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