classification
Title: Idle: increase max calltip width
Type: enhancement Stage: resolved
Components: IDLE Versions: Python 3.3, Python 3.4, Python 2.7
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: terry.reedy Nosy List: python-dev, serhiy.storchaka, skrah, terry.reedy
Priority: normal Keywords: patch

Created on 2014-01-21 22:41 by terry.reedy, last changed 2019-03-25 08:06 by terry.reedy. This issue is now closed.

Files
File name Uploaded Description Edit
idle_calltips_wrap.patch serhiy.storchaka, 2014-01-22 18:50 review
20338-3.3.diff terry.reedy, 2014-01-24 07:09 review
20338-2.7.diff terry.reedy, 2014-01-26 08:14 review
Messages (22)
msg208716 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2014-01-21 22:41
Idle calltips are currently limited to 75 (signature) or 70 (doc line) chars. This is insufficient for some stdlib signatures. Example:

>>> from http.client import HTTPConnection
>>> import inspect
>>> fob=HTTPConnection.__init__
>>> line=inspect.formatargspec(*inspect.getfullargspec(fob))
>>> line, len(line)
('(self, host, port=None, strict=<object object at 0x03125148>, timeout=<object object at 0x01FF7408>, source_address=None)', 121)

The truncated tip displayed is "(self, host, port=None, strict=<object object at 0x03125148>, timeout=<object obj ..."

Given that Idle is a graphics application, not a 80-char text console application, there is no obvious reason for the particular restriction other that esthetics. The tip is in a popup window that is separate from the shell or editor window where '(' was typed. On my widescreen monitor, there is room for, say, 200 chars. If I move the underlying window to the far right, so there is not enough room for even the truncated "HTTPConnection(" tip, Windows just truncates it further, as with any other window.

Serhiy, if you have a *nix machine, can you verify that there is no problem with that either?

The limits appear in multiple places in CallTipWindow.showtip and CallTips.get_argspec. Since get_argspec truncates a docstring line to less that showtip allows, there is no ' ...' added and showtip only affects inspect signatures. Get_argspec could leave length enforcement, whatever we decide it should be, to showtip.

I think the first thing to do is replace the multiple hard-coded numbers with one named constant.
msg208717 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2014-01-21 22:46
Meant to mention that wrapping is a possible alternative to truncation, although a limit of say, 200, would make either alternative rare.
msg208718 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2014-01-21 23:23
On further thought, I think the best place to truncate lines is in get_argspec, after being split from the docstring and before rejoined.
msg208735 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2014-01-22 02:22
I deleted truncation code in CallTipWindow.py, all three versions, but messed up issue number.
2.7 http://hg.python.org/cpython/rev/c28e07377b03
changeset:   88630:c28e07377b03

3.3 (same) http://hg.python.org/cpython/rev/1b89fd73c625
changeset:   88631:1b89fd73c625

Issue still open for changing CallTips._MAX_COLS.
msg208783 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2014-01-22 11:29
There is no problem on Linux.

I think wrapping will be more preferable. 200 columns looks to wide for tips.

Perhaps in 3.4 we can use textwrap.wrap() with the max_lines argument. And left simple truncating in earlier versions.
msg208832 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2014-01-22 17:42
How about we extend the limit from 79 to say 120, to accommodate people who use longer docstring lines, and only wrap the signature line, when present, like the one for HTTPConnection, as the signature is the important information to include.
msg208833 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2014-01-22 18:01
Yes, I think we can extend the limit (until implement wrapping). On my narrow monitor there is a room only for 120 character tip (when it starts right from left side of the screen), so I prefer to wrap long lines. In any case too long lines are not comfortable to read.

>>> len(str(inspect.signature(textwrap.TextWrapper)))
242
msg208839 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2014-01-22 18:50
Here is a patch (for 3.4) which wraps long lines. Now you can display a tip for textwrap.TextWrapper.
msg208893 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2014-01-23 09:15
1. For multiple reasons, I want to at least start with only wrapping the signature string.

I want to keep the code as simple and fast as possible, and not worry much about rare oddball or PEP8-violating cases. I want to only do what is needed for the purpose of calltips, which is to remind one of the call sequence. Adding more detracts from that purpose. Tips are not intended to replace using the doc or help(f) to learn about f.

As it is, I am not happy about possibly including more than 1 or 2 lines of a docstring for Python-coded functions. I intend to revisit that when the dust settles with Clinic and inspect. That fact that another patch *will* be needed, almost certainly, is another reason to not do too much now.

Wrapping overly long docstrings should not needed. Even if the initial _MAX_COL slice of the first line of the docstring of a Python-coded function fails to say enough about the return value, there is help(f) to get full details.

I reread PEP 8 and, contrary to what I (mis) remembered, it says that docstrings should remain limited to 72 (or 79) chars *even if* a project increases the limit for *code* lines up to say, 100. The reason for this is precisely because other software, like help, Idle, and others, read, process, and display docstrings. If docstrings violate this, I think they should be changed, and not accomadated (except as suggested below).

