cpython: 03b5f75ddac7 (original) (raw)
--- a/Lib/idlelib/CallTips.py +++ b/Lib/idlelib/CallTips.py @@ -100,52 +100,53 @@ class CallTips: return rpcclt.remotecall("exec", "get_the_calltip", (expression,), {}) else:
entity = self.get_entity(expression)[](#l1.7)
return get_argspec(entity)[](#l1.8)
return get_argspec(get_entity(expression))[](#l1.9)
- def get_entity(self, expression):
"""Return the object corresponding to expression evaluated[](#l1.12)
in a namespace spanning sys.modules and __main.dict__.[](#l1.13)
"""[](#l1.14)
if expression:[](#l1.15)
namespace = sys.modules.copy()[](#l1.16)
namespace.update(__main__.__dict__)[](#l1.17)
try:[](#l1.18)
return eval(expression, namespace)[](#l1.19)
except BaseException:[](#l1.20)
# An uncaught exception closes idle, and eval can raise any[](#l1.21)
# exception, especially if user classes are involved.[](#l1.22)
return None[](#l1.23)
- """Return the object corresponding to expression evaluated
- in a namespace spanning sys.modules and main.dict.
- """
- if expression:
namespace = sys.modules.copy()[](#l1.29)
namespace.update(__main__.__dict__)[](#l1.30)
try:[](#l1.31)
return eval(expression, namespace)[](#l1.32)
except BaseException:[](#l1.33)
# An uncaught exception closes idle, and eval can raise any[](#l1.34)
# exception, especially if user classes are involved.[](#l1.35)
return None[](#l1.36)
-def _find_constructor(class_ob):
- "Find the nearest init() in the class tree."
- try:
return class_ob.__init__.__func__[](#l1.41)
- except AttributeError:
for base in class_ob.__bases__:[](#l1.43)
init = _find_constructor(base)[](#l1.44)
if init:[](#l1.45)
return init[](#l1.46)
return None[](#l1.47)
+# 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):
- 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.
fob = _find_constructor(ob)[](#l1.67)
if fob is None:[](#l1.68)
fob = lambda: None[](#l1.69)
elif isinstance(ob, types.MethodType):[](#l1.70)
fob = ob.__func__[](#l1.71)
fob = getattr(ob, '__init__', None)[](#l1.72)
elif isinstance(ob.__call__, types.MethodType):[](#l1.73)
fob = ob.__call__[](#l1.74) else:[](#l1.75) fob = ob[](#l1.76)
if isinstance(fob, (types.FunctionType, types.LambdaType)):[](#l1.77)
if isinstance(fob, (types.FunctionType, types.MethodType)):[](#l1.78) argspec = inspect.formatargspec(*inspect.getfullargspec(fob))[](#l1.79)
pat = re.compile('self\,?\s*')[](#l1.80)
argspec = pat.sub("", argspec)[](#l1.81)
doc = getattr(ob, "__doc__", "")[](#l1.82)
if (isinstance(ob, (type, types.MethodType)) or[](#l1.83)
isinstance(ob.__call__, types.MethodType)):[](#l1.84)
argspec = _self_pat.sub("", argspec)[](#l1.85)
if isinstance(ob.__call__, types.MethodType):[](#l1.87)
doc = ob.__call__.__doc__[](#l1.88)
else:[](#l1.89)
doc = getattr(ob, "__doc__", "")[](#l1.90) if doc:[](#l1.91) doc = doc.lstrip()[](#l1.92) pos = doc.find("\n")[](#l1.93)
@@ -154,13 +155,16 @@ def get_argspec(ob): if argspec: argspec += "\n" argspec += doc[:pos]
################################################# # -# Test code -# +# Test code tests CallTips.fetch_tip, get_entity, and get_argspec + def main():
def t1(): "()" Putting expected in docstrings results in doubled tips for test 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)"[](#l1.117)
def t1(self): "()"[](#l1.118)
def t2(self, ai, b=None): "(ai, b=None)"[](#l1.119)
def t3(self, ai, *args): "(ai, *args)"[](#l1.120)
def t4(self, *args): "(*args)"[](#l1.121)
def t5(self, ai, *args): "(ai, *args)"[](#l1.122)
def t6(self, ai, b=None, *args, **kw): "(ai, b=None, *args, **kw)"[](#l1.123)
- def test(tests):
ct = CallTips()[](#l1.128)
failed=[][](#l1.129)
for t in tests:[](#l1.130)
expected = t.__doc__ + "\n" + t.__doc__[](#l1.131)
name = t.__name__[](#l1.132)
# exercise fetch_tip(), not just get_argspec()[](#l1.133)
try:[](#l1.134)
qualified_name = "%s.%s" % (t.__self__.__class__.__name__, name)[](#l1.135)
except AttributeError:[](#l1.136)
qualified_name = name[](#l1.137)
argspec = ct.fetch_tip(qualified_name)[](#l1.138)
if argspec != expected:[](#l1.139)
failed.append(t)[](#l1.140)
fmt = "%s - expected %s, but got %s"[](#l1.141)
print(fmt % (t.__name__, expected, get_argspec(t)))[](#l1.142)
print("%d of %d tests failed" % (len(failed), len(tests)))[](#l1.143)
def __init__(self, ai=None, *b): "(self, ai=None, *b)"[](#l1.144)
def t1(self): "(self)"[](#l1.145)
def t2(self, ai, b=None): "(self, ai, b=None)"[](#l1.146)
def t3(self, ai, *args): "(self, ai, *args)"[](#l1.147)
def t4(self, *args): "(self, *args)"[](#l1.148)
def t5(self, ai, *args): "(self, ai, *args)"[](#l1.149)
def t6(self, ai, b=None, *args, **kw): "(self, ai, b=None, *args, **kw)"[](#l1.150)
@classmethod[](#l1.151)
def cm(cls, a): "(cls, a)"[](#l1.152)
@staticmethod[](#l1.153)
def sm(b): "(b)"[](#l1.154)
def __call__(self, ci): "(ci)"[](#l1.155)
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
- def test(expression, expected):
nonlocal num_tests, num_fail[](#l1.173)
num_tests += 1[](#l1.174)
argspec = tip(expression)[](#l1.175)
if argspec != expected:[](#l1.176)
num_fail += 1[](#l1.177)
fmt = "%s - expected\n%r\n - but got\n%r"[](#l1.178)
print(fmt % (expression, expected, argspec))[](#l1.179)
- def test_builtins():
# if first line of a possibly multiline compiled docstring changes,[](#l1.183)
# must change corresponding test string[](#l1.184)
test('int', "int(x[, base]) -> integer")[](#l1.185)
test('Int', Int.__doc__)[](#l1.186)
test('types.MethodType', "method(function, instance)")[](#l1.187)
test('list', "list() -> new empty list")[](#l1.188)
test('List', List.__doc__)[](#l1.189)
test('list.__new__',[](#l1.190)
'T.__new__(S, ...) -> a new object with type S, a subtype of T')[](#l1.191)
test('list.__init__',[](#l1.192)
'x.__init__(...) initializes x; see help(type(x)) for signature')[](#l1.193)
append_doc = "L.append(object) -> None -- append object to end"[](#l1.194)
test('list.append', append_doc)[](#l1.195)
test('[].append', append_doc)[](#l1.196)
test('List.append', append_doc)[](#l1.197)
test('SB()', _default_callable_argspec)[](#l1.198)
- def test_funcs():
for func in (t1, t2, t3, t4, t5, t6, TC,):[](#l1.201)
fdoc = func.__doc__[](#l1.202)
test(func.__name__, fdoc + "\n" + fdoc)[](#l1.203)
for func in (TC.t1, TC.t2, TC.t3, TC.t4, TC.t5, TC.t6, TC.cm, TC.sm):[](#l1.204)
fdoc = func.__doc__[](#l1.205)
test('TC.'+func.__name__, fdoc + "\n" + fdoc)[](#l1.206)
- def test_methods():
for func in (tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6):[](#l1.209)
fdoc = func.__doc__[](#l1.210)
test('tc.'+func.__name__, _self_pat.sub("", fdoc) + "\n" + fdoc)[](#l1.211)
fdoc = tc.__call__.__doc__[](#l1.212)
test('tc', fdoc + "\n" + fdoc)[](#l1.213)
- def test_non_callables():
# expression evaluates, but not to a callable[](#l1.216)
for expr in ('0', '0.0' 'num_tests', b'num_tests', '[]', '{}'):[](#l1.217)
test(expr, '')[](#l1.218)
# expression does not evaluate, but raises an exception[](#l1.219)
for expr in ('1a', 'xyx', 'num_tests.xyz', '[int][1]', '{0:int}[1]'):[](#l1.220)
test(expr, '')[](#l1.221)
--- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -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.
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -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
- 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