Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 52013e5

Browse files
authored
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.
1 parent 6e623ff commit 52013e5

4 files changed

Lines changed: 32 additions & 15 deletions

File tree

Lib/idlelib/NEWS.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ Released on 2020-10-05?
33
======================================
44

55

6+
bpo-38689: IDLE will no longer freeze when inspect.signature fails
7+
when fetching a calltip.
8+
69
bpo-27115: For 'Go to Line', use a Query entry box subclass with
710
IDLE standard behavior and improved error checking.
811

Lib/idlelib/calltip.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,20 +129,22 @@ def get_argspec(ob):
129129
empty line or _MAX_LINES. For builtins, this typically includes
130130
the arguments in addition to the return value.
131131
'''
132-
argspec = default = ""
132+
# Determine function object fob to inspect.
133133
try:
134134
ob_call = ob.__call__
135-
except BaseException:
136-
return default
137-
135+
except BaseException: # Buggy user object could raise anything.
136+
return '' # No popup for non-callables.
138137
fob = ob_call if isinstance(ob_call, types.MethodType) else ob
139138

139+
# Initialize argspec and wrap it to get lines.
140140
try:
141141
argspec = str(inspect.signature(fob))
142-
except ValueError as err:
142+
except Exception as err:
143143
msg = str(err)
144144
if msg.startswith(_invalid_method):
145145
return _invalid_method
146+
else:
147+
argspec = ''
146148

147149
if '/' in argspec and len(argspec) < _MAX_COLS - len(_argument_positional):
148150
# Add explanation TODO remove after 3.7, before 3.9.
@@ -154,6 +156,7 @@ def get_argspec(ob):
154156
lines = (textwrap.wrap(argspec, _MAX_COLS, subsequent_indent=_INDENT)
155157
if len(argspec) > _MAX_COLS else [argspec] if argspec else [])
156158

159+
# Augment lines from docstring, if any, and join to get argspec.
157160
if isinstance(ob_call, types.MethodType):
158161
doc = ob_call.__doc__
159162
else:
@@ -167,9 +170,8 @@ def get_argspec(ob):
167170
line = line[: _MAX_COLS - 3] + '...'
168171
lines.append(line)
169172
argspec = '\n'.join(lines)
170-
if not argspec:
171-
argspec = _default_callable_argspec
172-
return argspec
173+
174+
return argspec or _default_callable_argspec
173175

174176

175177
if __name__ == '__main__':

Lib/idlelib/idle_test/test_calltip.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -219,20 +219,30 @@ def test_no_docstring(self):
219219
with self.subTest(meth=meth, mtip=mtip):
220220
self.assertEqual(get_spec(meth), mtip)
221221

222-
def test_attribute_exception(self):
222+
def test_buggy_getattr_class(self):
223223
class NoCall:
224-
def __getattr__(self, name):
225-
raise BaseException
224+
def __getattr__(self, name): # Not invoked for class attribute.
225+
raise IndexError # Bug.
226226
class CallA(NoCall):
227-
def __call__(oui, a, b, c):
227+
def __call__(self, ci): # Bug does not matter.
228228
pass
229229
class CallB(NoCall):
230-
def __call__(self, ci):
230+
def __call__(oui, a, b, c): # Non-standard 'self'.
231231
pass
232232

233233
for meth, mtip in ((NoCall, default_tip), (CallA, default_tip),
234-
(NoCall(), ''), (CallA(), '(a, b, c)'),
235-
(CallB(), '(ci)')):
234+
(NoCall(), ''), (CallA(), '(ci)'),
235+
(CallB(), '(a, b, c)')):
236+
with self.subTest(meth=meth, mtip=mtip):
237+
self.assertEqual(get_spec(meth), mtip)
238+
239+
def test_metaclass_class(self): # Failure case for issue 38689.
240+
class Type(type): # Type() requires 3 type args, returns class.
241+
__class__ = property({}.__getitem__, {}.__setitem__)
242+
class Object(metaclass=Type):
243+
__slots__ = '__class__'
244+
for meth, mtip in ((Type, default_tip), (Object, default_tip),
245+
(Object(), '')):
236246
with self.subTest(meth=meth, mtip=mtip):
237247
self.assertEqual(get_spec(meth), mtip)
238248

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
IDLE will no longer freeze when inspect.signature fails when fetching
2+
a calltip.

0 commit comments

Comments
 (0)