Merge from 3.2, #12510

This commit is contained in:
Terry Jan Reedy 2012-06-07 20:04:17 -04:00
commit 571ee57695
3 changed files with 136 additions and 74 deletions

View File

@ -100,52 +100,53 @@ class CallTips:
return rpcclt.remotecall("exec", "get_the_calltip",
(expression,), {})
else:
entity = self.get_entity(expression)
return get_argspec(entity)
return get_argspec(get_entity(expression))
def get_entity(self, expression):
"""Return the object corresponding to expression evaluated
in a namespace spanning sys.modules and __main.dict__.
"""
if expression:
namespace = sys.modules.copy()
namespace.update(__main__.__dict__)
try:
return eval(expression, namespace)
except BaseException:
# An uncaught exception closes idle, and eval can raise any
# exception, especially if user classes are involved.
return None
def get_entity(expression):
"""Return the object corresponding to expression evaluated
in a namespace spanning sys.modules and __main.dict__.
"""
if expression:
namespace = sys.modules.copy()
namespace.update(__main__.__dict__)
try:
return eval(expression, namespace)
except BaseException:
# An uncaught exception closes idle, and eval can raise any
# exception, especially if user classes are involved.
return None
def _find_constructor(class_ob):
"Find the nearest __init__() in the class tree."
try:
return class_ob.__init__.__func__
except AttributeError:
for base in class_ob.__bases__:
init = _find_constructor(base)
if init:
return init
return None
# The following are used in both get_argspec and tests
_self_pat = re.compile('self\,?\s*')
_default_callable_argspec = "No docstring, see docs."
def get_argspec(ob):
"""Get a string describing the arguments for the given object,
only if it is callable."""
'''Return a string describing the arguments and return of a callable object.
For Python-coded functions and methods, the first line is introspected.
Delete 'self' parameter for classes (.__init__) and bound methods.
The last line is the first line of the doc string. For builtins, this typically
includes the arguments in addition to the return value.
'''
argspec = ""
if ob is not None and hasattr(ob, '__call__'):
if hasattr(ob, '__call__'):
if isinstance(ob, type):
fob = _find_constructor(ob)
if fob is None:
fob = lambda: None
elif isinstance(ob, types.MethodType):
fob = ob.__func__
fob = getattr(ob, '__init__', None)
elif isinstance(ob.__call__, types.MethodType):
fob = ob.__call__
else:
fob = ob
if isinstance(fob, (types.FunctionType, types.LambdaType)):
if isinstance(fob, (types.FunctionType, types.MethodType)):
argspec = inspect.formatargspec(*inspect.getfullargspec(fob))
pat = re.compile('self\,?\s*')
argspec = pat.sub("", argspec)
doc = getattr(ob, "__doc__", "")
if (isinstance(ob, (type, types.MethodType)) or
isinstance(ob.__call__, types.MethodType)):
argspec = _self_pat.sub("", argspec)
if isinstance(ob.__call__, types.MethodType):
doc = ob.__call__.__doc__
else:
doc = getattr(ob, "__doc__", "")
if doc:
doc = doc.lstrip()
pos = doc.find("\n")
@ -154,13 +155,16 @@ def get_argspec(ob):
if argspec:
argspec += "\n"
argspec += doc[:pos]
if not argspec:
argspec = _default_callable_argspec
return argspec
#################################################
#
# Test code
#
# Test code tests CallTips.fetch_tip, get_entity, and get_argspec
def main():
# Putting expected in docstrings results in doubled tips for test
def t1(): "()"
def t2(a, b=None): "(a, b=None)"
def t3(a, *args): "(a, *args)"
@ -170,39 +174,88 @@ def main():
class TC(object):
"(ai=None, *b)"
def __init__(self, ai=None, *b): "(ai=None, *b)"
def t1(self): "()"
def t2(self, ai, b=None): "(ai, b=None)"
def t3(self, ai, *args): "(ai, *args)"
def t4(self, *args): "(*args)"
def t5(self, ai, *args): "(ai, *args)"
def t6(self, ai, b=None, *args, **kw): "(ai, b=None, *args, **kw)"
__main__.__dict__.update(locals())
def test(tests):
ct = CallTips()
failed=[]
for t in tests:
expected = t.__doc__ + "\n" + t.__doc__
name = t.__name__
# exercise fetch_tip(), not just get_argspec()
try:
qualified_name = "%s.%s" % (t.__self__.__class__.__name__, name)
except AttributeError:
qualified_name = name
argspec = ct.fetch_tip(qualified_name)
if argspec != expected:
failed.append(t)
fmt = "%s - expected %s, but got %s"
print(fmt % (t.__name__, expected, get_argspec(t)))
print("%d of %d tests failed" % (len(failed), len(tests)))
def __init__(self, ai=None, *b): "(self, ai=None, *b)"
def t1(self): "(self)"
def t2(self, ai, b=None): "(self, ai, b=None)"
def t3(self, ai, *args): "(self, ai, *args)"
def t4(self, *args): "(self, *args)"
def t5(self, ai, *args): "(self, ai, *args)"
def t6(self, ai, b=None, *args, **kw): "(self, ai, b=None, *args, **kw)"
@classmethod
def cm(cls, a): "(cls, a)"
@staticmethod
def sm(b): "(b)"
def __call__(self, ci): "(ci)"
tc = TC()
tests = (t1, t2, t3, t4, t5, t6,
TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6)
test(tests)
# Python classes that inherit builtin methods
class Int(int): "Int(x[, base]) -> integer"
class List(list): "List() -> new empty list"
# Simulate builtin with no docstring for default argspec test
class SB: __call__ = None
__main__.__dict__.update(locals()) # required for get_entity eval()
num_tests = num_fail = 0
tip = CallTips().fetch_tip
def test(expression, expected):
nonlocal num_tests, num_fail
num_tests += 1
argspec = tip(expression)
if argspec != expected:
num_fail += 1
fmt = "%s - expected\n%r\n - but got\n%r"
print(fmt % (expression, expected, argspec))
def test_builtins():
# if first line of a possibly multiline compiled docstring changes,
# must change corresponding test string
test('int', "int(x[, base]) -> integer")
test('Int', Int.__doc__)
test('types.MethodType', "method(function, instance)")
test('list', "list() -> new empty list")
test('List', List.__doc__)
test('list.__new__',
'T.__new__(S, ...) -> a new object with type S, a subtype of T')
test('list.__init__',
'x.__init__(...) initializes x; see help(type(x)) for signature')
append_doc = "L.append(object) -> None -- append object to end"
test('list.append', append_doc)
test('[].append', append_doc)
test('List.append', append_doc)
test('SB()', _default_callable_argspec)
def test_funcs():
for func in (t1, t2, t3, t4, t5, t6, TC,):
fdoc = func.__doc__
test(func.__name__, fdoc + "\n" + fdoc)
for func in (TC.t1, TC.t2, TC.t3, TC.t4, TC.t5, TC.t6, TC.cm, TC.sm):
fdoc = func.__doc__
test('TC.'+func.__name__, fdoc + "\n" + fdoc)
def test_methods():
for func in (tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6):
fdoc = func.__doc__
test('tc.'+func.__name__, _self_pat.sub("", fdoc) + "\n" + fdoc)
fdoc = tc.__call__.__doc__
test('tc', fdoc + "\n" + fdoc)
def test_non_callables():
# expression evaluates, but not to a callable
for expr in ('0', '0.0' 'num_tests', b'num_tests', '[]', '{}'):
test(expr, '')
# expression does not evaluate, but raises an exception
for expr in ('1a', 'xyx', 'num_tests.xyz', '[int][1]', '{0:int}[1]'):
test(expr, '')
test_builtins()
test_funcs()
test_non_callables()
test_methods()
print("%d of %d tests failed" % (num_fail, num_tests))
if __name__ == '__main__':
main()

