blob: 4b78917d7d98c11746e1abbe66183834f0ef0cf4 [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 Reedya0f1e222014-01-26 19:55:34 -05007import inspect
Thomas Wouterscf297e42007-02-23 15:07:44 +00008import re
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +00009import sys
Terry Jan Reedya0f1e222014-01-26 19:55:34 -050010import textwrap
David Scherer7aced172000-08-15 01:13:23 +000011import types
12
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040013from idlelib import calltip_w
14from idlelib.hyperparser import HyperParser
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000015
David Scherer7aced172000-08-15 01:13:23 +000016
Terry Jan Reedy06e20292018-06-19 23:00:35 -040017class Calltip:
David Scherer7aced172000-08-15 01:13:23 +000018
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000019 def __init__(self, editwin=None):
Raymond Hettingerc5e378d2004-05-04 08:34:56 +000020 if editwin is None: # subprocess and test
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000021 self.editwin = None
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000022 else:
23 self.editwin = editwin
24 self.text = editwin.text
25 self.active_calltip = None
26 self._calltip_window = self._make_tk_calltip_window
David Scherer7aced172000-08-15 01:13:23 +000027
28 def close(self):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000029 self._calltip_window = None
David Scherer7aced172000-08-15 01:13:23 +000030
David Scherer7aced172000-08-15 01:13:23 +000031 def _make_tk_calltip_window(self):
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000032 # See __init__ for usage
Terry Jan Reedy9af18362018-06-20 02:18:49 -040033 return calltip_w.CalltipWindow(self.text)
David Scherer7aced172000-08-15 01:13:23 +000034
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000035 def _remove_calltip_window(self, event=None):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000036 if self.active_calltip:
37 self.active_calltip.hidetip()
38 self.active_calltip = None
Kurt B. Kaiserae676472001-07-12 23:10:35 +000039
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000040 def force_open_calltip_event(self, event):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000041 "The user selected the menu entry or hotkey, open the tip."
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000042 self.open_calltip(True)
Serhiy Storchaka213ce122017-06-27 07:02:32 +030043 return "break"
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000044
45 def try_open_calltip_event(self, event):
Terry Jan Reedy9af18362018-06-20 02:18:49 -040046 """Happens when it would be nice to open a calltip, but not really
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000047 necessary, for example after an opening bracket, so function calls
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000048 won't be made.
49 """
50 self.open_calltip(False)
51
52 def refresh_calltip_event(self, event):
Tal Einat87e59ac2018-08-05 09:21:08 +030053 if self.active_calltip and self.active_calltip.tipwindow:
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000054 self.open_calltip(False)
55
56 def open_calltip(self, evalfuncs):
David Scherer7aced172000-08-15 01:13:23 +000057 self._remove_calltip_window()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000058
59 hp = HyperParser(self.editwin, "insert")
60 sur_paren = hp.get_surrounding_brackets('(')
61 if not sur_paren:
62 return
63 hp.set_index(sur_paren[0])
Terry Jan Reedye606e232012-06-03 00:27:54 -040064 expression = hp.get_expression()
65 if not expression:
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000066 return
Terry Jan Reedye606e232012-06-03 00:27:54 -040067 if not evalfuncs and (expression.find('(') != -1):
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000068 return
Terry Jan Reedye606e232012-06-03 00:27:54 -040069 argspec = self.fetch_tip(expression)
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000070 if not argspec:
71 return
72 self.active_calltip = self._calltip_window()
73 self.active_calltip.showtip(argspec, sur_paren[0], sur_paren[1])
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000074
Terry Jan Reedye606e232012-06-03 00:27:54 -040075 def fetch_tip(self, expression):
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000076 """Return the argument list and docstring of a function or class.
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000077
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000078 If there is a Python subprocess, get the calltip there. Otherwise,
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +000079 either this fetch_tip() is running in the subprocess or it was
80 called in an IDLE running without the subprocess.
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000081
82 The subprocess environment is that of the most recently run script. If
83 two unrelated modules are being edited some calltips in the current
84 module may be inoperative if the module was not the last to run.
85
Thomas Wouterscf297e42007-02-23 15:07:44 +000086 To find methods, fetch_tip must be fed a fully qualified name.
87
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000088 """
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000089 try:
90 rpcclt = self.editwin.flist.pyshell.interp.rpcclt
Terry Jan Reedye606e232012-06-03 00:27:54 -040091 except AttributeError:
Kurt B. Kaisere54710b2002-12-12 19:15:39 +000092 rpcclt = None
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000093 if rpcclt:
94 return rpcclt.remotecall("exec", "get_the_calltip",
Terry Jan Reedye606e232012-06-03 00:27:54 -040095 (expression,), {})
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000096 else:
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -040097 return get_argspec(get_entity(expression))
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000098
wohlganger58fc71c2017-09-10 16:19:47 -050099
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400100def get_entity(expression):
101 """Return the object corresponding to expression evaluated
Terry Jan Reedy2b751552019-03-23 03:50:15 -0400102 in a namespace spanning sys.modules and globals().
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400103 """
104 if expression:
Terry Jan Reedy2b751552019-03-23 03:50:15 -0400105 namespace = {**sys.modules, **globals()}
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400106 try:
Terry Jan Reedy2b751552019-03-23 03:50:15 -0400107 return eval(expression, namespace) # Only protect user code.
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400108 except BaseException:
109 # An uncaught exception closes idle, and eval can raise any
110 # exception, especially if user classes are involved.
111 return None
David Scherer7aced172000-08-15 01:13:23 +0000112
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500113# The following are used in get_argspec and some in tests
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500114_MAX_COLS = 85
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500115_MAX_LINES = 5 # enough for bytes
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500116_INDENT = ' '*4 # for wrapped signatures
R David Murray44b548d2016-09-08 13:59:53 -0400117_first_param = re.compile(r'(?<=\()\w*\,?\s*')
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500118_default_callable_argspec = "See source or doc"
Louie Lu3b0f6202017-08-10 08:58:13 +0800119_invalid_method = "invalid method signature"
120_argument_positional = "\n['/' marks preceding arguments as positional-only]\n"
David Scherer7aced172000-08-15 01:13:23 +0000121
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000122def get_argspec(ob):
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500123 '''Return a string describing the signature of a callable object, or ''.
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400124
125 For Python-coded functions and methods, the first line is introspected.
126 Delete 'self' parameter for classes (.__init__) and bound methods.
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500127 The next lines are the first lines of the doc string up to the first
128 empty line or _MAX_LINES. For builtins, this typically includes
129 the arguments in addition to the return value.
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400130 '''
Louie Lu3b0f6202017-08-10 08:58:13 +0800131 argspec = default = ""
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500132 try:
133 ob_call = ob.__call__
134 except BaseException:
Louie Lu3b0f6202017-08-10 08:58:13 +0800135 return default
136
137 fob = ob_call if isinstance(ob_call, types.MethodType) else ob
138
139 try:
140 argspec = str(inspect.signature(fob))
141 except ValueError as err:
142 msg = str(err)
143 if msg.startswith(_invalid_method):
144 return _invalid_method
145
146 if '/' in argspec:
147 """Using AC's positional argument should add the explain"""
148 argspec += _argument_positional
149 if isinstance(fob, type) and argspec == '()':
150 """fob with no argument, use default callable argspec"""
151 argspec = _default_callable_argspec
Terry Jan Reedy2a2ce4f2012-06-07 19:41:04 -0400152
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500153 lines = (textwrap.wrap(argspec, _MAX_COLS, subsequent_indent=_INDENT)
Louie Lu3b0f6202017-08-10 08:58:13 +0800154 if len(argspec) > _MAX_COLS else [argspec] if argspec else [])
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500155
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500156 if isinstance(ob_call, types.MethodType):
157 doc = ob_call.__doc__
158 else:
159 doc = getattr(ob, "__doc__", "")
160 if doc:
Terry Jan Reedya0f1e222014-01-26 19:55:34 -0500161 for line in doc.split('\n', _MAX_LINES)[:_MAX_LINES]:
Terry Jan Reedyd5710f82014-01-21 20:45:17 -0500162 line = line.strip()
163 if not line:
164 break
165 if len(line) > _MAX_COLS:
166 line = line[: _MAX_COLS - 3] + '...'
167 lines.append(line)
Emmanuel Ariasab54b9a2019-01-03 04:47:58 -0300168 argspec = '\n'.join(lines)
Terry Jan Reedy715476d2014-01-21 15:36:51 -0500169 if not argspec:
170 argspec = _default_callable_argspec
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000171 return argspec
David Scherer7aced172000-08-15 01:13:23 +0000172
Louie Lu3b0f6202017-08-10 08:58:13 +0800173
Kurt B. Kaiser152b3c22007-08-30 06:35:15 +0000174if __name__ == '__main__':
Terry Jan Reedydb4e5c52013-05-27 21:32:03 -0400175 from unittest import main
Terry Jan Reedy06e20292018-06-19 23:00:35 -0400176 main('idlelib.idle_test.test_calltip', verbosity=2)