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