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.
13 from HyperParser
import HyperParser
21 ("Show call tip", "<<force-open-calltip>>"),
25 def __init__(self
, editwin
=None):
26 if editwin
is None: # subprocess and test
29 self
.editwin
= editwin
30 self
.text
= editwin
.text
32 self
._make
_calltip
_window
= self
._make
_tk
_calltip
_window
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):
43 self
.calltip
.hidetip()
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.
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
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,
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('(')
73 hp
.set_index(sur_paren
[0])
74 name
= hp
.get_expression()
75 if not name
or (not evalfuncs
and name
.find('(') != -1):
77 arg_text
= self
.fetch_tip(name
)
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.
98 rpcclt
= self
.editwin
.flist
.pyshell
.interp
.rpcclt
102 return rpcclt
.remotecall("exec", "get_the_calltip",
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__"
111 namespace
= sys
.modules
.copy()
112 namespace
.update(__main__
.__dict
__)
114 return eval(name
, namespace
)
115 except (NameError, AttributeError):
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.
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
129 def get_arg_text(ob
):
130 """Get a string describing the arguments for the given object"""
134 if type(ob
) in (types
.ClassType
, types
.TypeType
):
135 # Look for the highest __init__ in the class chain.
136 fob
= _find_constructor(ob
)
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.
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:
158 if fob
.func_code
.co_flags
& 0x8:
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__", "")
167 if pos
< 0 or pos
> 70:
171 arg_text
+= doc
[:pos
]
174 #################################################
178 if __name__
=='__main__':
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>)"
190 def __init__(self
, ai
=None, *b
): "(ai=None, ...)"
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>)"
203 expected
= t
.__doc
__ + "\n" + t
.__doc
__
205 # exercise fetch_tip(), not just get_arg_text()
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
:
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
))
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
)