[Python-checkins] cpython (3.3): Issue #16630: Make Idle calltips work even when __getattr__ raises.
terry.reedy
python-checkins at python.org
Tue Jan 21 21:37:39 CET 2014
http://hg.python.org/cpython/rev/2fe0b2dcc98c
changeset: 88618:2fe0b2dcc98c
branch: 3.3
parent: 88615:eb7565c212f1
user: Terry Jan Reedy <tjreedy at udel.edu>
date: Tue Jan 21 15:36:51 2014 -0500
summary:
Issue #16630: Make Idle calltips work even when __getattr__ raises.
Initial patch by Roger Serwy.
files:
Lib/idlelib/CallTips.py | 62 +++++++------
Lib/idlelib/idle_test/test_calltips.py | 24 ++++-
2 files changed, 50 insertions(+), 36 deletions(-)
diff --git a/Lib/idlelib/CallTips.py b/Lib/idlelib/CallTips.py
--- a/Lib/idlelib/CallTips.py
+++ b/Lib/idlelib/CallTips.py
@@ -118,47 +118,49 @@
# The following are used in both get_argspec and tests
_first_param = re.compile('(?<=\()\w*\,?\s*')
-_default_callable_argspec = "No docstring, see docs."
+_default_callable_argspec = "See source or doc"
def get_argspec(ob):
- '''Return a string describing the arguments and return of a callable object.
+ '''Return a string describing the signature of a callable object, or ''.
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 hasattr(ob, '__call__'):
- if isinstance(ob, type):
- fob = getattr(ob, '__init__', None)
- elif isinstance(ob.__call__, types.MethodType):
- fob = ob.__call__
- else:
- fob = ob
- if isinstance(fob, (types.FunctionType, types.MethodType)):
- argspec = inspect.formatargspec(*inspect.getfullargspec(fob))
- if (isinstance(ob, (type, types.MethodType)) or
- isinstance(ob.__call__, types.MethodType)):
- argspec = _first_param.sub("", argspec)
+ try:
+ ob_call = ob.__call__
+ except BaseException:
+ return argspec
+ if isinstance(ob, type):
+ fob = ob.__init__
+ elif isinstance(ob_call, types.MethodType):
+ fob = ob_call
+ else:
+ fob = ob
+ if isinstance(fob, (types.FunctionType, types.MethodType)):
+ argspec = inspect.formatargspec(*inspect.getfullargspec(fob))
+ if (isinstance(ob, (type, types.MethodType)) or
+ isinstance(ob_call, types.MethodType)):
+ argspec = _first_param.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")
- if pos < 0 or pos > 70:
- pos = 70
- if argspec:
- argspec += "\n"
- argspec += doc[:pos]
- if not argspec:
- argspec = _default_callable_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")
+ if pos < 0 or pos > 70:
+ pos = 70
+ if argspec:
+ argspec += "\n"
+ argspec += doc[:pos]
+ if not argspec:
+ argspec = _default_callable_argspec
return argspec
if __name__ == '__main__':
from unittest import main
- main('idlelib.idle_test.test_calltips', verbosity=2, exit=False)
+ main('idlelib.idle_test.test_calltips', verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_calltips.py b/Lib/idlelib/idle_test/test_calltips.py
--- a/Lib/idlelib/idle_test/test_calltips.py
+++ b/Lib/idlelib/idle_test/test_calltips.py
@@ -2,6 +2,7 @@
import idlelib.CallTips as ct
import types
+default_tip = ct._default_callable_argspec
# Test Class TC is used in multiple get_argspec test methods
class TC():
@@ -63,7 +64,7 @@
gtest(List.append, append_doc)
gtest(types.MethodType, "method(function, instance)")
- gtest(SB(), ct._default_callable_argspec)
+ gtest(SB(), default_tip)
def test_functions(self):
def t1(): 'doc'
@@ -88,13 +89,13 @@
def test_bound_methods(self):
# test that first parameter is correctly removed from argspec
- # using _first_param re to calculate expected masks re errors
for meth, mtip in ((tc.t1, "()"), (tc.t4, "(*args)"), (tc.t6, "(self)"),
- (TC.cm, "(a)"),):
+ (tc.__call__, '(ci)'), (tc, '(ci)'), (TC.cm, "(a)"),):
self.assertEqual(signature(meth), mtip + "\ndoc")
- self.assertEqual(signature(tc), "(ci)\ndoc")
- # directly test that re works to delete first parameter even when it
- # non-ascii chars, such as various forms of A.
+
+ def test_non_ascii_name(self):
+ # test that re works to delete a first parameter name that
+ # includes non-ascii chars, such as various forms of A.
uni = "(A\u0391\u0410\u05d0\u0627\u0905\u1e00\u3042, a)"
assert ct._first_param.sub('', uni) == '(a)'
@@ -105,6 +106,17 @@
self.assertEqual(signature(TC.nd), "(s)")
self.assertEqual(signature(tc.nd), "()")
+ def test_attribute_exception(self):
+ class NoCall:
+ def __getattr__(self, name):
+ raise BaseException
+ class Call(NoCall):
+ def __call__(self, ci):
+ pass
+ for meth, mtip in ((NoCall, default_tip), (Call, default_tip),
+ (NoCall(), ''), (Call(), '(ci)')):
+ self.assertEqual(signature(meth), mtip)
+
def test_non_callables(self):
for obj in (0, 0.0, '0', b'0', [], {}):
self.assertEqual(signature(obj), '')
--
Repository URL: http://hg.python.org/cpython
More information about the Python-checkins
mailing list