Backport importlib to at least Python 2.5 by getting rid of use of str.format.
[python.git] / Lib / idlelib / CallTips.py
blob7bcc1e26959f284c8ec155c6b2392916925135e1
1 """CallTips.py - An IDLE Extension to Jog Your Memory
3 Call Tips are floating windows which display function, class, and method
4 parameter and docstring information when you type an opening parenthesis, and
5 which disappear when you type a closing parenthesis.
7 """
8 import re
9 import sys
10 import types
12 import CallTipWindow
13 from HyperParser import HyperParser
15 import __main__
17 class CallTips:
19 menudefs = [
20 ('edit', [
21 ("Show call tip", "<<force-open-calltip>>"),
25 def __init__(self, editwin=None):
26 if editwin is None: # subprocess and test
27 self.editwin = None
28 return
29 self.editwin = editwin
30 self.text = editwin.text
31 self.calltip = None
32 self._make_calltip_window = self._make_tk_calltip_window
34 def close(self):
35 self._make_calltip_window = None
37 def _make_tk_calltip_window(self):
38 # See __init__ for usage
39 return CallTipWindow.CallTip(self.text)
41 def _remove_calltip_window(self, event=None):
42 if self.calltip:
43 self.calltip.hidetip()
44 self.calltip = None
46 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)
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)
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)
66 def open_calltip(self, evalfuncs):
67 self._remove_calltip_window()
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
77 arg_text = self.fetch_tip(name)
78 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])
83 def fetch_tip(self, name):
84 """Return the argument list and docstring of a function or class
86 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.
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.
94 To find methods, fetch_tip must be fed a fully qualified name.
96 """
97 try:
98 rpcclt = self.editwin.flist.pyshell.interp.rpcclt
99 except:
100 rpcclt = None
101 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)
108 def get_entity(self, name):
109 "Lookup name in a namespace spanning sys.modules and __main.dict__"
110 if name:
111 namespace = sys.modules.copy()
112 namespace.update(__main__.__dict__)
113 try:
114 return eval(name, namespace)
115 except (NameError, AttributeError):
116 return None
118 def _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
129 def get_arg_text(ob):
130 """Get a string describing the arguments for the given object"""
131 arg_text = ""
132 if ob is not None:
133 arg_offset = 0
134 if type(ob) in (types.ClassType, types.TypeType):
135 # 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:
140 arg_offset = 1
141 elif type(ob)==types.MethodType:
142 # bit of a hack for methods - turn it into a function
143 # but we drop the "self" param.
144 fob = ob.im_func
145 arg_offset = 1
146 else:
147 fob = ob
148 # Try to build one for Python defined functions
149 if type(fob) in [types.FunctionType, types.LambdaType]:
150 argcount = fob.func_code.co_argcount
151 real_args = fob.func_code.co_varnames[arg_offset:argcount]
152 defaults = fob.func_defaults or []
153 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)
156 if fob.func_code.co_flags & 0x4:
157 items.append("...")
158 if fob.func_code.co_flags & 0x8:
159 items.append("***")
160 arg_text = ", ".join(items)
161 arg_text = "(%s)" % re.sub("\.\d+", "<tuple>", arg_text)
162 # See if we can use the docstring
163 doc = getattr(ob, "__doc__", "")
164 if doc:
165 doc = doc.lstrip()
166 pos = doc.find("\n")
167 if pos < 0 or pos > 70:
168 pos = 70
169 if arg_text:
170 arg_text += "\n"
171 arg_text += doc[:pos]
172 return arg_text
174 #################################################
176 # Test code
178 if __name__=='__main__':
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 def t7((a, b), c, (d, e)): "(<tuple>, c, <tuple>)"
188 class TC(object):
189 "(ai=None, ...)"
190 def __init__(self, ai=None, *b): "(ai=None, ...)"
191 def t1(self): "()"
192 def t2(self, ai, b=None): "(ai, b=None)"
193 def t3(self, ai, *args): "(ai, ...)"
194 def t4(self, *args): "(...)"
195 def t5(self, ai, *args): "(ai, ...)"
196 def t6(self, ai, b=None, *args, **kw): "(ai, b=None, ..., ***)"
197 def t7(self, (ai, b), c, (d, e)): "(<tuple>, c, <tuple>)"
199 def test(tests):
200 ct = CallTips()
201 failed=[]
202 for t in tests:
203 expected = t.__doc__ + "\n" + t.__doc__
204 name = t.__name__
205 # exercise fetch_tip(), not just get_arg_text()
206 try:
207 qualified_name = "%s.%s" % (t.im_class.__name__, name)
208 except AttributeError:
209 qualified_name = name
210 arg_text = ct.fetch_tip(qualified_name)
211 if arg_text != expected:
212 failed.append(t)
213 fmt = "%s - expected %s, but got %s"
214 print fmt % (t.__name__, expected, get_arg_text(t))
215 print "%d of %d tests failed" % (len(failed), len(tests))
217 tc = TC()
218 tests = (t1, t2, t3, t4, t5, t6, t7,
219 TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6, tc.t7)
221 test(tests)