##// 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 def object_inspect(self, oname):
1196 def object_inspect(self, oname):
1197 info = self._object_find(oname)
1197 info = self._object_find(oname)
1198 if info.found:
1198 if info.found:
1199 return self.inspector.info(info.obj, info=info)
1199 return self.inspector.info(info.obj, oname, info=info)
1200 else:
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 # Things related to history management
1205 # Things related to history management
@@ -76,18 +76,15 b" info_fields = ['type_name', 'base_class', 'string_form', 'namespace',"
76 'call_def', 'call_docstring',
76 'call_def', 'call_docstring',
77 # These won't be printed but will be used to determine how to
77 # These won't be printed but will be used to determine how to
78 # format the object
78 # format the object
79 'ismagic', 'isalias', 'argspec', 'found',
79 'ismagic', 'isalias', 'argspec', 'found', 'name',
80 ]
80 ]
81
81
82
82
83 ObjectInfo = namedtuple('ObjectInfo', info_fields)
83 def object_info(**kw):
84
84 """Make an object info dict with all fields present."""
85
86 def mk_object_info(kw):
87 """Make a f"""
88 infodict = dict(izip_longest(info_fields, [None]))
85 infodict = dict(izip_longest(info_fields, [None]))
89 infodict.update(kw)
86 infodict.update(kw)
90 return ObjectInfo(**infodict)
87 return infodict
91
88
92
89
93 def getdoc(obj):
90 def getdoc(obj):
@@ -161,11 +158,76 b' def getargspec(obj):'
161 func_obj = obj
158 func_obj = obj
162 elif inspect.ismethod(obj):
159 elif inspect.ismethod(obj):
163 func_obj = obj.im_func
160 func_obj = obj.im_func
161 elif hasattr(obj, '__call__'):
162 func_obj = obj.__call__
164 else:
163 else:
165 raise TypeError('arg is not a Python function')
164 raise TypeError('arg is not a Python function')
166 args, varargs, varkw = inspect.getargs(func_obj.func_code)
165 args, varargs, varkw = inspect.getargs(func_obj.func_code)
167 return args, varargs, varkw, func_obj.func_defaults
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 # Class definitions
232 # Class definitions
171
233
@@ -178,7 +240,9 b' class myStringIO(StringIO.StringIO):'
178
240
179
241
180 class Inspector:
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 str_detail_level=0):
246 str_detail_level=0):
183 self.color_table = color_table
247 self.color_table = color_table
184 self.parser = PyColorize.Parser(code_color_table,out='str')
248 self.parser = PyColorize.Parser(code_color_table,out='str')
@@ -565,6 +629,7 b' class Inspector:'
565 ismagic = info.ismagic
629 ismagic = info.ismagic
566 isalias = info.isalias
630 isalias = info.isalias
567 ospace = info.namespace
631 ospace = info.namespace
632
568 # Get docstring, special-casing aliases:
633 # Get docstring, special-casing aliases:
569 if isalias:
634 if isalias:
570 if not callable(obj):
635 if not callable(obj):
@@ -583,9 +648,8 b' class Inspector:'
583 if formatter is not None:
648 if formatter is not None:
584 ds = formatter(ds)
649 ds = formatter(ds)
585
650
586 # store output in a dict, we'll later convert it to an ObjectInfo. We
651 # store output in a dict, we initialize it here and fill it as we go
587 # initialize it here and fill it as we go
652 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
588 out = dict(found=True, isalias=isalias, ismagic=ismagic)
589
653
590 string_max = 200 # max size of strings to show (snipped if longer)
654 string_max = 200 # max size of strings to show (snipped if longer)
591 shalf = int((string_max -5)/2)
655 shalf = int((string_max -5)/2)
@@ -650,17 +714,14 b' class Inspector:'
650 binary_file = True
714 binary_file = True
651
715
652 # reconstruct the function definition and print it:
716 # reconstruct the function definition and print it:
653 defln = self._getdef(obj,oname)
717 defln = self._getdef(obj, oname)
654 if defln:
718 if defln:
655 out['definition'] = self.format(defln)
719 out['definition'] = self.format(defln)
656 args, varargs, varkw, func_defaults = getargspec(obj)
720
657 out['argspec'] = dict(args=args, varargs=varargs,
658 varkw=varkw, func_defaults=func_defaults)
659
660 # Docstrings only in detail 0 mode, since source contains them (we
721 # Docstrings only in detail 0 mode, since source contains them (we
661 # avoid repetitions). If source fails, we add them back, see below.
722 # avoid repetitions). If source fails, we add them back, see below.
662 if ds and detail_level == 0:
723 if ds and detail_level == 0:
663 out['docstring'] = indent(ds)
724 out['docstring'] = ds
664
725
665 # Original source code for any callable
726 # Original source code for any callable
666 if detail_level:
727 if detail_level:
@@ -700,11 +761,11 b' class Inspector:'
700 if init_def:
761 if init_def:
701 out['init_definition'] = self.format(init_def)
762 out['init_definition'] = self.format(init_def)
702 if init_ds:
763 if init_ds:
703 out['init_docstring'] = indent(init_ds)
764 out['init_docstring'] = init_ds
765
704 # and class docstring for instances:
766 # and class docstring for instances:
705 elif obj_type is types.InstanceType or \
767 elif obj_type is types.InstanceType or \
706 isinstance(obj,object):
768 isinstance(obj, object):
707
708 # First, check whether the instance docstring is identical to the
769 # First, check whether the instance docstring is identical to the
709 # class one, and print it separately if they don't coincide. In
770 # class one, and print it separately if they don't coincide. In
710 # most cases they will, but it's nice to print all the info for
771 # most cases they will, but it's nice to print all the info for
@@ -723,7 +784,7 b' class Inspector:'
723 class_ds.startswith('module(name[,') ):
784 class_ds.startswith('module(name[,') ):
724 class_ds = None
785 class_ds = None
725 if class_ds and ds != class_ds:
786 if class_ds and ds != class_ds:
726 out['class_docstring'] = indent(class_ds)
787 out['class_docstring'] = class_ds
727
788
728 # Next, try to show constructor docstrings
789 # Next, try to show constructor docstrings
729 try:
790 try:
@@ -735,11 +796,11 b' class Inspector:'
735 except AttributeError:
796 except AttributeError:
736 init_ds = None
797 init_ds = None
737 if init_ds:
798 if init_ds:
738 out['init_docstring'] = indent(init_ds)
799 out['init_docstring'] = init_ds
739
800
740 # Call form docstring for callable instances
801 # Call form docstring for callable instances
741 if hasattr(obj,'__call__'):
802 if hasattr(obj, '__call__'):
742 call_def = self._getdef(obj.__call__,oname)
803 call_def = self._getdef(obj.__call__, oname)
743 if call_def is not None:
804 if call_def is not None:
744 out['call_def'] = self.format(call_def)
805 out['call_def'] = self.format(call_def)
745 call_ds = getdoc(obj.__call__)
806 call_ds = getdoc(obj.__call__)
@@ -747,9 +808,30 b' class Inspector:'
747 if call_ds and call_ds.startswith('x.__call__(...) <==> x(...)'):
808 if call_ds and call_ds.startswith('x.__call__(...) <==> x(...)'):
748 call_ds = None
809 call_ds = None
749 if call_ds:
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 def psearch(self,pattern,ns_table,ns_search=[],
837 def psearch(self,pattern,ns_table,ns_search=[],
@@ -122,15 +122,20 b' class CallTipWidget(QtGui.QLabel):'
122 # 'CallTipWidget' interface
122 # 'CallTipWidget' interface
123 #--------------------------------------------------------------------------
123 #--------------------------------------------------------------------------
124
124
125 def show_docstring(self, doc, maxlines=20):
125 def show_call_info(self, call_line=None, doc=None, maxlines=20):
126 """ Attempts to show the specified docstring at the current cursor
126 """ Attempts to show the specified call line and docstring at the
127 location. The docstring is dedented and possibly truncated for
127 current cursor location. The docstring is possibly truncated for
128 length.
128 length.
129 """
129 """
130 doc = dedent(doc.rstrip()).lstrip()
130 if doc:
131 match = re.match("(?:[^\n]*\n){%i}" % maxlines, doc)
131 match = re.match("(?:[^\n]*\n){%i}" % maxlines, doc)
132 if match:
132 if match:
133 doc = doc[:match.end()] + '\n[Documentation continues...]'
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 return self.show_tip(doc)
139 return self.show_tip(doc)
135
140
136 def show_tip(self, tip):
141 def show_tip(self, tip):
@@ -10,6 +10,7 b' from PyQt4 import QtCore, QtGui'
10
10
11 # Local imports
11 # Local imports
12 from IPython.core.inputsplitter import InputSplitter, transform_classic_prompt
12 from IPython.core.inputsplitter import InputSplitter, transform_classic_prompt
13 from IPython.core.oinspect import call_tip
13 from IPython.frontend.qt.base_frontend_mixin import BaseFrontendMixin
14 from IPython.frontend.qt.base_frontend_mixin import BaseFrontendMixin
14 from IPython.utils.traitlets import Bool
15 from IPython.utils.traitlets import Bool
15 from bracket_matcher import BracketMatcher
16 from bracket_matcher import BracketMatcher
@@ -334,9 +335,13 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
334 info = self._request_info.get('call_tip')
335 info = self._request_info.get('call_tip')
335 if info and info.id == rep['parent_header']['msg_id'] and \
336 if info and info.id == rep['parent_header']['msg_id'] and \
336 info.pos == cursor.position():
337 info.pos == cursor.position():
337 doc = rep['content']['docstring']
338 # Get the information for a call tip. For now we format the call
338 if doc:
339 # line as string, later we can pass False to format_call and
339 self._call_tip_widget.show_docstring(doc)
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 def _handle_pyout(self, msg):
346 def _handle_pyout(self, msg):
342 """ Handle display hook output.
347 """ Handle display hook output.
@@ -303,9 +303,8 b' class Kernel(Configurable):'
303
303
304 def object_info_request(self, ident, parent):
304 def object_info_request(self, ident, parent):
305 object_info = self.shell.object_inspect(parent['content']['oname'])
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
306 # Before we send this object over, we scrub it for JSON usage
307 # it for JSON usage
307 oinfo = json_clean(object_info)
308 oinfo = json_clean(object_info._asdict())
309 msg = self.session.send(self.reply_socket, 'object_info_reply',
308 msg = self.session.send(self.reply_socket, 'object_info_reply',
310 oinfo, parent, ident)
309 oinfo, parent, ident)
311 io.raw_print(msg)
310 io.raw_print(msg)
@@ -462,6 +462,9 b' field names that IPython prints at the terminal.'
462 Message type: ``object_info_reply``::
462 Message type: ``object_info_reply``::
463
463
464 content = {
464 content = {
465 # The name the object was requested under
466 'name' : str,
467
465 # Boolean flag indicating whether the named object was found or not. If
468 # Boolean flag indicating whether the named object was found or not. If
466 # it's false, all other fields will be empty.
469 # it's false, all other fields will be empty.
467 'found' : bool,
470 'found' : bool,
@@ -511,7 +514,7 b' Message type: ``object_info_reply``::'
511 # that these must be matched *in reverse* with the 'args'
514 # that these must be matched *in reverse* with the 'args'
512 # list above, since the first positional args have no default
515 # list above, since the first positional args have no default
513 # value at all.
516 # value at all.
514 func_defaults : list,
517 defaults : list,
515 },
518 },
516
519
517 # For instances, provide the constructor signature (the definition of
520 # For instances, provide the constructor signature (the definition of
General Comments 0
You need to be logged in to leave comments. Login now