From d5710f8b36a456872f3cadd913a26f4aa76afc01 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 21 Jan 2014 20:45:17 -0500 Subject: [PATCH] Issue #16638: Include up to 5 docstring header lines (before first blank) in Idle calltips. This is needed for builtins, such bytes (which is why 5). Based on patch by Serhiy Storchaka. --- Lib/idlelib/CallTips.py | 26 ++++++++++++++++---------- Lib/idlelib/idle_test/test_calltips.py | 22 ++++++++++++++++++---- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/Lib/idlelib/CallTips.py b/Lib/idlelib/CallTips.py index 83bb63805b3..4f88da6f5df 100644 --- a/Lib/idlelib/CallTips.py +++ b/Lib/idlelib/CallTips.py @@ -116,17 +116,21 @@ def get_entity(expression): # exception, especially if user classes are involved. return None -# The following are used in both get_argspec and tests +# The following are used in get_argspec and some in tests +_MAX_COLS = 79 +_MAX_LINES = 5 # enough for bytes _first_param = re.compile('(?<=\()\w*\,?\s*') _default_callable_argspec = "See source or doc" + def get_argspec(ob): '''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. + The next lines are the first lines of the doc string up to the first + empty line or _MAX_LINES. For builtins, this typically includes + the arguments in addition to the return value. ''' argspec = "" try: @@ -150,13 +154,15 @@ def get_argspec(ob): 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] + lines = [argspec] if argspec else [] + for line in doc.split('\n', 5)[:_MAX_LINES]: + line = line.strip() + if not line: + break + if len(line) > _MAX_COLS: + line = line[: _MAX_COLS - 3] + '...' + lines.append(line) + argspec = '\n'.join(lines) if not argspec: argspec = _default_callable_argspec return argspec diff --git a/Lib/idlelib/idle_test/test_calltips.py b/Lib/idlelib/idle_test/test_calltips.py index 18cc35c1f9c..eca24ec1293 100644 --- a/Lib/idlelib/idle_test/test_calltips.py +++ b/Lib/idlelib/idle_test/test_calltips.py @@ -42,17 +42,15 @@ class Get_signatureTest(unittest.TestCase): # For a simple mismatch, change the expected output to the actual. def test_builtins(self): - # These test will break if # Python class that inherits builtin methods class List(list): "List() doc" - # Simulate builtin with no docstring for default argspec test + # Simulate builtin with no docstring for default tip test class SB: __call__ = None def gtest(obj, out): self.assertEqual(signature(obj), out) - gtest(list, "list() -> new empty list") gtest(List, List.__doc__) gtest(list.__new__, 'T.__new__(S, ...) -> a new object with type S, a subtype of T') @@ -66,6 +64,21 @@ class Get_signatureTest(unittest.TestCase): gtest(types.MethodType, "method(function, instance)") gtest(SB(), default_tip) + def test_multiline_docstring(self): + # Test fewer lines than max. + self.assertEqual(signature(list), + "list() -> new empty list\n" + "list(iterable) -> new list initialized from iterable's items") + + # Test max lines and line (currently) too long. + self.assertEqual(signature(bytes), +"bytes(iterable_of_ints) -> bytes\n" +"bytes(string, encoding[, errors]) -> bytes\n" +"bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer\n" +#bytes(int) -> bytes object of size given by the parameter initialized with null bytes +"bytes(int) -> bytes object of size given by the parameter initialized with n...\n" +"bytes() -> empty bytes object") + def test_functions(self): def t1(): 'doc' t1.tip = "()" @@ -100,7 +113,8 @@ class Get_signatureTest(unittest.TestCase): assert ct._first_param.sub('', uni) == '(a)' def test_no_docstring(self): - def nd(s): pass + def nd(s): + pass TC.nd = nd self.assertEqual(signature(nd), "(s)") self.assertEqual(signature(TC.nd), "(s)")