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.
11 from HyperParser
import HyperParser
19 ("Show call tip", "<<force-open-calltip>>"),
23 def __init__(self
, editwin
=None):
24 if editwin
is None: # subprocess and test
27 self
.editwin
= editwin
28 self
.text
= editwin
.text
30 self
._make
_calltip
_window
= self
._make
_tk
_calltip
_window
33 self
._make
_calltip
_window
= None
35 def _make_tk_calltip_window(self
):
36 # See __init__ for usage
37 return CallTipWindow
.CallTip(self
.text
)
39 def _remove_calltip_window(self
, event
=None):
41 self
.calltip
.hidetip()
44 def force_open_calltip_event(self
, event
):
45 """Happens when the user really wants to open a CallTip, even if a
46 function call is needed.
48 self
.open_calltip(True)
50 def try_open_calltip_event(self
, event
):
51 """Happens when it would be nice to open a CallTip, but not really
52 neccesary, for example after an opening bracket, so function calls
55 self
.open_calltip(False)
57 def refresh_calltip_event(self
, event
):
58 """If there is already a calltip window, check if it is still needed,
61 if self
.calltip
and self
.calltip
.is_active():
62 self
.open_calltip(False)
64 def open_calltip(self
, evalfuncs
):
65 self
._remove
_calltip
_window
()
67 hp
= HyperParser(self
.editwin
, "insert")
68 sur_paren
= hp
.get_surrounding_brackets('(')
71 hp
.set_index(sur_paren
[0])
72 name
= hp
.get_expression()
73 if not name
or (not evalfuncs
and name
.find('(') != -1):
75 arg_text
= self
.fetch_tip(name
)
78 self
.calltip
= self
._make
_calltip
_window
()
79 self
.calltip
.showtip(arg_text
, sur_paren
[0], sur_paren
[1])
81 def fetch_tip(self
, name
):
82 """Return the argument list and docstring of a function or class
84 If there is a Python subprocess, get the calltip there. Otherwise,
85 either fetch_tip() is running in the subprocess itself or it was called
86 in an IDLE EditorWindow before any script had been run.
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.
94 rpcclt
= self
.editwin
.flist
.pyshell
.interp
.rpcclt
98 return rpcclt
.remotecall("exec", "get_the_calltip",
101 entity
= self
.get_entity(name
)
102 return get_arg_text(entity
)
104 def get_entity(self
, name
):
105 "Lookup name in a namespace spanning sys.modules and __main.dict__"
107 namespace
= sys
.modules
.copy()
108 namespace
.update(__main__
.__dict
__)
110 return eval(name
, namespace
)
114 def _find_constructor(class_ob
):
115 # Given a class object, return a function object used for the
116 # constructor (ie, __init__() ) or None if we can't find one.
118 return class_ob
.__init
__.im_func
119 except AttributeError:
120 for base
in class_ob
.__bases
__:
121 rc
= _find_constructor(base
)
122 if rc
is not None: return rc
125 def get_arg_text(ob
):
126 """Get a string describing the arguments for the given object"""
130 if type(ob
) in (types
.ClassType
, types
.TypeType
):
131 # Look for the highest __init__ in the class chain.
132 fob
= _find_constructor(ob
)
137 elif type(ob
)==types
.MethodType
:
138 # bit of a hack for methods - turn it into a function
139 # but we drop the "self" param.
144 # Try and build one for Python defined functions
145 if type(fob
) in [types
.FunctionType
, types
.LambdaType
]:
147 realArgs
= fob
.func_code
.co_varnames
[argOffset
:fob
.func_code
.co_argcount
]
148 defaults
= fob
.func_defaults
or []
149 defaults
= list(map(lambda name
: "=%s" % repr(name
), defaults
))
150 defaults
= [""] * (len(realArgs
)-len(defaults
)) + defaults
151 items
= map(lambda arg
, dflt
: arg
+dflt
, realArgs
, defaults
)
152 if fob
.func_code
.co_flags
& 0x4:
154 if fob
.func_code
.co_flags
& 0x8:
156 argText
= ", ".join(items
)
157 argText
= "(%s)" % argText
160 # See if we can use the docstring
161 doc
= getattr(ob
, "__doc__", "")
165 if pos
< 0 or pos
> 70:
172 #################################################
176 if __name__
=='__main__':
179 def t2(a
, b
=None): "(a, b=None)"
180 def t3(a
, *args
): "(a, ...)"
181 def t4(*args
): "(...)"
182 def t5(a
, *args
): "(a, ...)"
183 def t6(a
, b
=None, *args
, **kw
): "(a, b=None, ..., ***)"
187 def __init__(self
, a
=None, *b
): "(a=None, ...)"
189 def t2(self
, a
, b
=None): "(a, b=None)"
190 def t3(self
, a
, *args
): "(a, ...)"
191 def t4(self
, *args
): "(...)"
192 def t5(self
, a
, *args
): "(a, ...)"
193 def t6(self
, a
, b
=None, *args
, **kw
): "(a, b=None, ..., ***)"
199 expected
= t
.__doc
__ + "\n" + t
.__doc
__
201 arg_text
= ct
.fetch_tip(name
)
202 if arg_text
!= expected
:
204 print "%s - expected %s, but got %s" % (t
, expected
,
205 get_arg_text(entity
))
206 print "%d of %d tests failed" % (len(failed
), len(tests
))
209 tests
= (t1
, t2
, t3
, t4
, t5
, t6
,
210 TC
, tc
.t1
, tc
.t2
, tc
.t3
, tc
.t4
, tc
.t5
, tc
.t6
)