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