blob: 40bc5a0ad798fe3eeef6ff38f79f74747b45a21f [file] [log] [blame]
wohlganger58fc71c2017-09-10 16:19:47 -05001"""Pop up a reminder of how to call a function.
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. Kaiser5afa1df2002-10-10 08:25:24 +00006"""
Terry Jan Reedy0fe45132019-03-24 17:12:28 -04007import __main__
Terry Jan Reedya0f1e222014-01-26 19:55:34 -05008import inspect
Thomas Wouterscf297e42007-02-23 15:07:44 +00009import re
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000010import sys
Terry Jan Reedya0f1e222014-01-26 19:55:34 -050011import textwrap
David Scherer7aced172000-08-15 01:13:23 +000012import types
13
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040014from idlelib import calltip_w
15from idlelib.hyperparser import HyperParser
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000016
David Scherer7aced172000-08-15 01:13:23 +000017
Terry Jan Reedy06e20292018-06-19 23:00:35 -040018class Calltip:
David Scherer7aced172000-08-15 01:13:23 +000019
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000020 def __init__(self, editwin=None):
Raymond Hettingerc5e378d2004-05-04 08:34:56 +000021 if editwin is None: # subprocess and test
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000022 self.editwin = None
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000023 else:
24 self.editwin = editwin
25 self.text = editwin.text
26 self.active_calltip = None
27 self._calltip_window = self._make_tk_calltip_window
David Scherer7aced172000-08-15 01:13:23 +000028
29 def close(self):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000030 self._calltip_window = None
David Scherer7aced172000-08-15 01:13:23 +000031
David Scherer7aced172000-08-15 01:13:23 +000032 def _make_tk_calltip_window(self):
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000033 # See __init__ for usage
Terry Jan Reedy9af18362018-06-20 02:18:49 -040034 return calltip_w.CalltipWindow(self.text)
David Scherer7aced172000-08-15 01:13:23 +000035
Zackery Spytzbfdeaa32020-01-30 18:55:42 -070036 def remove_calltip_window(self, event=None):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000037 if self.active_calltip:
38 self.active_calltip.hidetip()
39 self.active_calltip = None
Kurt B. Kaiserae676472001-07-12 23:10:35 +000040
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000041 def force_open_calltip_event(self, event):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000042 "The user selected the menu entry or hotkey, open the tip."
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000043 self.open_calltip(True)
Serhiy Storchaka213ce122017-06-27 07:02:32 +030044 return "break"
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000045
46 def try_open_calltip_event(self, event):
Terry Jan Reedy9af18362018-06-20 02:18:49 -040047 """Happens when it would be nice to open a calltip, but not really
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000048 necessary, for example after an opening bracket, so function calls
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000049 won't be made.
50 """
51 self.open_calltip(False)
52
53 def refresh_calltip_event(self, event):
Tal Einat87e59ac2018-08-05 09:21:08 +030054 if self.active_calltip and self.active_calltip.tipwindow:
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000055 self.open_calltip(False)
56
57 def open_calltip(self, evalfuncs):
Tal Einatda7bb7b2020-11-02 05:59:52 +020058 """Maybe close an existing calltip and maybe open a new calltip.
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000059
Tal Einatda7bb7b2020-11-02 05:59:52 +020060 Called from (force_open|try_open|refresh)_calltip_event functions.
61 """
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000062 hp = HyperParser(self.editwin, "insert")
63 sur_paren = hp.get_surrounding_brackets('(')
Tal Einatda7bb7b2020-11-02 05:59:52 +020064
65 # If not inside parentheses, no calltip.
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000066 if not sur_paren:
Tal Einatda7bb7b2020-11-02 05:59:52 +020067 self.remove_calltip_window()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000068 return
Tal Einatda7bb7b2020-11-02 05:59:52 +020069
70 # If a calltip is shown for the current parentheses, do
71 # nothing.
72 if self.active_calltip:
73 opener_line, opener_col = map(int, sur_paren[0].split('.'))
74 if (
75 (opener_line, opener_col) ==
76 (self.active_calltip.parenline, self.active_calltip.parencol)
77 ):
78 return
79
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000080 hp.set_index(sur_paren[0])
Tal Einatda7bb7b2020-11-02 05:59:52 +020081 try:
82 expression = hp.get_expression()
83 except ValueError:
84 expression = None
Terry Jan Reedye606e232012-06-03 00:27:54 -040085 if not expression:
Tal Einatda7bb7b2020-11-02 05:59:52 +020086 # No expression before the opening parenthesis, e.g.
87 # because it's in a string or the opener for a tuple:
88 # Do nothing.
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000089 return
Tal Einatda7bb7b2020-11-02 05:59:52 +020090
91 # At this point, the current index is after an opening
92 # parenthesis, in a section of code, preceded by a valid
93 # expression. If there is a calltip shown, it's not for the
94 # same index and should be closed.
95 self.remove_calltip_window()
96
97 # Simple, fast heuristic: If the preceding expression includes
98 # an opening parenthesis, it likely includes a function call.
Terry Jan Reedye606e232012-06-03 00:27:54 -040099 if not evalfuncs and (expression.find('(') != -1):
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000100 return
Tal Einatda7bb7b2020-11-02 05:59:52 +0200101
Terry Jan Reedye606e232012-06-03 00:27:54 -0400102 argspec = self.fetch_tip(expression)
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000103 if not argspec:
104 return
105 self.active_calltip = self._calltip_window()
106 self.active_calltip.showtip(argspec, sur_paren[0], sur_paren[1])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000107
Terry Jan Reedye606e232012-06-03 00:27:54 -0400108 def fetch_tip(self, expression):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000109 """Return the argument list and docstring of a function or class.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000110
Kurt B. Kaisere54710b2002-12-12 19:15:39 +0000111 If there is a Python subprocess, get the calltip there. Otherwise,
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000112 either this fetch_tip() is running in the subprocess or it was
113 called in an IDLE running without the subprocess.
Kurt B. Kaisere54710b2002-12-12 19:15:39 +0000114
115 The subprocess environment is that of the most recently run script. If
116 two unrelated modules are being edited some calltips in the current
117 module may be inoperative if the module was not the last to run.
118
Thomas Wouterscf297e42007-02-23 15:07:44 +0000119 To find methods, fetch_tip must be fed a fully qualified name.
120
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000121 """
Kurt B. Kaisere54710b2002-12-12 19:15:39 +0000122 try:
123 rpcclt = self.editwin.flist.pyshell.interp.rpcclt
Terry Jan Reedye606e232012-06-03 00:27:54 -0400124 except AttributeError:
Kurt B. Kaisere54710b2002-12-12 19:15:39 +0000125 rpcclt = None
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000126 if rpcclt:
127 return rpcclt.remotecall("exec", "get_the_calltip",
Terry Jan Reedye606e232012-06-03 00:27:54 -0400128 (expression,), {})
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000129 else:
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400130 return get_argspec(get_entity(expression))
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000131
wohlganger58fc71c2017-09-10 16:19:47 -0500132
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400133def get_entity(expression):
134 """Return the object corresponding to expression evaluated
Terry Jan Reedy0fe45132019-03-24 17:12:28 -0400135 in a namespace spanning sys.modules and __main.dict__.
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400136 """
137 if expression:
Terry Jan Reedy0fe45132019-03-24 17:12:28 -0400138 namespace = {**sys.modules, **__main__.__dict__}
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400139 try:
Terry Jan Reedy2b751552019-03-23 03:50:15 -0400140 return eval(expression, namespace) # Only protect user code.
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400141 except BaseException:
142 # An uncaught exception closes idle, and eval can raise any
143 # exception, especially if user classes are involved.
144 return None
David Scherer7aced172000-08-15 01:13:23 +0000145
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500146# The following are used in get_argspec and some in tests
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500147_MAX_COLS = 85
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500148_MAX_LINES = 5 # enough for bytes
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500149_INDENT = ' '*4 # for wrapped signatures
R David Murray44b548d2016-09-08 13:59:53 -0400150_first_param = re.compile(r'(?<=\()\w*\,?\s*')
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500151_default_callable_argspec = "See source or doc"
Louie Lu3b0f6202017-08-10 08:58:13 +0800152_invalid_method = "invalid method signature"
David Scherer7aced172000-08-15 01:13:23 +0000153
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000154def get_argspec(ob):
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500155 '''Return a string describing the signature of a callable object, or ''.
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400156
157 For Python-coded functions and methods, the first line is introspected.
158 Delete 'self' parameter for classes (.__init__) and bound methods.
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500159 The next lines are the first lines of the doc string up to the first
160 empty line or _MAX_LINES. For builtins, this typically includes
161 the arguments in addition to the return value.
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400162 '''
Tal Einat52013e52020-04-04 06:05:58 +0300163 # Determine function object fob to inspect.
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500164 try:
165 ob_call = ob.__call__
Tal Einat52013e52020-04-04 06:05:58 +0300166 except BaseException: # Buggy user object could raise anything.
167 return '' # No popup for non-callables.
Terry Jan Reedy7ddbaa72020-11-20 01:59:11 -0500168 # For Get_argspecTest.test_buggy_getattr_class, CallA() & CallB().
Louie Lu3b0f6202017-08-10 08:58:13 +0800169 fob = ob_call if isinstance(ob_call, types.MethodType) else ob
170
Tal Einat52013e52020-04-04 06:05:58 +0300171 # Initialize argspec and wrap it to get lines.
Louie Lu3b0f6202017-08-10 08:58:13 +0800172 try:
173 argspec = str(inspect.signature(fob))
Tal Einat52013e52020-04-04 06:05:58 +0300174 except Exception as err:
Louie Lu3b0f6202017-08-10 08:58:13 +0800175 msg = str(err)
176 if msg.startswith(_invalid_method):
177 return _invalid_method
Tal Einat52013e52020-04-04 06:05:58 +0300178 else:
179 argspec = ''
Louie Lu3b0f6202017-08-10 08:58:13 +0800180
Louie Lu3b0f6202017-08-10 08:58:13 +0800181 if isinstance(fob, type) and argspec == '()':
Terry Jan Reedy949fe972019-06-04 21:55:37 -0400182 # If fob has no argument, use default callable argspec.
Louie Lu3b0f6202017-08-10 08:58:13 +0800183 argspec = _default_callable_argspec
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400184
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500185 lines = (textwrap.wrap(argspec, _MAX_COLS, subsequent_indent=_INDENT)
Louie Lu3b0f6202017-08-10 08:58:13 +0800186 if len(argspec) > _MAX_COLS else [argspec] if argspec else [])
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500187
Tal Einat52013e52020-04-04 06:05:58 +0300188 # Augment lines from docstring, if any, and join to get argspec.
Terry Jan Reedy7ddbaa72020-11-20 01:59:11 -0500189 doc = inspect.getdoc(ob)
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500190 if doc:
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500191 for line in doc.split('\n', _MAX_LINES)[:_MAX_LINES]:
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500192 line = line.strip()
193 if not line:
194 break
195 if len(line) > _MAX_COLS:
196 line = line[: _MAX_COLS - 3] + '...'
197 lines.append(line)
Emmanuel Ariasab54b9a2019-01-03 04:47:58 -0300198 argspec = '\n'.join(lines)
Tal Einat52013e52020-04-04 06:05:58 +0300199
200 return argspec or _default_callable_argspec
David Scherer7aced172000-08-15 01:13:23 +0000201
Louie Lu3b0f6202017-08-10 08:58:13 +0800202
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000203if __name__ == '__main__':
Terry Jan Reedydb4e5c52013-05-27 21:32:03 -0400204 from unittest import main
Terry Jan Reedy06e20292018-06-19 23:00:35 -0400205 main('idlelib.idle_test.test_calltip', verbosity=2)