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