bpo-38689: avoid IDLE hanging when calltip fails getting a signature (GH-17152)
Inspect.signature failed on the test case because its isinstance call raised.
(cherry picked from commit 52013e5b6d
)
Co-authored-by: Tal Einat <taleinat+github@gmail.com>
This commit is contained in:
parent
e6685ad053
commit
15337726e5
|
@ -3,6 +3,9 @@ Released on 2019-12-16?
|
|||
======================================
|
||||
|
||||
|
||||
bpo-38689: IDLE will no longer freeze when inspect.signature fails
|
||||
when fetching a calltip.
|
||||
|
||||
bpo-27115: For 'Go to Line', use a Query entry box subclass with
|
||||
IDLE standard behavior and improved error checking.
|
||||
|
||||
|
|
|
@ -129,20 +129,22 @@ def get_argspec(ob):
|
|||
empty line or _MAX_LINES. For builtins, this typically includes
|
||||
the arguments in addition to the return value.
|
||||
'''
|
||||
argspec = default = ""
|
||||
# Determine function object fob to inspect.
|
||||
try:
|
||||
ob_call = ob.__call__
|
||||
except BaseException:
|
||||
return default
|
||||
|
||||
except BaseException: # Buggy user object could raise anything.
|
||||
return '' # No popup for non-callables.
|
||||
fob = ob_call if isinstance(ob_call, types.MethodType) else ob
|
||||
|
||||
# Initialize argspec and wrap it to get lines.
|
||||
try:
|
||||
argspec = str(inspect.signature(fob))
|
||||
except ValueError as err:
|
||||
except Exception as err:
|
||||
msg = str(err)
|
||||
if msg.startswith(_invalid_method):
|
||||
return _invalid_method
|
||||
else:
|
||||
argspec = ''
|
||||
|
||||
if '/' in argspec and len(argspec) < _MAX_COLS - len(_argument_positional):
|
||||
# Add explanation TODO remove after 3.7, before 3.9.
|
||||
|
@ -154,6 +156,7 @@ def get_argspec(ob):
|
|||
lines = (textwrap.wrap(argspec, _MAX_COLS, subsequent_indent=_INDENT)
|
||||
if len(argspec) > _MAX_COLS else [argspec] if argspec else [])
|
||||
|
||||
# Augment lines from docstring, if any, and join to get argspec.
|
||||
if isinstance(ob_call, types.MethodType):
|
||||
doc = ob_call.__doc__
|
||||
else:
|
||||
|
@ -167,9 +170,8 @@ def get_argspec(ob):
|
|||
line = line[: _MAX_COLS - 3] + '...'
|
||||
lines.append(line)
|
||||
argspec = '\n'.join(lines)
|
||||
if not argspec:
|
||||
argspec = _default_callable_argspec
|
||||
return argspec
|
||||
|
||||
return argspec or _default_callable_argspec
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -219,20 +219,30 @@ bytes() -> empty bytes object''')
|
|||
with self.subTest(meth=meth, mtip=mtip):
|
||||
self.assertEqual(get_spec(meth), mtip)
|
||||
|
||||
def test_attribute_exception(self):
|
||||
def test_buggy_getattr_class(self):
|
||||
class NoCall:
|
||||
def __getattr__(self, name):
|
||||
raise BaseException
|
||||
def __getattr__(self, name): # Not invoked for class attribute.
|
||||
raise IndexError # Bug.
|
||||
class CallA(NoCall):
|
||||
def __call__(oui, a, b, c):
|
||||
def __call__(self, ci): # Bug does not matter.
|
||||
pass
|
||||
class CallB(NoCall):
|
||||
def __call__(self, ci):
|
||||
def __call__(oui, a, b, c): # Non-standard 'self'.
|
||||
pass
|
||||
|
||||
for meth, mtip in ((NoCall, default_tip), (CallA, default_tip),
|
||||
(NoCall(), ''), (CallA(), '(a, b, c)'),
|
||||
(CallB(), '(ci)')):
|
||||
(NoCall(), ''), (CallA(), '(ci)'),
|
||||
(CallB(), '(a, b, c)')):
|
||||
with self.subTest(meth=meth, mtip=mtip):
|
||||
self.assertEqual(get_spec(meth), mtip)
|
||||
|
||||
def test_metaclass_class(self): # Failure case for issue 38689.
|
||||
class Type(type): # Type() requires 3 type args, returns class.
|
||||
__class__ = property({}.__getitem__, {}.__setitem__)
|
||||
class Object(metaclass=Type):
|
||||
__slots__ = '__class__'
|
||||
for meth, mtip in ((Type, default_tip), (Object, default_tip),
|
||||
(Object(), '')):
|
||||
with self.subTest(meth=meth, mtip=mtip):
|
||||
self.assertEqual(get_spec(meth), mtip)
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
IDLE will no longer freeze when inspect.signature fails when fetching
|
||||
a calltip.
|
Loading…
Reference in New Issue