blob: 6f81af48914e4e5b5264b3cb11589b1b8b0663eb [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
11
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000012import CallTipWindow
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000013from 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
54 neccesary, for example after an opening bracket, so function calls
55 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
Thomas Wouterscf297e42007-02-23 15:07:44 +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)
Thomas Wouterscf297e42007-02-23 15:07:44 +0000115 except (NameError, AttributeError):
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000116 return None
David Scherer7aced172000-08-15 01:13:23 +0000117
118def _find_constructor(class_ob):
119 # Given a class object, return a function object used for the
120 # constructor (ie, __init__() ) or None if we can't find one.
121 try:
122 return class_ob.__init__.im_func
123 except AttributeError:
124 for base in class_ob.__bases__:
125 rc = _find_constructor(base)
126 if rc is not None: return rc
127 return None
128
129def get_arg_text(ob):
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000130 """Get a string describing the arguments for the given object"""
Thomas Wouterscf297e42007-02-23 15:07:44 +0000131 arg_text = ""
David Scherer7aced172000-08-15 01:13:23 +0000132 if ob is not None:
Thomas Wouterscf297e42007-02-23 15:07:44 +0000133 arg_offset = 0
Guido van Rossum13257902007-06-07 23:15:56 +0000134 if isinstance(ob, type):
David Scherer7aced172000-08-15 01:13:23 +0000135 # Look for the highest __init__ in the class chain.
136 fob = _find_constructor(ob)
137 if fob is None:
138 fob = lambda: None
139 else:
Thomas Wouterscf297e42007-02-23 15:07:44 +0000140 arg_offset = 1
Guido van Rossum13257902007-06-07 23:15:56 +0000141 elif isinstace(ob, types.MethodType):
David Scherer7aced172000-08-15 01:13:23 +0000142 # bit of a hack for methods - turn it into a function
143 # but we drop the "self" param.
144 fob = ob.im_func
Thomas Wouterscf297e42007-02-23 15:07:44 +0000145 arg_offset = 1
David Scherer7aced172000-08-15 01:13:23 +0000146 else:
147 fob = ob
Thomas Wouterscf297e42007-02-23 15:07:44 +0000148 # Try to build one for Python defined functions
Guido van Rossum13257902007-06-07 23:15:56 +0000149 if isinstance(fob, (types.FunctionType, types.LambdaType)):
Neal Norwitz221085d2007-02-25 20:55:47 +0000150 argcount = fob.__code__.co_argcount
151 real_args = fob.__code__.co_varnames[arg_offset:argcount]
152 defaults = fob.__defaults__ or []
Thomas Wouterscf297e42007-02-23 15:07:44 +0000153 defaults = list(map(lambda name: "=%s" % repr(name), defaults))
154 defaults = [""] * (len(real_args) - len(defaults)) + defaults
155 items = map(lambda arg, dflt: arg + dflt, real_args, defaults)
Neal Norwitz221085d2007-02-25 20:55:47 +0000156 if fob.__code__.co_flags & 0x4:
Thomas Wouterscf297e42007-02-23 15:07:44 +0000157 items.append("...")
Neal Norwitz221085d2007-02-25 20:55:47 +0000158 if fob.__code__.co_flags & 0x8:
Thomas Wouterscf297e42007-02-23 15:07:44 +0000159 items.append("***")
160 arg_text = ", ".join(items)
161 arg_text = "(%s)" % re.sub("\.\d+", "<tuple>", arg_text)
David Scherer7aced172000-08-15 01:13:23 +0000162 # See if we can use the docstring
Kurt B. Kaiser6145a622003-07-23 15:42:14 +0000163 doc = getattr(ob, "__doc__", "")
Kurt B. Kaiser908aece2002-09-15 22:02:58 +0000164 if doc:
Kurt B. Kaiser6145a622003-07-23 15:42:14 +0000165 doc = doc.lstrip()
Kurt B. Kaiser908aece2002-09-15 22:02:58 +0000166 pos = doc.find("\n")
167 if pos < 0 or pos > 70:
168 pos = 70
Thomas Wouterscf297e42007-02-23 15:07:44 +0000169 if arg_text:
170 arg_text += "\n"
171 arg_text += doc[:pos]
172 return arg_text
David Scherer7aced172000-08-15 01:13:23 +0000173
174#################################################
175#
176# Test code
177#
178if __name__=='__main__':
179
180 def t1(): "()"
181 def t2(a, b=None): "(a, b=None)"
182 def t3(a, *args): "(a, ...)"
183 def t4(*args): "(...)"
184 def t5(a, *args): "(a, ...)"
185 def t6(a, b=None, *args, **kw): "(a, b=None, ..., ***)"
186
Thomas Wouterscf297e42007-02-23 15:07:44 +0000187 class TC(object):
188 "(ai=None, ...)"
189 def __init__(self, ai=None, *b): "(ai=None, ...)"
David Scherer7aced172000-08-15 01:13:23 +0000190 def t1(self): "()"
Thomas Wouterscf297e42007-02-23 15:07:44 +0000191 def t2(self, ai, b=None): "(ai, b=None)"
192 def t3(self, ai, *args): "(ai, ...)"
David Scherer7aced172000-08-15 01:13:23 +0000193 def t4(self, *args): "(...)"
Thomas Wouterscf297e42007-02-23 15:07:44 +0000194 def t5(self, ai, *args): "(ai, ...)"
195 def t6(self, ai, b=None, *args, **kw): "(ai, b=None, ..., ***)"
David Scherer7aced172000-08-15 01:13:23 +0000196
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000197 def test(tests):
198 ct = CallTips()
David Scherer7aced172000-08-15 01:13:23 +0000199 failed=[]
200 for t in tests:
201 expected = t.__doc__ + "\n" + t.__doc__
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000202 name = t.__name__
Thomas Wouterscf297e42007-02-23 15:07:44 +0000203 # exercise fetch_tip(), not just get_arg_text()
204 try:
205 qualified_name = "%s.%s" % (t.im_class.__name__, name)
206 except AttributeError:
207 qualified_name = name
208 arg_text = ct.fetch_tip(qualified_name)
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000209 if arg_text != expected:
David Scherer7aced172000-08-15 01:13:23 +0000210 failed.append(t)
Thomas Wouterscf297e42007-02-23 15:07:44 +0000211 fmt = "%s - expected %s, but got %s"
212 print(fmt % (t.__name__, expected, get_arg_text(t)))
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000213 print("%d of %d tests failed" % (len(failed), len(tests)))
David Scherer7aced172000-08-15 01:13:23 +0000214
215 tc = TC()
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000216 tests = (t1, t2, t3, t4, t5, t6,
217 TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6)
David Scherer7aced172000-08-15 01:13:23 +0000218
219 test(tests)