blob: ce09983eec75a92e29ae0522808612db5b689622 [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.
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +00006
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +00007"""
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +00008import re
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +00009import sys
David Scherer7aced172000-08-15 01:13:23 +000010import types
11
Florent Xiclunad630c042010-04-02 07:24:52 +000012from idlelib import CallTipWindow
13from idlelib.HyperParser import HyperParser
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000014
15import __main__
16
David Scherer7aced172000-08-15 01:13:23 +000017class CallTips:
18
19 menudefs = [
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000020 ('edit', [
21 ("Show call tip", "<<force-open-calltip>>"),
22 ])
David Scherer7aced172000-08-15 01:13:23 +000023 ]
24
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000025 def __init__(self, editwin=None):
Raymond Hettingerc5e378d2004-05-04 08:34:56 +000026 if editwin is None: # subprocess and test
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000027 self.editwin = None
28 return
David Scherer7aced172000-08-15 01:13:23 +000029 self.editwin = editwin
30 self.text = editwin.text
31 self.calltip = None
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000032 self._make_calltip_window = self._make_tk_calltip_window
David Scherer7aced172000-08-15 01:13:23 +000033
34 def close(self):
35 self._make_calltip_window = None
36
David Scherer7aced172000-08-15 01:13:23 +000037 def _make_tk_calltip_window(self):
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000038 # See __init__ for usage
David Scherer7aced172000-08-15 01:13:23 +000039 return CallTipWindow.CallTip(self.text)
40
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000041 def _remove_calltip_window(self, event=None):
David Scherer7aced172000-08-15 01:13:23 +000042 if self.calltip:
43 self.calltip.hidetip()
44 self.calltip = None
Kurt B. Kaiserae676472001-07-12 23:10:35 +000045
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000046 def force_open_calltip_event(self, event):
47 """Happens when the user really wants to open a CallTip, even if a
48 function call is needed.
49 """
50 self.open_calltip(True)
51
52 def try_open_calltip_event(self, event):
53 """Happens when it would be nice to open a CallTip, but not really
Mark Dickinson3e4caeb2009-02-21 20:27:01 +000054 necessary, for example after an opening bracket, so function calls
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000055 won't be made.
56 """
57 self.open_calltip(False)
58
59 def refresh_calltip_event(self, event):
60 """If there is already a calltip window, check if it is still needed,
61 and if so, reload it.
62 """
63 if self.calltip and self.calltip.is_active():
64 self.open_calltip(False)
65
66 def open_calltip(self, evalfuncs):
David Scherer7aced172000-08-15 01:13:23 +000067 self._remove_calltip_window()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000068
69 hp = HyperParser(self.editwin, "insert")
70 sur_paren = hp.get_surrounding_brackets('(')
71 if not sur_paren:
72 return
73 hp.set_index(sur_paren[0])
74 name = hp.get_expression()
75 if not name or (not evalfuncs and name.find('(') != -1):
76 return
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000077 arg_text = self.fetch_tip(name)
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000078 if not arg_text:
79 return
80 self.calltip = self._make_calltip_window()
81 self.calltip.showtip(arg_text, sur_paren[0], sur_paren[1])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000082
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000083 def fetch_tip(self, name):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000084 """Return the argument list and docstring of a function or class
85
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000086 If there is a Python subprocess, get the calltip there. Otherwise,
87 either fetch_tip() is running in the subprocess itself or it was called
88 in an IDLE EditorWindow before any script had been run.
89
90 The subprocess environment is that of the most recently run script. If
91 two unrelated modules are being edited some calltips in the current
92 module may be inoperative if the module was not the last to run.
93
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +000094 To find methods, fetch_tip must be fed a fully qualified name.
95
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000096 """
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000097 try:
98 rpcclt = self.editwin.flist.pyshell.interp.rpcclt
99 except:
100 rpcclt = None
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000101 if rpcclt:
102 return rpcclt.remotecall("exec", "get_the_calltip",
103 (name,), {})
104 else:
105 entity = self.get_entity(name)
106 return get_arg_text(entity)
107
108 def get_entity(self, name):
Kurt B. Kaisere54710b2002-12-12 19:15:39 +0000109 "Lookup name in a namespace spanning sys.modules and __main.dict__"
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000110 if name:
David Scherer7aced172000-08-15 01:13:23 +0000111 namespace = sys.modules.copy()
112 namespace.update(__main__.__dict__)
113 try:
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000114 return eval(name, namespace)
Terry Jan Reedyd9d276b2012-05-27 21:28:42 -0400115 # any exception is possible if evalfuncs True in open_calltip
116 # at least Syntax, Name, Attribute, Index, and Key E. if not
117 except:
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000118 return None
David Scherer7aced172000-08-15 01:13:23 +0000119
120def _find_constructor(class_ob):
121 # Given a class object, return a function object used for the
122 # constructor (ie, __init__() ) or None if we can't find one.
123 try:
124 return class_ob.__init__.im_func
125 except AttributeError:
126 for base in class_ob.__bases__:
127 rc = _find_constructor(base)
128 if rc is not None: return rc
129 return None
130
131def get_arg_text(ob):
Terry Jan Reedyd9d276b2012-05-27 21:28:42 -0400132 """Get a string describing the arguments for the given object,
133 only if it is callable."""
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000134 arg_text = ""
Terry Jan Reedyd9d276b2012-05-27 21:28:42 -0400135 if ob is not None and hasattr(ob, '__call__'):
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000136 arg_offset = 0
Kurt B. Kaiserc6bacd52006-07-23 04:19:49 +0000137 if type(ob) in (types.ClassType, types.TypeType):
David Scherer7aced172000-08-15 01:13:23 +0000138 # Look for the highest __init__ in the class chain.
139 fob = _find_constructor(ob)
140 if fob is None:
141 fob = lambda: None
142 else:
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000143 arg_offset = 1
David Scherer7aced172000-08-15 01:13:23 +0000144 elif type(ob)==types.MethodType:
145 # bit of a hack for methods - turn it into a function
146 # but we drop the "self" param.
147 fob = ob.im_func
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000148 arg_offset = 1
David Scherer7aced172000-08-15 01:13:23 +0000149 else:
150 fob = ob
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000151 # Try to build one for Python defined functions
David Scherer7aced172000-08-15 01:13:23 +0000152 if type(fob) in [types.FunctionType, types.LambdaType]:
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000153 argcount = fob.func_code.co_argcount
154 real_args = fob.func_code.co_varnames[arg_offset:argcount]
155 defaults = fob.func_defaults or []
156 defaults = list(map(lambda name: "=%s" % repr(name), defaults))
157 defaults = [""] * (len(real_args) - len(defaults)) + defaults
158 items = map(lambda arg, dflt: arg + dflt, real_args, defaults)
159 if fob.func_code.co_flags & 0x4:
160 items.append("...")
161 if fob.func_code.co_flags & 0x8:
162 items.append("***")
163 arg_text = ", ".join(items)
164 arg_text = "(%s)" % re.sub("\.\d+", "<tuple>", arg_text)
David Scherer7aced172000-08-15 01:13:23 +0000165 # See if we can use the docstring
Kurt B. Kaiser6145a622003-07-23 15:42:14 +0000166 doc = getattr(ob, "__doc__", "")
Kurt B. Kaiser908aece2002-09-15 22:02:58 +0000167 if doc:
Kurt B. Kaiser6145a622003-07-23 15:42:14 +0000168 doc = doc.lstrip()
Kurt B. Kaiser908aece2002-09-15 22:02:58 +0000169 pos = doc.find("\n")
170 if pos < 0 or pos > 70:
171 pos = 70
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000172 if arg_text:
173 arg_text += "\n"
174 arg_text += doc[:pos]
175 return arg_text
David Scherer7aced172000-08-15 01:13:23 +0000176
177#################################################
178#
179# Test code
180#
181if __name__=='__main__':
182
183 def t1(): "()"
184 def t2(a, b=None): "(a, b=None)"
185 def t3(a, *args): "(a, ...)"
186 def t4(*args): "(...)"
187 def t5(a, *args): "(a, ...)"
188 def t6(a, b=None, *args, **kw): "(a, b=None, ..., ***)"
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000189 def t7((a, b), c, (d, e)): "(<tuple>, c, <tuple>)"
David Scherer7aced172000-08-15 01:13:23 +0000190
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000191 class TC(object):
192 "(ai=None, ...)"
193 def __init__(self, ai=None, *b): "(ai=None, ...)"
David Scherer7aced172000-08-15 01:13:23 +0000194 def t1(self): "()"
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000195 def t2(self, ai, b=None): "(ai, b=None)"
196 def t3(self, ai, *args): "(ai, ...)"
David Scherer7aced172000-08-15 01:13:23 +0000197 def t4(self, *args): "(...)"
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000198 def t5(self, ai, *args): "(ai, ...)"
199 def t6(self, ai, b=None, *args, **kw): "(ai, b=None, ..., ***)"
200 def t7(self, (ai, b), c, (d, e)): "(<tuple>, c, <tuple>)"
David Scherer7aced172000-08-15 01:13:23 +0000201
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000202 def test(tests):
203 ct = CallTips()
David Scherer7aced172000-08-15 01:13:23 +0000204 failed=[]
205 for t in tests:
206 expected = t.__doc__ + "\n" + t.__doc__
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000207 name = t.__name__
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000208 # exercise fetch_tip(), not just get_arg_text()
209 try:
210 qualified_name = "%s.%s" % (t.im_class.__name__, name)
211 except AttributeError:
212 qualified_name = name
213 arg_text = ct.fetch_tip(qualified_name)
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000214 if arg_text != expected:
David Scherer7aced172000-08-15 01:13:23 +0000215 failed.append(t)
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000216 fmt = "%s - expected %s, but got %s"
217 print fmt % (t.__name__, expected, get_arg_text(t))
David Scherer7aced172000-08-15 01:13:23 +0000218 print "%d of %d tests failed" % (len(failed), len(tests))
219
220 tc = TC()
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000221 tests = (t1, t2, t3, t4, t5, t6, t7,
222 TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6, tc.t7)
David Scherer7aced172000-08-15 01:13:23 +0000223
224 test(tests)