"""CallTips.py - An IDLE Extension to Jog Your Memory
Call Tips are floating windows which display function, class, and method
parameter and docstring information when you type an opening parenthesis, and
which disappear when you type a closing parenthesis.
from idlelib import CallTipWindow
from idlelib.HyperParser import HyperParser
("Show call tip", "<<force-open-calltip>>"),
def __init__(self, editwin=None):
if editwin is None: # subprocess and test
self._make_calltip_window = self._make_tk_calltip_window
self._make_calltip_window = None
def _make_tk_calltip_window(self):
return CallTipWindow.CallTip(self.text)
def _remove_calltip_window(self, event=None):
def force_open_calltip_event(self, event):
"""Happens when the user really wants to open a CallTip, even if a
def try_open_calltip_event(self, event):
"""Happens when it would be nice to open a CallTip, but not really
necessary, for example after an opening bracket, so function calls
def refresh_calltip_event(self, event):
"""If there is already a calltip window, check if it is still needed,
if self.calltip and self.calltip.is_active():
def open_calltip(self, evalfuncs):
self._remove_calltip_window()
hp = HyperParser(self.editwin, "insert")
sur_paren = hp.get_surrounding_brackets('(')
hp.set_index(sur_paren[0])
expression = hp.get_expression()
if not expression or (not evalfuncs and expression.find('(') != -1):
arg_text = self.fetch_tip(expression)
self.calltip = self._make_calltip_window()
self.calltip.showtip(arg_text, sur_paren[0], sur_paren[1])
def fetch_tip(self, expression):
"""Return the argument list and docstring of a function or class
If there is a Python subprocess, get the calltip there. Otherwise,
either fetch_tip() is running in the subprocess itself or it was called
in an IDLE EditorWindow before any script had been run.
The subprocess environment is that of the most recently run script. If
two unrelated modules are being edited some calltips in the current
module may be inoperative if the module was not the last to run.
To find methods, fetch_tip must be fed a fully qualified name.
rpcclt = self.editwin.flist.pyshell.interp.rpcclt
return rpcclt.remotecall("exec", "get_the_calltip",
entity = self.get_entity(expression)
return get_arg_text(entity)
def get_entity(self, expression):
"""Return the object corresponding to expression evaluated
in a namespace spanning sys.modules and __main.dict__.
namespace = sys.modules.copy()
namespace.update(__main__.__dict__)
return eval(expression, namespace)
# An uncaught exception closes idle, and eval can raise any
# exception, especially if user classes are involved.
def _find_constructor(class_ob):
# Given a class object, return a function object used for the
# constructor (ie, __init__() ) or None if we can't find one.
return class_ob.__init__.im_func
for base in class_ob.__bases__:
rc = _find_constructor(base)
if rc is not None: return rc
# The following are used in get_arg_text
_MAX_LINES = 5 # enough for bytes
_INDENT = ' '*4 # for wrapped signatures
'''Return a string describing the signature of a callable object, or ''.
For Python-coded functions and methods, the first line is introspected.
Delete 'self' parameter for classes (.__init__) and bound methods.
The next lines are the first lines of the doc string up to the first
empty line or _MAX_LINES. For builtins, this typically includes
the arguments in addition to the return value.
if type(ob) is types.ClassType: # old-style
if type(ob) in (types.ClassType, types.TypeType):
# Look for the first __init__ in the class chain with .im_func.
# Slot wrappers (builtins, classes defined in funcs) do not.
fob = _find_constructor(ob)
elif type(ob) == types.MethodType:
# bit of a hack for methods - turn it into a function
# and drop the "self" param for bound methods
if ob.im_self is not None:
elif type(ob_call) == types.MethodType:
# a callable class instance
# Try to build one for Python defined functions
if type(fob) in [types.FunctionType, types.LambdaType]:
argcount = fob.func_code.co_argcount
real_args = fob.func_code.co_varnames[arg_offset:argcount]
defaults = fob.func_defaults or []
defaults = list(map(lambda name: "=%s" % repr(name), defaults))
defaults = [""] * (len(real_args) - len(defaults)) + defaults
items = map(lambda arg, dflt: arg + dflt, real_args, defaults)
for flag, pre, name in ((0x4, '*', 'args'), (0x8, '**', 'kwargs')):
if fob.func_code.co_flags & flag:
if name not in real_args:
while ((name+'%s') % i) in real_args:
items.append((pre_name+'%s') % i)
argspec = ", ".join(items)
argspec = "(%s)" % re.sub("(?<!\d)\.\d+", "<tuple>", argspec)
lines = (textwrap.wrap(argspec, _MAX_COLS, subsequent_indent=_INDENT)
if len(argspec) > _MAX_COLS else [argspec] if argspec else [])
if isinstance(ob_call, types.MethodType):
doc = getattr(ob, "__doc__", "")
for line in doc.split('\n', _MAX_LINES)[:_MAX_LINES]:
if len(line) > _MAX_COLS:
line = line[: _MAX_COLS - 3] + '...'
argspec = '\n'.join(lines)
if __name__ == '__main__':
from unittest import main
main('idlelib.idle_test.test_calltips', verbosity=2)