View File

@ -1,6 +1,14 @@
What's New in IDLE 3.3.0?
=========================
- Issue # 12510: Attempt to get certain tool tips no longer crashes IDLE.
Erroneous tool tips have been corrected. Default added for callables.
- Issue10365: File open dialog now works instead of crashing even when
parent window is closed while dialog is open.
- Issue 14876: use user-selected font for highlight configuration.
- Issue #14937: Perform auto-completion of filenames in strings even for
non-ASCII filenames. Likewise for identifiers.

View File

@ -144,14 +144,15 @@ Library
- Issue #14443: Tell rpmbuild to use the correct version of Python in
bdist_rpm. Initial patch by Ross Lagerwall.
- Issue #14929: Stop Idle 3.x from closing on Unicode decode errors when grepping.
Patch by Roger Serwy.
- Issue #14929: Stop Idle 3.x from closing on Unicode decode errors when
grepping. Patch by Roger Serwy.
- Issue #12515: email now registers a defect if it gets to EOF while parsing
a MIME part without seeing the closing MIME boundary.
- Issue #12510: Attempting to get invalid tooltip no longer closes Idle.
Original patch by Roger Serwy.
Other tooltipss have been corrected or improved and the number of tests
has been tripled. Original patch by Roger Serwy.
- Issue #1672568: email now always decodes base64 payloads, adding padding and
ignoring non-base64-alphabet characters if needed, and registering defects
@ -161,8 +162,8 @@ Library
is a missing header/body separator line. MalformedHeaderDefect, which the
existing code would never actually generate, is deprecated.
- Issue #10365: File open dialog now works instead of crashing
even when parent window is closed. Patch by Roger Serwy.
- Issue #10365: File open dialog now works instead of crashing even when
the parent window is closed before the dialog. Patch by Roger Serwy.
- Issue #8739: Updated smtpd to support RFC 5321, and added support for the
RFC 1870 SIZE extension.