comparison CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/idlelib/calltip.py @ 68:5028fdace37b

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 16:23:26 -0400
parents
children
comparison
equal deleted inserted replaced
67:0e9998148a16 68:5028fdace37b
1 """Pop up a reminder of how to call a function.
2
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.
6 """
7 import __main__
8 import inspect
9 import re
10 import sys
11 import textwrap
12 import types
13
14 from idlelib import calltip_w
15 from idlelib.hyperparser import HyperParser
16
17
18 class Calltip:
19
20 def __init__(self, editwin=None):
21 if editwin is None: # subprocess and test
22 self.editwin = None
23 else:
24 self.editwin = editwin
25 self.text = editwin.text
26 self.active_calltip = None
27 self._calltip_window = self._make_tk_calltip_window
28
29 def close(self):
30 self._calltip_window = None
31
32 def _make_tk_calltip_window(self):
33 # See __init__ for usage
34 return calltip_w.CalltipWindow(self.text)
35
36 def _remove_calltip_window(self, event=None):
37 if self.active_calltip:
38 self.active_calltip.hidetip()
39 self.active_calltip = None
40
41 def force_open_calltip_event(self, event):
42 "The user selected the menu entry or hotkey, open the tip."
43 self.open_calltip(True)
44 return "break"
45
46 def try_open_calltip_event(self, event):
47 """Happens when it would be nice to open a calltip, but not really
48 necessary, for example after an opening bracket, so function calls
49 won't be made.
50 """
51 self.open_calltip(False)
52
53 def refresh_calltip_event(self, event):
54 if self.active_calltip and self.active_calltip.tipwindow:
55 self.open_calltip(False)
56
57 def open_calltip(self, evalfuncs):
58 self._remove_calltip_window()
59
60 hp = HyperParser(self.editwin, "insert")
61 sur_paren = hp.get_surrounding_brackets('(')
62 if not sur_paren:
63 return
64 hp.set_index(sur_paren[0])
65 expression = hp.get_expression()
66 if not expression:
67 return
68 if not evalfuncs and (expression.find('(') != -1):
69 return
70 argspec = self.fetch_tip(expression)
71 if not argspec:
72 return
73 self.active_calltip = self._calltip_window()
74 self.active_calltip.showtip(argspec, sur_paren[0], sur_paren[1])
75
76 def fetch_tip(self, expression):
77 """Return the argument list and docstring of a function or class.
78
79 If there is a Python subprocess, get the calltip there. Otherwise,
80 either this fetch_tip() is running in the subprocess or it was
81 called in an IDLE running without the subprocess.
82
83 The subprocess environment is that of the most recently run script. If
84 two unrelated modules are being edited some calltips in the current
85 module may be inoperative if the module was not the last to run.
86
87 To find methods, fetch_tip must be fed a fully qualified name.
88
89 """
90 try:
91 rpcclt = self.editwin.flist.pyshell.interp.rpcclt
92 except AttributeError:
93 rpcclt = None
94 if rpcclt:
95 return rpcclt.remotecall("exec", "get_the_calltip",
96 (expression,), {})
97 else:
98 return get_argspec(get_entity(expression))
99
100
101 def get_entity(expression):
102 """Return the object corresponding to expression evaluated
103 in a namespace spanning sys.modules and __main.dict__.
104 """
105 if expression:
106 namespace = {**sys.modules, **__main__.__dict__}
107 try:
108 return eval(expression, namespace) # Only protect user code.
109 except BaseException:
110 # An uncaught exception closes idle, and eval can raise any
111 # exception, especially if user classes are involved.
112 return None
113
114 # The following are used in get_argspec and some in tests
115 _MAX_COLS = 85
116 _MAX_LINES = 5 # enough for bytes
117 _INDENT = ' '*4 # for wrapped signatures
118 _first_param = re.compile(r'(?<=\()\w*\,?\s*')
119 _default_callable_argspec = "See source or doc"
120 _invalid_method = "invalid method signature"
121 _argument_positional = " # '/' marks preceding args as positional-only."
122
123 def get_argspec(ob):
124 '''Return a string describing the signature of a callable object, or ''.
125
126 For Python-coded functions and methods, the first line is introspected.
127 Delete 'self' parameter for classes (.__init__) and bound methods.
128 The next lines are the first lines of the doc string up to the first
129 empty line or _MAX_LINES. For builtins, this typically includes
130 the arguments in addition to the return value.
131 '''
132 argspec = default = ""
133 try:
134 ob_call = ob.__call__
135 except BaseException:
136 return default
137
138 fob = ob_call if isinstance(ob_call, types.MethodType) else ob
139
140 try:
141 argspec = str(inspect.signature(fob))
142 except ValueError as err:
143 msg = str(err)
144 if msg.startswith(_invalid_method):
145 return _invalid_method
146
147 if '/' in argspec and len(argspec) < _MAX_COLS - len(_argument_positional):
148 # Add explanation TODO remove after 3.7, before 3.9.
149 argspec += _argument_positional
150 if isinstance(fob, type) and argspec == '()':
151 # If fob has no argument, use default callable argspec.
152 argspec = _default_callable_argspec
153
154 lines = (textwrap.wrap(argspec, _MAX_COLS, subsequent_indent=_INDENT)
155 if len(argspec) > _MAX_COLS else [argspec] if argspec else [])
156
157 if isinstance(ob_call, types.MethodType):
158 doc = ob_call.__doc__
159 else:
160 doc = getattr(ob, "__doc__", "")
161 if doc:
162 for line in doc.split('\n', _MAX_LINES)[:_MAX_LINES]:
163 line = line.strip()
164 if not line:
165 break
166 if len(line) > _MAX_COLS:
167 line = line[: _MAX_COLS - 3] + '...'
168 lines.append(line)
169 argspec = '\n'.join(lines)
170 if not argspec:
171 argspec = _default_callable_argspec
172 return argspec
173
174
175 if __name__ == '__main__':
176 from unittest import main
177 main('idlelib.idle_test.test_calltip', verbosity=2)