Mercurial > repos > rliterman > csp2
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) |