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