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

Skip to content

Commit 2a2ce4f

Browse files
committed
Issue #12510: Revise and triple # of calltip tests, with an eye to unittest
use. Make the get_entity 'method' a module function as it did not use 'self'. Delete buggy _find_constructor function that is not needed, at least in 3.x. Revise get_argspec so all tests pass. Add and fix NEWS entries.
1 parent 29471de commit 2a2ce4f

3 files changed

Lines changed: 138 additions & 76 deletions

File tree

Lib/idlelib/CallTips.py

Lines changed: 123 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -100,52 +100,53 @@ def fetch_tip(self, expression):
100100
return rpcclt.remotecall("exec", "get_the_calltip",
101101
(expression,), {})
102102
else:
103-
entity = self.get_entity(expression)
104-
return get_argspec(entity)
103+
return get_argspec(get_entity(expression))
105104

106-
def get_entity(self, expression):
107-
"""Return the object corresponding to expression evaluated
108-
in a namespace spanning sys.modules and __main.dict__.
109-
"""
110-
if expression:
111-
namespace = sys.modules.copy()
112-
namespace.update(__main__.__dict__)
113-
try:
114-
return eval(expression, namespace)
115-
except BaseException:
116-
# An uncaught exception closes idle, and eval can raise any
117-
# exception, especially if user classes are involved.
118-
return None
119-
120-
def _find_constructor(class_ob):
121-
"Find the nearest __init__() in the class tree."
122-
try:
123-
return class_ob.__init__.__func__
124-
except AttributeError:
125-
for base in class_ob.__bases__:
126-
init = _find_constructor(base)
127-
if init:
128-
return init
129-
return None
105+
def get_entity(expression):
106+
"""Return the object corresponding to expression evaluated
107+
in a namespace spanning sys.modules and __main.dict__.
108+
"""
109+
if expression:
110+
namespace = sys.modules.copy()
111+
namespace.update(__main__.__dict__)
112+
try:
113+
return eval(expression, namespace)
114+
except BaseException:
115+
# An uncaught exception closes idle, and eval can raise any
116+
# exception, especially if user classes are involved.
117+
return None
118+
119+
# The following are used in both get_argspec and tests
120+
_self_pat = re.compile('self\,?\s*')
121+
_default_callable_argspec = "No docstring, see docs."
130122

131123
def get_argspec(ob):
132-
"""Get a string describing the arguments for the given object,
133-
only if it is callable."""
124+
'''Return a string describing the arguments and return of a callable object.
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 last line is the first line of the doc string. For builtins, this typically
129+
includes the arguments in addition to the return value.
130+
131+
'''
134132
argspec = ""
135-
if ob is not None and hasattr(ob, '__call__'):
133+
if hasattr(ob, '__call__'):
136134
if isinstance(ob, type):
137-
fob = _find_constructor(ob)
138-
if fob is None:
139-
fob = lambda: None
140-
elif isinstance(ob, types.MethodType):
141-
fob = ob.__func__
135+
fob = getattr(ob, '__init__', None)
136+
elif isinstance(ob.__call__, types.MethodType):
137+
fob = ob.__call__
142138
else:
143139
fob = ob
144-
if isinstance(fob, (types.FunctionType, types.LambdaType)):
140+
if isinstance(fob, (types.FunctionType, types.MethodType)):
145141
argspec = inspect.formatargspec(*inspect.getfullargspec(fob))
146-
pat = re.compile('self\,?\s*')
147-
argspec = pat.sub("", argspec)
148-
doc = getattr(ob, "__doc__", "")
142+
if (isinstance(ob, (type, types.MethodType)) or
143+
isinstance(ob.__call__, types.MethodType)):
144+
argspec = _self_pat.sub("", argspec)
145+
146+
if isinstance(ob.__call__, types.MethodType):
147+
doc = ob.__call__.__doc__
148+
else:
149+
doc = getattr(ob, "__doc__", "")
149150
if doc:
150151
doc = doc.lstrip()
151152
pos = doc.find("\n")
@@ -154,13 +155,16 @@ def get_argspec(ob):
154155
if argspec:
155156
argspec += "\n"
156157
argspec += doc[:pos]
158+
if not argspec:
159+
argspec = _default_callable_argspec
157160
return argspec
158161

