blob: 3c8c096d23c9d38946799d3ad7b5d909cfa80920 [file] [log] [blame]
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +00001"""CallTips.py - An IDLE Extension to Jog Your Memory
David Scherer7aced172000-08-15 01:13:23 +00002
Kurt B. Kaisere54710b2002-12-12 19:15:39 +00003Call Tips are floating windows which display function, class, and method
4parameter and docstring information when you type an opening parenthesis, and
5which disappear when you type a closing parenthesis.
Thomas Wouterscf297e42007-02-23 15:07:44 +00006
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +00007"""
Thomas Wouterscf297e42007-02-23 15:07:44 +00008import re
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +00009import sys
David Scherer7aced172000-08-15 01:13:23 +000010import types
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000011import inspect
David Scherer7aced172000-08-15 01:13:23 +000012
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +000013from idlelib import CallTipWindow
14from idlelib.HyperParser import HyperParser
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000015
16import __main__
17
David Scherer7aced172000-08-15 01:13:23 +000018class CallTips:
19
20 menudefs = [
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000021 ('edit', [
22 ("Show call tip", "<<force-open-calltip>>"),
23 ])
David Scherer7aced172000-08-15 01:13:23 +000024 ]
25
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000026 def __init__(self, editwin=None):
Raymond Hettingerc5e378d2004-05-04 08:34:56 +000027 if editwin is None: # subprocess and test
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000028 self.editwin = None
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000029 else:
30 self.editwin = editwin
31 self.text = editwin.text
32 self.active_calltip = None
33 self._calltip_window = self._make_tk_calltip_window
David Scherer7aced172000-08-15 01:13:23 +000034
35 def close(self):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000036 self._calltip_window = None
David Scherer7aced172000-08-15 01:13:23 +000037
David Scherer7aced172000-08-15 01:13:23 +000038 def _make_tk_calltip_window(self):
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000039 # See __init__ for usage
David Scherer7aced172000-08-15 01:13:23 +000040 return CallTipWindow.CallTip(self.text)
41
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000042 def _remove_calltip_window(self, event=None):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000043 if self.active_calltip:
44 self.active_calltip.hidetip()
45 self.active_calltip = None
Kurt B. Kaiserae676472001-07-12 23:10:35 +000046
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000047 def force_open_calltip_event(self, event):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000048 "The user selected the menu entry or hotkey, open the tip."
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000049 self.open_calltip(True)
50
51 def try_open_calltip_event(self, event):
52 """Happens when it would be nice to open a CallTip, but not really
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000053 necessary, for example after an opening bracket, so function calls
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000054 won't be made.
55 """
56 self.open_calltip(False)
57
58 def refresh_calltip_event(self, event):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000059 if self.active_calltip and self.active_calltip.is_active():
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000060 self.open_calltip(False)
61
62 def open_calltip(self, evalfuncs):
David Scherer7aced172000-08-15 01:13:23 +000063 self._remove_calltip_window()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000064
65 hp = HyperParser(self.editwin, "insert")
66 sur_paren = hp.get_surrounding_brackets('(')
67 if not sur_paren:
68 return
69 hp.set_index(sur_paren[0])
Terry Jan Reedye606e232012-06-03 00:27:54 -040070 expression = hp.get_expression()
71 if not expression:
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000072 return
Terry Jan Reedye606e232012-06-03 00:27:54 -040073 if not evalfuncs and (expression.find('(') != -1):
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000074 return
Terry Jan Reedye606e232012-06-03 00:27:54 -040075 argspec = self.fetch_tip(expression)
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000076 if not argspec:
77 return
78 self.active_calltip = self._calltip_window()
79 self.active_calltip.showtip(argspec, sur_paren[0], sur_paren[1])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000080
Terry Jan Reedye606e232012-06-03 00:27:54 -040081 def fetch_tip(self, expression):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000082 """Return the argument list and docstring of a function or class.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000083
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000084 If there is a Python subprocess, get the calltip there. Otherwise,
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000085 either this fetch_tip() is running in the subprocess or it was
86 called in an IDLE running without the subprocess.
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000087
88 The subprocess environment is that of the most recently run script. If
89 two unrelated modules are being edited some calltips in the current
90 module may be inoperative if the module was not the last to run.
91
Thomas Wouterscf297e42007-02-23 15:07:44 +000092 To find methods, fetch_tip must be fed a fully qualified name.
93
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000094 """
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000095 try:
96 rpcclt = self.editwin.flist.pyshell.interp.rpcclt
Terry Jan Reedye606e232012-06-03 00:27:54 -040097 except AttributeError:
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000098 rpcclt = None
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000099 if rpcclt:
100 return rpcclt.remotecall("exec", "get_the_calltip",
Terry Jan Reedye606e232012-06-03 00:27:54 -0400101 (expression,), {})
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000102 else:
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400103 return get_argspec(get_entity(expression))
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000104
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400105def get_entity(expression):
106 """Return the object corresponding to expression evaluated
107 in a namespace spanning sys.modules and __main.dict__.
108 """
109 if expression:
110 namespace = sys.modules.copy()
111 namespace.update(__main__.__dict__)
112 try:
113 return eval(expression, namespace)
114 except BaseException:
115 # An uncaught exception closes idle, and eval can raise any
116 # exception, especially if user classes are involved.
117 return None
David Scherer7aced172000-08-15 01:13:23 +0000118
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400119# The following are used in both get_argspec and tests
Terry Jan Reedy44dea9d2012-07-09 00:13:21 -0400120_first_param = re.compile('(?<=\()\w*\,?\s*')
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400121_default_callable_argspec = "No docstring, see docs."
David Scherer7aced172000-08-15 01:13:23 +0000122
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000123def get_argspec(ob):
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400124 '''Return a string describing the arguments and return of a callable object.
125
126 For Python-coded functions and methods, the first line is introspected.
127 Delete 'self' parameter for classes (.__init__) and bound methods.
128 The last line is the first line of the doc string. For builtins, this typically
129 includes the arguments in addition to the return value.
130
131 '''
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000132 argspec = ""
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400133 if hasattr(ob, '__call__'):
Guido van Rossum13257902007-06-07 23:15:56 +0000134 if isinstance(ob, type):
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400135 fob = getattr(ob, '__init__', None)
136 elif isinstance(ob.__call__, types.MethodType):
137 fob = ob.__call__
David Scherer7aced172000-08-15 01:13:23 +0000138 else:
139 fob = ob
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400140 if isinstance(fob, (types.FunctionType, types.MethodType)):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000141 argspec = inspect.formatargspec(*inspect.getfullargspec(fob))
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400142 if (isinstance(ob, (type, types.MethodType)) or
143 isinstance(ob.__call__, types.MethodType)):
Terry Jan Reedy44dea9d2012-07-09 00:13:21 -0400144 argspec = _first_param.sub("", argspec)
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400145
146 if isinstance(ob.__call__, types.MethodType):
147 doc = ob.__call__.__doc__
148 else:
149 doc = getattr(ob, "__doc__", "")
Kurt B. Kaiser908aece2002-09-15 22:02:58 +0000150 if doc:
Kurt B. Kaiser6145a622003-07-23 15:42:14 +0000151 doc = doc.lstrip()
Kurt B. Kaiser908aece2002-09-15 22:02:58 +0000152 pos = doc.find("\n")
153 if pos < 0 or pos > 70:
154 pos = 70
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000155 if argspec:
156 argspec += "\n"
157 argspec += doc[:pos]
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400158 if not argspec:
159 argspec = _default_callable_argspec
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000160 return argspec
David Scherer7aced172000-08-15 01:13:23 +0000161
162#################################################
163#
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400164# Test code tests CallTips.fetch_tip, get_entity, and get_argspec
165
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000166def main():
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400167 # Putting expected in docstrings results in doubled tips for test
David Scherer7aced172000-08-15 01:13:23 +0000168 def t1(): "()"
169 def t2(a, b=None): "(a, b=None)"
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000170 def t3(a, *args): "(a, *args)"
171 def t4(*args): "(*args)"
Terry Jan Reedy44dea9d2012-07-09 00:13:21 -0400172 def t5(a, b=None, *args, **kw): "(a, b=None, *args, **kw)"
David Scherer7aced172000-08-15 01:13:23 +0000173
Thomas Wouterscf297e42007-02-23 15:07:44 +0000174 class TC(object):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000175 "(ai=None, *b)"
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400176 def __init__(self, ai=None, *b): "(self, ai=None, *b)"
177 def t1(self): "(self)"
178 def t2(self, ai, b=None): "(self, ai, b=None)"
179 def t3(self, ai, *args): "(self, ai, *args)"
180 def t4(self, *args): "(self, *args)"
Terry Jan Reedy44dea9d2012-07-09 00:13:21 -0400181 def t5(self, ai, b=None, *args, **kw): "(self, ai, b=None, *args, **kw)"
182 def t6(no, self): "(no, self)"
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400183 @classmethod
184 def cm(cls, a): "(cls, a)"
185 @staticmethod
186 def sm(b): "(b)"
Terry Jan Reedy44dea9d2012-07-09 00:13:21 -0400187 def __call__(self, ci): "(self, ci)"
David Scherer7aced172000-08-15 01:13:23 +0000188
189 tc = TC()
David Scherer7aced172000-08-15 01:13:23 +0000190
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400191 # Python classes that inherit builtin methods
192 class Int(int): "Int(x[, base]) -> integer"
193 class List(list): "List() -> new empty list"
194 # Simulate builtin with no docstring for default argspec test
195 class SB: __call__ = None
196
197 __main__.__dict__.update(locals()) # required for get_entity eval()
198
199 num_tests = num_fail = 0
200 tip = CallTips().fetch_tip
201
202 def test(expression, expected):
203 nonlocal num_tests, num_fail
204 num_tests += 1
205 argspec = tip(expression)
206 if argspec != expected:
207 num_fail += 1
208 fmt = "%s - expected\n%r\n - but got\n%r"
209 print(fmt % (expression, expected, argspec))
210
211 def test_builtins():
212 # if first line of a possibly multiline compiled docstring changes,
213 # must change corresponding test string
Chris Jerdonek17519682012-12-09 18:17:27 -0800214 test('int', "int(x=0) -> integer")
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400215 test('Int', Int.__doc__)
216 test('types.MethodType', "method(function, instance)")
217 test('list', "list() -> new empty list")
218 test('List', List.__doc__)
219 test('list.__new__',
220 'T.__new__(S, ...) -> a new object with type S, a subtype of T')
221 test('list.__init__',
222 'x.__init__(...) initializes x; see help(type(x)) for signature')
223 append_doc = "L.append(object) -> None -- append object to end"
224 test('list.append', append_doc)
225 test('[].append', append_doc)
226 test('List.append', append_doc)
227 test('SB()', _default_callable_argspec)
228
229 def test_funcs():
Terry Jan Reedy44dea9d2012-07-09 00:13:21 -0400230 for func in (t1, t2, t3, t4, t5, TC,):
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400231 fdoc = func.__doc__
232 test(func.__name__, fdoc + "\n" + fdoc)
Terry Jan Reedy44dea9d2012-07-09 00:13:21 -0400233 for func in (TC.t1, TC.t2, TC.t3, TC.t4, TC.t5, TC.t6, TC.sm,
234 TC.__call__):
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400235 fdoc = func.__doc__
236 test('TC.'+func.__name__, fdoc + "\n" + fdoc)
Terry Jan Reedy44dea9d2012-07-09 00:13:21 -0400237 fdoc = TC.cm.__func__.__doc__
238 test('TC.cm.__func__', fdoc + "\n" + fdoc)
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400239
240 def test_methods():
Terry Jan Reedy44dea9d2012-07-09 00:13:21 -0400241 # test that first parameter is correctly removed from argspec
242 # using _first_param re to calculate expected masks re errors
243 for meth, mdoc in ((tc.t1, "()"), (tc.t4, "(*args)"), (tc.t6, "(self)"),
244 (TC.cm, "(a)"),):
245 test('tc.'+meth.__name__, mdoc + "\n" + meth.__doc__)
246 test('tc', "(ci)" + "\n" + tc.__call__.__doc__)
247 # directly test that re works to delete unicode parameter name
248 uni = "(A\u0391\u0410\u05d0\u0627\u0905\u1e00\u3042, a)" # various As
249 assert _first_param.sub('', uni) == '(a)'
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400250
251 def test_non_callables():
252 # expression evaluates, but not to a callable
253 for expr in ('0', '0.0' 'num_tests', b'num_tests', '[]', '{}'):
254 test(expr, '')
255 # expression does not evaluate, but raises an exception
256 for expr in ('1a', 'xyx', 'num_tests.xyz', '[int][1]', '{0:int}[1]'):
257 test(expr, '')
258
259 test_builtins()
260 test_funcs()
261 test_non_callables()
262 test_methods()
263
264 print("%d of %d tests failed" % (num_fail, num_tests))
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000265
266if __name__ == '__main__':
267 main()