So I want to add, before the docstring fetch, only this:

    if len(argspec) > _MAX_COLS:
        argspec =  textwrap.wrap(argspec, _MAX_COLS,
                subsequent_indent='   ')

dict is definitely an oddball and I do not want to include it in a test at this time. I do not mind suggesting help(dict), though the current docstring is incomplete. Here is the real signature and description, which I verified from the examples below. The multiple example lines should follow a condensed summary.

def dict([map_or_it,] **kwds)
  '''Return new dictionary initialized from pairs of positional-only
  map_or_it, if present, and updated with kwds, if present.

  <start current docstring with specifics>
  '''

>>> dict({'a':1}, b=2)
{'b': 2, 'a': 1}
>>> dict({'a':1}, a=2)
{'a': 2}

2. For two reasons, I want to increase _MAX_COLS a bit, say to 85.

First, when wrapping a signature, the 'words' after the first few typically have the form 'name=value'.  They will average much longer than the average English word (4, I think). The average wrapped line length will be _MAX_COLS - half the average 'word' length. For example (one which should become a test):

>>> for line in textwrap.wrap(str(signature(textwrap.TextWrapper)),
		width = 85, subsequent_indent='   '):
	print(line)

(width=70, initial_indent='', subsequent_indent='', expand_tabs=True,
   replace_whitespace=True, fix_sentence_endings=False, break_long_words=True,
   drop_whitespace=True, break_on_hyphens=True, tabsize=8, *, max_lines=None,
   placeholder=' [...]')

The lengths, including indents, are 69, 78, 77, and 24, all <= 79. Very few signature lines, wrapped or not, will fall in [80-85].

Second, PEP 8 allows initial summary lines to be 79 chars, as they are not "flowing long blocks of text". (I verified this with Guido.) Some docstrings in the stdlib 'cheat' a few chars, and 85 will accommodate those without ever being excessively long.
msg208900 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2014-01-23 09:41
Well, do this.

I think we should add several test functions with too long signatures and/or docstrings.
msg208901 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2014-01-23 09:43
And note that currently there is a bug in doc.split('\n', 5)[:_MAX_LINES]. An argument of split() should be _MAX_LINES.
msg209041 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2014-01-24 07:09
I changed 5 to _MAX_LINES.

Other than trivial duplication, I think each test example should be added for a reason, because there is something different about it that might conceivably cause a failure. For example, there should be a case with more than _MAX_LINES in the docstring. But I think one is enough to make sure we split with maxsplit. And I think one text wrap is enough.

The many 't#' test functions were needed for 2.x because it has 12 lines of complicated code that were replaced here by 1.
   argspec = inspect.formatargspec(*inspect.getfullargspec(fob))
For 3.x, we would not need as many tests if the inspect methods were adequately tested and debugged. However, they were not fixed to handle keyword only args until 3.4 and they are still in flux.

The attached patch will need adjustment for 2.7 (I believe) and 3.4 (because *,... is handled better). I plan to do so when fresher.
msg209042 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2014-01-24 07:51
textwrap.TextWrapper in 3.3 doesn't have '*' and following parameters.
msg209288 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2014-01-26 08:14
I removed the incorrect * comment.

Attached is a 2.7 port of the 3.3 patch. It turns out that textwrap.TextWrap in 2.7 is an old-style class. Such did not work previously because they do not have .__call__ and failed the hasattr() check, now a try:except check. In 2.7, callable(old) == True, so there must be an explicit type check. I added one in the patch.
msg209292 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2014-01-26 08:35
LGTM. But I prefer larger indent.

Perhaps we need explicit tests for constructors of old- and new-style classes, with __new__, with __init__, and without __new__ and __init__.
msg209372 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2014-01-27 01:25
New changeset d960c4cabb2b by Terry Jan Reedy in branch '2.7':
Issue #20338: Increase allowed tip width slightly and wrap long signagure lines.
http://hg.python.org/cpython/rev/d960c4cabb2b

New changeset 7dcc19308a7b by Terry Jan Reedy in branch '3.3':
Issue #20338: Increase allowed tip width slightly and wrap long signagure lines.
http://hg.python.org/cpython/rev/7dcc19308a7b

New changeset f7061219303c by Terry Jan Reedy in branch 'default':
Issue #20338: Increase allowed tip width slightly and wrap long signagure lines.
http://hg.python.org/cpython/rev/f7061219303c
msg209374 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2014-01-27 01:37
I increased indent to 4 spaces and made it easier to change.

I believe old-styles classes do not have .__new__, so test of TextWrapper will ensure that they work.

Inspect.signature should have correct logic to handle .__new__, with or without .__init__. (I presume that if both are present, they must be consistent, so .__init__ should be used.) You might want to check both behavior and test file for .signature.

Idle gets .signature upgrades for free when we switch, which I hope can be in a few months at worst. Programmer use of .__new__ without .__init__ is rare enough (extremely so) that I prefer to wait. As it is, switching will already mean removing some of the current logic.
msg209391 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2014-01-27 04:06
Actually, all new-style classes .__new__ and .__init__ from object if not over-ridden. The calltip code depends on the .__init__ inheritance.
msg209523 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2014-01-28 09:48
I think test_idle is failing on many build slaves following this
commit.
msg209545 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2014-01-28 13:08
On 1/28/2014 4:48 AM, Stefan Krah wrote:
>
> Stefan Krah added the comment:
>
> I think test_idle is failing on many build slaves following this
> commit.