159162
#################################################
160163
#
161-
# Test code
162-
#
164+
# Test code tests CallTips.fetch_tip, get_entity, and get_argspec
165+
163166
def main():
167+
# Putting expected in docstrings results in doubled tips for test
164168
def t1(): "()"
165169
def t2(a, b=None): "(a, b=None)"
166170
def t3(a, *args): "(a, *args)"
@@ -170,39 +174,88 @@ def t6(a, b=None, *args, **kw): "(a, b=None, *args, **kw)"
170174

171175
class TC(object):
172176
"(ai=None, *b)"
173-
def __init__(self, ai=None, *b): "(ai=None, *b)"
174-
def t1(self): "()"
175-
def t2(self, ai, b=None): "(ai, b=None)"
176-
def t3(self, ai, *args): "(ai, *args)"
177-
def t4(self, *args): "(*args)"
178-
def t5(self, ai, *args): "(ai, *args)"
179-
def t6(self, ai, b=None, *args, **kw): "(ai, b=None, *args, **kw)"
180-
181-
__main__.__dict__.update(locals())
182-
183-
def test(tests):
184-
ct = CallTips()
185-
failed=[]
186-
for t in tests:
187-
expected = t.__doc__ + "\n" + t.__doc__
188-
name = t.__name__
189-
# exercise fetch_tip(), not just get_argspec()
190-
try:
191-
qualified_name = "%s.%s" % (t.__self__.__class__.__name__, name)
192-
except AttributeError:
193-
qualified_name = name
194-
argspec = ct.fetch_tip(qualified_name)
195-
if argspec != expected:
196-
failed.append(t)
197-
fmt = "%s - expected %s, but got %s"
198-
print(fmt % (t.__name__, expected, get_argspec(t)))
199-
print("%d of %d tests failed" % (len(failed), len(tests)))
177+
def __init__(self, ai=None, *b): "(self, ai=None, *b)"
178+
def t1(self): "(self)"
179+
def t2(self, ai, b=None): "(self, ai, b=None)"
180+
def t3(self, ai, *args): "(self, ai, *args)"
181+
def t4(self, *args): "(self, *args)"
182+
def t5(self, ai, *args): "(self, ai, *args)"
183+
def t6(self, ai, b=None, *args, **kw): "(self, ai, b=None, *args, **kw)"
184+
@classmethod
185+
def cm(cls, a): "(cls, a)"
186+
@staticmethod
187+
def sm(b): "(b)"
188+
def __call__(self, ci): "(ci)"
200189

201190
tc = TC()
202-
tests = (t1, t2, t3, t4, t5, t6,
203-
TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6)
204191

