##// END OF EJS Templates
Add function signature info to calltips....
Fernando Perez -
Show More
@@ -0,0 +1,89 b''
1 """Tests for the object inspection functionality.
2 """
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2010 The IPython Development Team.
5 #
6 # Distributed under the terms of the BSD License.
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
10
11 #-----------------------------------------------------------------------------
12 # Imports
13 #-----------------------------------------------------------------------------
14 from __future__ import print_function
15
16 # Stdlib imports
17
18 # Third-party imports
19 import nose.tools as nt
20
21 # Our own imports
22 from .. import oinspect
23
24 #-----------------------------------------------------------------------------
25 # Globals and constants
26 #-----------------------------------------------------------------------------
27
28 inspector = oinspect.Inspector()
29
30 #-----------------------------------------------------------------------------
31 # Local utilities
32 #-----------------------------------------------------------------------------
33
34 # A few generic objects we can then inspect in the tests below
35
36 class Call(object):
37 """This is the class docstring."""
38
39 def __init__(self, x, y=1):
40 """This is the constructor docstring."""
41
42 def __call__(self, *a, **kw):
43 """This is the call docstring."""
44
45 def method(self, x, z=2):
46 """Some method's docstring"""
47
48 def f(x, y=2, *a, **kw):
49 """A simple function."""
50
51 def g(y, z=3, *a, **kw):
52 pass # no docstring
53
54
55 def check_calltip(obj, name, call, docstring):
56 """Generic check pattern all calltip tests will use"""
57 info = inspector.info(obj, name)
58 call_line, ds = oinspect.call_tip(info)
59 nt.assert_equal(call_line, call)
60 nt.assert_equal(ds, docstring)
61
62 #-----------------------------------------------------------------------------
63 # Tests
64 #-----------------------------------------------------------------------------
65
66 def test_calltip_class():
67 check_calltip(Call, 'Call', 'Call(x, y=1)', Call.__init__.__doc__)
68
69
70 def test_calltip_instance():
71 c = Call(1)
72 check_calltip(c, 'c', 'c(*a, **kw)', c.__call__.__doc__)
73
74
75 def test_calltip_method():
76 c = Call(1)
77 check_calltip(c.method, 'c.method', 'c.method(x, z=2)', c.method.__doc__)
78
79
80 def test_calltip_function():
81 check_calltip(f, 'f', 'f(x, y=2, *a, **kw)', f.__doc__)
82
83
84 def test_calltip_function2():
85 check_calltip(g, 'g', 'g(y, z=3, *a, **kw)', '<no docstring>')
86
87
88 def test_calltip_builtin():
89 check_calltip(sum, 'sum', None, sum.__doc__)
@@ -1196,9 +1196,10 b' class InteractiveShell(Configurable, Magic):'
1196 1196 def object_inspect(self, oname):
1197 1197 info = self._object_find(oname)
1198 1198 if info.found:
1199 return self.inspector.info(info.obj, info=info)
1199 return self.inspector.info(info.obj, oname, info=info)
1200 1200 else:
1201 return oinspect.mk_object_info({'found' : False})
1201 return oinspect.mk_object_info({'name' : oname,
1202 'found' : False})
1202 1203
1203 1204 #-------------------------------------------------------------------------
1204 1205 # Things related to history management
@@ -76,18 +76,15 b" info_fields = ['type_name', 'base_class', 'string_form', 'namespace',"
76 76 'call_def', 'call_docstring',
77 77 # These won't be printed but will be used to determine how to
78 78 # format the object
79 'ismagic', 'isalias', 'argspec', 'found',
79 'ismagic', 'isalias', 'argspec', 'found', 'name',
80 80 ]
81 81
82 82
83 ObjectInfo = namedtuple('ObjectInfo', info_fields)
84
85
86 def mk_object_info(kw):
87 """Make a f"""
83 def object_info(**kw):
84 """Make an object info dict with all fields present."""
88 85 infodict = dict(izip_longest(info_fields, [None]))
89 86 infodict.update(kw)
90 return ObjectInfo(**infodict)
87 return infodict
91 88
92 89
93 90 def getdoc(obj):
@@ -161,11 +158,76 b' def getargspec(obj):'
161 158 func_obj = obj
162 159 elif inspect.ismethod(obj):
163 160 func_obj = obj.im_func
161 elif hasattr(obj, '__call__'):
162 func_obj = obj.__call__
164 163 else:
165 164 raise TypeError('arg is not a Python function')
166 165 args, varargs, varkw = inspect.getargs(func_obj.func_code)
167 166 return args, varargs, varkw, func_obj.func_defaults
168 167
168
169 def format_argspec(argspec):
170 """Format argspect, convenience wrapper around inspect's.
171
172 This takes a dict instead of ordered arguments and calls
173 inspect.format_argspec with the arguments in the necessary order.
174 """
175 return inspect.formatargspec(argspec['args'], argspec['varargs'],
176 argspec['varkw'], argspec['defaults'])
177
178
179 def call_tip(oinfo, format_call=True):
180 """Extract call tip data from an oinfo dict.
181
182 Parameters
183 ----------
184 oinfo : dict
185
186 format_call : bool, optional
187 If True, the call line is formatted and returned as a string. If not, a
188 tuple of (name, argspec) is returned.
189
190 Returns
191 -------
192 call_info : None, str or (str, dict) tuple.
193 When format_call is True, the whole call information is formattted as a
194 single string. Otherwise, the object's name and its argspec dict are
195 returned. If no call information is available, None is returned.
196
197 docstring : str or None
198 The most relevant docstring for calling purposes is returned, if
199 available. The priority is: call docstring for callable instances, then
200 constructor docstring for classes, then main object's docstring otherwise
201 (regular functions).
202 """
203 # Get call definition
204 argspec = oinfo['argspec']
205 if argspec is None:
206 call_line = None
207 else:
208 # Callable objects will have 'self' as their first argument, prune
209 # it out if it's there for clarity (since users do *not* pass an
210 # extra first argument explicitly).
211 try:
212 has_self = argspec['args'][0] == 'self'
213 except (KeyError, IndexError):
214 pass
215 else:
216 if has_self:
217 argspec['args'] = argspec['args'][1:]
218
219 call_line = oinfo['name']+format_argspec(argspec)
220
221 # Now get docstring.
222 # The priority is: call docstring, constructor docstring, main one.
223 doc = oinfo['call_docstring']
224 if doc is None:
225 doc = oinfo['init_docstring']
226 if doc is None:
227 doc = oinfo['docstring']
228
229 return call_line, doc
230
169 231 #****************************************************************************
170 232 # Class definitions
171 233
@@ -178,7 +240,9 b' class myStringIO(StringIO.StringIO):'
178 240
179 241
180 242 class Inspector:
181 def __init__(self,color_table,code_color_table,scheme,
243 def __init__(self, color_table=InspectColors,
244 code_color_table=PyColorize.ANSICodeColors,
245 scheme='NoColor',
182 246 str_detail_level=0):
183 247 self.color_table = color_table
184 248 self.parser = PyColorize.Parser(code_color_table,out='str')
@@ -565,6 +629,7 b' class Inspector:'
565 629 ismagic = info.ismagic
566 630 isalias = info.isalias
567 631 ospace = info.namespace
632
568 633 # Get docstring, special-casing aliases:
569 634 if isalias:
570 635 if not callable(obj):
@@ -583,9 +648,8 b' class Inspector:'
583 648 if formatter is not None:
584 649 ds = formatter(ds)
585 650
586 # store output in a dict, we'll later convert it to an ObjectInfo. We
587 # initialize it here and fill it as we go
588 out = dict(found=True, isalias=isalias, ismagic=ismagic)
651 # store output in a dict, we initialize it here and fill it as we go
652 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
589 653
590 654 string_max = 200 # max size of strings to show (snipped if longer)
591 655 shalf = int((string_max -5)/2)
@@ -653,14 +717,11 b' class Inspector:'
653 717 defln = self._getdef(obj,oname)
654 718 if defln:
655 719 out['definition'] = self.format(defln)
656 args, varargs, varkw, func_defaults = getargspec(obj)
657 out['argspec'] = dict(args=args, varargs=varargs,
658 varkw=varkw, func_defaults=func_defaults)
659 720
660 721 # Docstrings only in detail 0 mode, since source contains them (we
661 722 # avoid repetitions). If source fails, we add them back, see below.
662 723 if ds and detail_level == 0:
663 out['docstring'] = indent(ds)
724 out['docstring'] = ds
664 725
665 726 # Original source code for any callable
666 727 if detail_level:
@@ -700,11 +761,11 b' class Inspector:'
700 761 if init_def:
701 762 out['init_definition'] = self.format(init_def)
702 763 if init_ds:
703 out['init_docstring'] = indent(init_ds)
764 out['init_docstring'] = init_ds
765
704 766 # and class docstring for instances:
705 767 elif obj_type is types.InstanceType or \
706 768 isinstance(obj,object):
707
708 769 # First, check whether the instance docstring is identical to the
709 770 # class one, and print it separately if they don't coincide. In
710 771 # most cases they will, but it's nice to print all the info for
@@ -723,7 +784,7 b' class Inspector:'
723 784 class_ds.startswith('module(name[,') ):
724 785 class_ds = None
725 786 if class_ds and ds != class_ds:
726 out['class_docstring'] = indent(class_ds)
787 out['class_docstring'] = class_ds
727 788
728 789 # Next, try to show constructor docstrings
729 790 try:
@@ -735,7 +796,7 b' class Inspector:'
735 796 except AttributeError:
736 797 init_ds = None
737 798 if init_ds:
738 out['init_docstring'] = indent(init_ds)
799 out['init_docstring'] = init_ds
739 800
740 801 # Call form docstring for callable instances
741 802 if hasattr(obj,'__call__'):
@@ -747,9 +808,30 b' class Inspector:'
747 808 if call_ds and call_ds.startswith('x.__call__(...) <==> x(...)'):
748 809 call_ds = None
749 810 if call_ds:
750 out['call_docstring'] = indent(call_ds)
811 out['call_docstring'] = call_ds
812
813 # Compute the object's argspec as a callable. The key is to decide
814 # whether to pull it from the object itself, from its __init__ or
815 # from its __call__ method.
816
817 if inspect.isclass(obj):
818 callable_obj = obj.__init__
819 elif callable(obj):
820 callable_obj = obj
821 else:
822 callable_obj = None
823
824 if callable_obj:
825 try:
826 args, varargs, varkw, defaults = getargspec(callable_obj)
827 except (TypeError, AttributeError):
828 # For extensions/builtins we can't retrieve the argspec
829 pass
830 else:
831 out['argspec'] = dict(args=args, varargs=varargs,
832 varkw=varkw, defaults=defaults)
751 833
752 return mk_object_info(out)
834 return object_info(**out)
753 835
754 836
755 837 def psearch(self,pattern,ns_table,ns_search=[],
@@ -122,15 +122,20 b' class CallTipWidget(QtGui.QLabel):'
122 122 # 'CallTipWidget' interface
123 123 #--------------------------------------------------------------------------
124 124
125 def show_docstring(self, doc, maxlines=20):
126 """ Attempts to show the specified docstring at the current cursor
127 location. The docstring is dedented and possibly truncated for
125 def show_call_info(self, call_line=None, doc=None, maxlines=20):
126 """ Attempts to show the specified call line and docstring at the
127 current cursor location. The docstring is possibly truncated for
128 128 length.
129 129 """
130 doc = dedent(doc.rstrip()).lstrip()
130 if doc:
131 131 match = re.match("(?:[^\n]*\n){%i}" % maxlines, doc)
132 132 if match:
133 133 doc = doc[:match.end()] + '\n[Documentation continues...]'
134 else:
135 doc = ''
136
137 if call_line:
138 doc = '\n\n'.join([call_line, doc])
134 139 return self.show_tip(doc)
135 140
136 141 def show_tip(self, tip):
@@ -10,6 +10,7 b' from PyQt4 import QtCore, QtGui'
10 10
11 11 # Local imports
12 12 from IPython.core.inputsplitter import InputSplitter, transform_classic_prompt
13 from IPython.core.oinspect import call_tip
13 14 from IPython.frontend.qt.base_frontend_mixin import BaseFrontendMixin
14 15 from IPython.utils.traitlets import Bool
15 16 from bracket_matcher import BracketMatcher
@@ -334,9 +335,13 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
334 335 info = self._request_info.get('call_tip')
335 336 if info and info.id == rep['parent_header']['msg_id'] and \
336 337 info.pos == cursor.position():
337 doc = rep['content']['docstring']
338 if doc:
339 self._call_tip_widget.show_docstring(doc)
338 # Get the information for a call tip. For now we format the call
339 # line as string, later we can pass False to format_call and
340 # syntax-highlight it ourselves for nicer formatting in the
341 # calltip.
342 call_info, doc = call_tip(rep['content'], format_call=True)
343 if call_info or doc:
344 self._call_tip_widget.show_call_info(call_info, doc)
340 345
341 346 def _handle_pyout(self, msg):
342 347 """ Handle display hook output.
@@ -303,9 +303,8 b' class Kernel(Configurable):'
303 303
304 304 def object_info_request(self, ident, parent):
305 305 object_info = self.shell.object_inspect(parent['content']['oname'])
306 # Before we send this object over, we turn it into a dict and we scrub
307 # it for JSON usage
308 oinfo = json_clean(object_info._asdict())
306 # Before we send this object over, we scrub it for JSON usage
307 oinfo = json_clean(object_info)
309 308 msg = self.session.send(self.reply_socket, 'object_info_reply',
310 309 oinfo, parent, ident)
311 310 io.raw_print(msg)
@@ -462,6 +462,9 b' field names that IPython prints at the terminal.'
462 462 Message type: ``object_info_reply``::
463 463
464 464 content = {
465 # The name the object was requested under
466 'name' : str,
467
465 468 # Boolean flag indicating whether the named object was found or not. If
466 469 # it's false, all other fields will be empty.
467 470 'found' : bool,
@@ -511,7 +514,7 b' Message type: ``object_info_reply``::'
511 514 # that these must be matched *in reverse* with the 'args'
512 515 # list above, since the first positional args have no default
513 516 # value at all.
514 func_defaults : list,
517 defaults : list,
515 518 },
516 519
517 520 # For instances, provide the constructor signature (the definition of
General Comments 0
You need to be logged in to leave comments. Login now