The two failures I saw on the first 4 3.x bots with Idle failures are 
the result of changes in the list docstrings. I presume these are 
related to use of Argument Clinic. I knew and documented that such 
failure was a possibility.

On my Win 7 machine

Jan 20, 32-bit repository build just before I added the tests that now fail.
 >>> list.__doc__
"list() -> new empty list\nlist(iterable) -> new list initialized from 
iterable's items"
 >>> list.__init__.__doc__
'x.__init__(...) initializes x; see help(type(x)) for signature'

Jan 26, 64-bit installed 3.4.0b3:
 >>> list.__doc__
"list(iterable) -> new list initialized from iterable's items"
 >>> list.__init__.__doc__
'Initializes self.  See help(type(self)) for accurate signature.'

At some point, I will switch to using inspect.signature for Idle 
calltips and simplify the Idle code. I will also simplify the tests and 
make them more robust.

I will re-compile and adjust the tests by the end of the day.

Terry
msg209610 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2014-01-29 02:33
On 1/28/2014 8:09 AM, Terry J. Reedy wrote:
>
> Terry J. Reedy added the comment:
>
> On 1/28/2014 4:48 AM, Stefan Krah wrote:
>>
>> Stefan Krah added the comment:
>>
>> I think test_idle is failing on many build slaves following this
>> commit.
>
> The two failures I saw on the first 4 3.x bots with Idle failures are
> the result of changes in the list docstrings. I presume these are
> related to use of Argument Clinic. I knew and documented that such
> failure was a possibility.
>
> On my Win 7 machine
>
> Jan 20, 32-bit repository build just before I added the tests that now fail.
>   >>> list.__doc__
> "list() -> new empty list\nlist(iterable) -> new list initialized from
> iterable's items"
>   >>> list.__init__.__doc__
> 'x.__init__(...) initializes x; see help(type(x)) for signature'

In rev88792, UTC 13:00 Larry fixed this mismatch by patching 
test_calltips.py to match the new string.

> Jan 26, 64-bit installed 3.4.0b3:
>   >>> list.__doc__
> "list(iterable) -> new list initialized from iterable's items"
>   >>> list.__init__.__doc__
> 'Initializes self.  See help(type(self)) for accurate signature.'

and fixed fixed the first-line deletion somewhere in changes to about 30 
other files.

Until I know that there is another test somewhere that docstrings are 
not somehow mangled, I will leave some tests here that they are as expected.

> I will re-compile and adjust the tests by the end of the day.

I recompiled but will do something else instead.

Terry
msg209627 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2014-01-29 09:35
Terry J. Reedy <report@bugs.python.org> wrote:
> and fixed fixed the first-line deletion somewhere in changes to about 30 
> other files.

Ah, so it was a Derby accident. Let's hope things stabilize soon.
History
Date User Action Args
2019-03-25 08:06:32terry.reedysetcomponents: + IDLE
2014-01-29 09:35:35skrahsetmessages: + msg209627
2014-01-29 02:33:15terry.reedysetmessages: + msg209610
2014-01-28 13:09:00terry.reedysetmessages: + msg209545
2014-01-28 09:48:01skrahsetnosy: + skrah
messages: + msg209523
2014-01-27 04:06:17terry.reedysetmessages: + msg209391
2014-01-27 01:37:06terry.reedysetstatus: open -> closed
resolution: fixed
messages: + msg209374

stage: commit review -> resolved
2014-01-27 01:25:05python-devsetnosy: + python-dev
messages: + msg209372
2014-01-26 08:35:13serhiy.storchakasetmessages: + msg209292
2014-01-26 08:14:42terry.reedysetfiles: + 20338-2.7.diff

messages: + msg209288
2014-01-24 07:51:31serhiy.storchakasetmessages: + msg209042
2014-01-24 07:09:07terry.reedysetfiles: + 20338-3.3.diff

messages: + msg209041
stage: patch review -> commit review
2014-01-23 09:43:06serhiy.storchakasetmessages: + msg208901
2014-01-23 09:41:34serhiy.storchakasetmessages: + msg208900
2014-01-23 09:15:02terry.reedysetmessages: + msg208893
2014-01-22 18:50:47serhiy.storchakasetfiles: + idle_calltips_wrap.patch
keywords: + patch
messages: + msg208839

stage: needs patch -> patch review
2014-01-22 18:01:30serhiy.storchakasetmessages: + msg208833
2014-01-22 17:42:17terry.reedysetmessages: + msg208832
2014-01-22 11:29:16serhiy.storchakasetmessages: + msg208783
2014-01-22 02:22:49terry.reedysetmessages: + msg208735
stage: test needed -> needs patch
2014-01-21 23:23:18terry.reedysetmessages: + msg208718
2014-01-21 22:46:53terry.reedysetmessages: + msg208717
2014-01-21 22:41:42terry.reedycreate