205-
test(tests)
192+
# Python classes that inherit builtin methods
193+
class Int(int): "Int(x[, base]) -> integer"
194+
class List(list): "List() -> new empty list"
195+
# Simulate builtin with no docstring for default argspec test
196+
class SB: __call__ = None
197+
198+
__main__.__dict__.update(locals()) # required for get_entity eval()
199+
200+
num_tests = num_fail = 0
201+
tip = CallTips().fetch_tip
202+
203+
def test(expression, expected):
204+
nonlocal num_tests, num_fail
205+
num_tests += 1
206+
argspec = tip(expression)
207+
if argspec != expected:
208+
num_fail += 1
209+
fmt = "%s - expected\n%r\n - but got\n%r"
210+
print(fmt % (expression, expected, argspec))
211+
212+
def test_builtins():
213+
# if first line of a possibly multiline compiled docstring changes,
214+
# must change corresponding test string
215+
test('int', "int(x[, base]) -> integer")
216+
test('Int', Int.__doc__)
217+
test('types.MethodType', "method(function, instance)")
218+
test('list', "list() -> new empty list")
219+
test('List', List.__doc__)
220+
test('list.__new__',
221+
'T.__new__(S, ...) -> a new object with type S, a subtype of T')
222+
test('list.__init__',
223+
'x.__init__(...) initializes x; see help(type(x)) for signature')
224+
append_doc = "L.append(object) -> None -- append object to end"
225+
test('list.append', append_doc)
226+
test('[].append', append_doc)
227+
test('List.append', append_doc)
228+
test('SB()', _default_callable_argspec)
229+
230+
def test_funcs():
231+
for func in (t1, t2, t3, t4, t5, t6, TC,):
232+
fdoc = func.__doc__
233+
test(func.__name__, fdoc + "\n" + fdoc)
234+
for func in (TC.t1, TC.t2, TC.t3, TC.t4, TC.t5, TC.t6, TC.cm, TC.sm):
235+
fdoc = func.__doc__
236+
test('TC.'+func.__name__, fdoc + "\n" + fdoc)
237+
238+
def test_methods():
239+
for func in (tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6):
240+
fdoc = func.__doc__
241+
test('tc.'+func.__name__, _self_pat.sub("", fdoc) + "\n" + fdoc)
242+
fdoc = tc.__call__.__doc__
243+
test('tc', fdoc + "\n" + fdoc)
244+
245+
def test_non_callables():
246+
# expression evaluates, but not to a callable
247+
for expr in ('0', '0.0' 'num_tests', b'num_tests', '[]', '{}'):
248+
test(expr, '')
249+
# expression does not evaluate, but raises an exception
250+
for expr in ('1a', 'xyx', 'num_tests.xyz', '[int][1]', '{0:int}[1]'):
251+
test(expr, '')
252+
253+
test_builtins()
254+
test_funcs()
255+
test_non_callables()
256+
test_methods()
257+
258+
print("%d of %d tests failed" % (num_fail, num_tests))
206259

207260
if __name__ == '__main__':
208261
main()

Lib/idlelib/NEWS.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
What's New in IDLE 3.2.4?
22
=========================
33

4+
- Issue # 12510: Attempt to get certain tool tips no longer crashes IDLE.
5+
Erroneous tool tips have been corrected. Default added for callables.
6+
7+
- Issue10365: File open dialog now works instead of crashing even when
8+
parent window is closed while dialog is open.
9+
10+
- Issue 14876: use user-selected font for highlight configuration.
11+
412
- Issue #14937: Perform auto-completion of filenames in strings even for
513
non-ASCII filenames. Likewise for identifiers.
614

Misc/NEWS

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,15 @@ Library
8989
- Issue #14443: Tell rpmbuild to use the correct version of Python in
9090
bdist_rpm. Initial patch by Ross Lagerwall.
9191

92-
- Issue14929: Stop Idle 3.x from closing on Unicode decode errors when grepping.
93-
Patch by Roger Serwy.
92+
- Issue #14929: Stop Idle 3.x from closing on Unicode decode errors when
93+
grepping. Patch by Roger Serwy.
9494

95-
- Issue12510: Attempting to get invalid tooltip no longer closes Idle.
96-
Original patch by Roger Serwy.
95+
- Issue #12510: Attempting to get invalid tooltip no longer closes Idle.
96+
Other tooltipss have been corrected or improved and the number of tests
97+
has been tripled. Original patch by Roger Serwy.
9798

98-
- Issue #10365: File open dialog now works instead of crashing
99-
even when parent window is closed. Patch by Roger Serwy.
99+
- Issue #10365: File open dialog now works instead of crashing even when
100+
the parent window is closed before the dialog. Patch by Roger Serwy.
100101

101102
- Issue #14876: Use user-selected font for highlight configuration.
102103

0 commit comments

Comments
 (0)