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