blob: 0349199963cca84b587c98d5f07cdf3426787e2c [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.
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +00006
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +00007"""
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +00008import re
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +00009import sys
David Scherer7aced172000-08-15 01:13:23 +000010import types
11
Florent Xiclunad630c042010-04-02 07:24:52 +000012from idlelib import CallTipWindow
13from idlelib.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
Mark Dickinson3e4caeb2009-02-21 20:27:01 +000054 necessary, for example after an opening bracket, so function calls
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000055 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])
Terry Jan Reedye93bc512012-06-03 00:58:36 -040074 expression = hp.get_expression()
75 if not expression or (not evalfuncs and expression.find('(') != -1):
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000076 return
Terry Jan Reedye93bc512012-06-03 00:58:36 -040077 arg_text = self.fetch_tip(expression)
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
Terry Jan Reedye93bc512012-06-03 00:58:36 -040083 def fetch_tip(self, expression):
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
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +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
Terry Jan Reedye93bc512012-06-03 00:58:36 -040099 except AttributeError:
Kurt B. Kaisere54710b2002-12-12 19:15:39 +0000100 rpcclt = None
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000101 if rpcclt:
102 return rpcclt.remotecall("exec", "get_the_calltip",
Terry Jan Reedye93bc512012-06-03 00:58:36 -0400103 (expression,), {})
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000104 else:
Terry Jan Reedye93bc512012-06-03 00:58:36 -0400105 entity = self.get_entity(expression)
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000106 return get_arg_text(entity)
107
Terry Jan Reedye93bc512012-06-03 00:58:36 -0400108 def get_entity(self, expression):
109 """Return the object corresponding to expression evaluated
110 in a namespace spanning sys.modules and __main.dict__.
111 """
112 if expression:
David Scherer7aced172000-08-15 01:13:23 +0000113 namespace = sys.modules.copy()
114 namespace.update(__main__.__dict__)
115 try:
Terry Jan Reedye93bc512012-06-03 00:58:36 -0400116 return eval(expression, namespace)
117 except BaseException:
118 # An uncaught exception closes idle, and eval can raise any
119 # exception, especially if user classes are involved.
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000120 return None
David Scherer7aced172000-08-15 01:13:23 +0000121
122def _find_constructor(class_ob):
123 # Given a class object, return a function object used for the
124 # constructor (ie, __init__() ) or None if we can't find one.
125 try:
126 return class_ob.__init__.im_func
127 except AttributeError:
128 for base in class_ob.__bases__:
129 rc = _find_constructor(base)
130 if rc is not None: return rc
131 return None
132
133def get_arg_text(ob):
Terry Jan Reedyd9d276b2012-05-27 21:28:42 -0400134 """Get a string describing the arguments for the given object,
135 only if it is callable."""
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000136 arg_text = ""
Terry Jan Reedyd9d276b2012-05-27 21:28:42 -0400137 if ob is not None and hasattr(ob, '__call__'):
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000138 arg_offset = 0
Kurt B. Kaiserc6bacd52006-07-23 04:19:49 +0000139 if type(ob) in (types.ClassType, types.TypeType):
David Scherer7aced172000-08-15 01:13:23 +0000140 # Look for the highest __init__ in the class chain.
141 fob = _find_constructor(ob)
142 if fob is None:
143 fob = lambda: None
144 else:
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000145 arg_offset = 1
Terry Jan Reedy02fd1fd2014-01-21 00:26:10 -0500146 elif type(ob) == types.MethodType:
David Scherer7aced172000-08-15 01:13:23 +0000147 # bit of a hack for methods - turn it into a function
Terry Jan Reedy02fd1fd2014-01-21 00:26:10 -0500148 # and drop the "self" param for bound methods
David Scherer7aced172000-08-15 01:13:23 +0000149 fob = ob.im_func
Terry Jan Reedy02fd1fd2014-01-21 00:26:10 -0500150 if ob.im_self:
151 arg_offset = 1
152 elif type(ob.__call__) == types.MethodType:
153 # a callable class instance
154 fob = ob.__call__.im_func
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000155 arg_offset = 1
David Scherer7aced172000-08-15 01:13:23 +0000156 else:
157 fob = ob
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000158 # Try to build one for Python defined functions
David Scherer7aced172000-08-15 01:13:23 +0000159 if type(fob) in [types.FunctionType, types.LambdaType]:
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000160 argcount = fob.func_code.co_argcount
161 real_args = fob.func_code.co_varnames[arg_offset:argcount]
162 defaults = fob.func_defaults or []
163 defaults = list(map(lambda name: "=%s" % repr(name), defaults))
164 defaults = [""] * (len(real_args) - len(defaults)) + defaults
165 items = map(lambda arg, dflt: arg + dflt, real_args, defaults)
166 if fob.func_code.co_flags & 0x4:
Terry Jan Reedy02fd1fd2014-01-21 00:26:10 -0500167 items.append("*args")
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000168 if fob.func_code.co_flags & 0x8:
Terry Jan Reedy02fd1fd2014-01-21 00:26:10 -0500169 items.append("**kwds")
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000170 arg_text = ", ".join(items)
Terry Jan Reedy34906552013-07-26 18:21:32 -0400171 arg_text = "(%s)" % re.sub("(?<!\d)\.\d+", "<tuple>", arg_text)
David Scherer7aced172000-08-15 01:13:23 +0000172 # See if we can use the docstring
Terry Jan Reedy02fd1fd2014-01-21 00:26:10 -0500173 if isinstance(ob.__call__, types.MethodType):
174 doc = ob.__call__.__doc__
175 else:
176 doc = getattr(ob, "__doc__", "")
Kurt B. Kaiser908aece2002-09-15 22:02:58 +0000177 if doc:
Kurt B. Kaiser6145a622003-07-23 15:42:14 +0000178 doc = doc.lstrip()
Kurt B. Kaiser908aece2002-09-15 22:02:58 +0000179 pos = doc.find("\n")
180 if pos < 0 or pos > 70:
181 pos = 70
Kurt B. Kaiserecf796e2007-02-05 23:02:16 +0000182 if arg_text:
183 arg_text += "\n"
184 arg_text += doc[:pos]
185 return arg_text
David Scherer7aced172000-08-15 01:13:23 +0000186
Terry Jan Reedy02fd1fd2014-01-21 00:26:10 -0500187if __name__ == '__main__':
Terry Jan Reedyb8fd9ca2013-05-30 14:47:33 -0400188 from unittest import main
189 main('idlelib.idle_test.test_calltips', verbosity=2, exit=False)