annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/idlelib/calltip.py @ 69:33d812a61356

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