Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exception on IDLE closing #64366

Closed
serhiy-storchaka opened this issue Jan 7, 2014 · 28 comments
Closed

Exception on IDLE closing #64366

serhiy-storchaka opened this issue Jan 7, 2014 · 28 comments
Assignees
Labels
topic-IDLE type-bug An unexpected behavior, bug, or error

Comments

@serhiy-storchaka
Copy link
Member

BPO 20167
Nosy @terryjreedy, @kbkaiser, @taleinat, @serwy, @serhiy-storchaka
Files
  • taleinat_idle_closing_exception.patch: Patch to fix the bug by catching and ignoring the raised exception. BROKEN: causes import error.
  • taleinat_idle_closing_exception_2.patch: New patch with the try/except inside the loop and added comment. BROKEN: causes import error.
  • taleinat_idle_closing_exception_3.patch: Patch which fixes the bug. Tested.
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/terryjreedy'
    closed_at = <Date 2014-10-11.22:23:03.204>
    created_at = <Date 2014-01-07.20:57:38.420>
    labels = ['expert-IDLE', 'type-bug']
    title = 'Exception on IDLE closing'
    updated_at = <Date 2014-10-11.22:23:03.202>
    user = 'https://github.com/serhiy-storchaka'

    bugs.python.org fields:

    activity = <Date 2014-10-11.22:23:03.202>
    actor = 'terry.reedy'
    assignee = 'terry.reedy'
    closed = True
    closed_date = <Date 2014-10-11.22:23:03.204>
    closer = 'terry.reedy'
    components = ['IDLE']
    creation = <Date 2014-01-07.20:57:38.420>
    creator = 'serhiy.storchaka'
    dependencies = []
    files = ['33923', '33926', '33927']
    hgrepos = []
    issue_num = 20167
    keywords = ['patch', '3.4regression']
    message_count = 28.0
    messages = ['207599', '210297', '210298', '210300', '210304', '210307', '210308', '210312', '210361', '210600', '210603', '210861', '210872', '211130', '212332', '228941', '228943', '228944', '228946', '228947', '228948', '228952', '228953', '228954', '228956', '228957', '228958', '229106']
    nosy_count = 7.0
    nosy_names = ['terry.reedy', 'kbk', 'taleinat', 'roger.serwy', 'RusiMody', 'python-dev', 'serhiy.storchaka']
    pr_nums = []
    priority = 'high'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue20167'
    versions = ['Python 2.7', 'Python 3.4', 'Python 3.5']

    @serhiy-storchaka
    Copy link
    Member Author

    When run IDLE with file name as agument

    $ ./python -m idlelib.idle Lib/decimal.py

    and then close or exit it, following traceback is printed:

    Exception ignored in: <bound method _ComplexBinder.__del__ of <idlelib.MultiCall._ComplexBinder object at 0xb5ead62c>>
    Traceback (most recent call last):
      File "/home/serhiy/py/cpython/Lib/idlelib/MultiCall.py", line 230, in __del__
      File "/home/serhiy/py/cpython/Lib/tkinter/__init__.py", line 1043, in unbind
    _tkinter.TclError: can't invoke "bind" command:  application has been destroyed
    Exception ignored in: <bound method _ComplexBinder.__del__ of <idlelib.MultiCall._ComplexBinder object at 0xb5eab92c>>
    Traceback (most recent call last):
      File "/home/serhiy/py/cpython/Lib/idlelib/MultiCall.py", line 230, in __del__
      File "/home/serhiy/py/cpython/Lib/tkinter/__init__.py", line 1043, in unbind
    _tkinter.TclError: can't invoke "bind" command:  application has been destroyed
    Exception ignored in: <bound method _ComplexBinder.__del__ of <idlelib.MultiCall._ComplexBinder object at 0xb5ea4e4c>>
    Traceback (most recent call last):
      File "/home/serhiy/py/cpython/Lib/idlelib/MultiCall.py", line 230, in __del__
      File "/home/serhiy/py/cpython/Lib/tkinter/__init__.py", line 1043, in unbind
    _tkinter.TclError: can't invoke "bind" command:  application has been destroyed
    Exception ignored in: <bound method _ComplexBinder.__del__ of <idlelib.MultiCall._ComplexBinder object at 0xb5ea46ec>>
    Traceback (most recent call last):
      File "/home/serhiy/py/cpython/Lib/idlelib/MultiCall.py", line 230, in __del__
      File "/home/serhiy/py/cpython/Lib/tkinter/__init__.py", line 1043, in unbind
    _tkinter.TclError: can't invoke "bind" command:  application has been destroyed

    This is occurred only in 3.4.

    @serhiy-storchaka serhiy-storchaka added topic-IDLE type-bug An unexpected behavior, bug, or error labels Jan 7, 2014
    @taleinat
    Copy link
    Contributor

    taleinat commented Feb 5, 2014

    Confirmed on OSX 10.8 with Python 3.4 (built from default branch, changeset 88969:32af4954e46a).

    Note that this doesn't happen when opening just a shell window, e.g. by running ./python -m idlelib.idle. Furthermore, even when running IDLE as described in the OP, if I open a shell window and then close, I don't get an exception.

    @taleinat
    Copy link
    Contributor

    taleinat commented Feb 5, 2014

    This is caused by MultiCall's _ComplexBinder.__del__() being called during app shutdown. _ComplexBinder.__del__() unbinds a bunch of event handlers from the widget to which it is attached. It seems that there's some edge case here where the underlying Tk widget has already been destroyed.

    Instead of trying to debug all of the Tk events and app shutdown order, I propose just surrounding this __del__() code with a try/except block, catching _tkinter.TclError and ignoring it. From my (somehwat limited) understanding of MultiCall, this shouldn't do any harm.

    See attached patch.

    @serhiy-storchaka
    Copy link
    Member Author

    1. try/except should be inside a loop, not outside.

    2. It would be better not to hide the problem under the rug, but find why the order of destruction is different when open shell window. Of course, if better solution will not be found, we will have to apply this patch before 3.4 releasing.

    @taleinat
    Copy link
    Contributor

    taleinat commented Feb 5, 2014

    I put the try/except outside of the loop on purpose. If calling the widget's unbind() method fails once, it will fail forever afterwards.

    If you prefer a different spelling, move the try/except into the loop, and break out of the loop in case of an exception:

    for seq, id in self.handlerids:
        try:
            self.widget.unbind(self.widgetinst, seq, id)
        except _tkinter.TclError:
            break

    As for avoiding the exception in the first place, I'm sure figuring out how IDLE shuts down would be a lot of work, and I honestly don't think it's necessary. Note that this problem is triggered in a __del__() method; making sure these are called at a proper time is problematic because the timing depends on the GC.

    @taleinat
    Copy link
    Contributor

    taleinat commented Feb 5, 2014

    Attaching second patch, to replace the first.

    As suggested by Serhiy, I moved the try/except block into the loop.

    I also added a comment explaining the try/except block and referencing this issue.

    @taleinat
    Copy link
    Contributor

    taleinat commented Feb 5, 2014

    Both previous patches caused an import error. Here's a new patch with that fixed, and actually tested to fix the bug.

    @terryjreedy
    Copy link
    Member

    I tried all three versions both installed and recent repository builds and verified that the overt issue is limited to 3.4. I agree that the exception message suggests stopping with the first exception. Since there is a small cost to try: and break, I am inclined to move the try back out of the loop. The only thing it could mask is a problem with self.handlerids, and I would expect that if that is corrupted or missing, there would be problems before shutdown.

    I know that in 3.4, shutdown order has been modified and the wording of the warning was discussed on pydev. I do not know (and do not care at the moment) if either ignoring __del__ exceptions or warning about them is new in 3.4. What I do remember from the discussion is agreement that 1. __del__ exceptions should be caught so shutdown can continue; 2. a warning should be given in case the exception indicates a bug in __del__; and 3. __del__ writers are responsible to catch exceptions that do not indicate a bug, so as to avoid the warning. So I am inclined to backport to all versions.

    @terryjreedy
    Copy link
    Member

    More thoughts: The reason for explicit __del__ is to release memory resources connected to other objects or structures at the behest of the Python instance being deleted. If __del__ were only called at shutdown, it would not be needed, as all memory is released anyway and we could delete __del__. If __del__ is called before shutdown (as would happen if the class were unittested) then exceptions would indicate a bug and should not be ignored. From this viewpoint, not only should the try: except: be within the loop, but the break should be conditioned on the exception message.

      if exc.args[0] == 'can't invoke "bind" command:  application has been destroyed': break

    The modern solution to this dilemma is to use .close instead of .__del__ and either explicitly call .close or have it called in the .__exit__ method of a context manager. For Idle classes, this would not be a problem for future test code, but it would be much trickier finding all places in current code where instances of a class with a .__del__ method get deleted. So a compromise patch with conditioned break seems most practical.

    I tried a few more experiments: run the .py file (F5, to create a Shell window) and then close both windows. Whether the closing was shell first or editor first, the messages do not appear. Three runs with -n gave the same results. So it seems that the problem only appears when there has never been a shell window. I agree with Serhiy (his point 2) that this is disconcerting and could indicate a 3.4-specific problem with Tk, tkinter, or Idle.

    @terryjreedy
    Copy link
    Member

    I checked that -m idlelib acts the same as -m idlelib.idle, and that there is still a problem after opening a second editor window after the original. So the messages seem tied to not opening a shell window. In testing the patch, I noticed that exceptions are now occurring in all three .__del__ methods. With the candidate release imminent, I decided to only fix what is broken and only apply to 3.4 and not worry now about possible merge problems in the future.

    @terryjreedy terryjreedy self-assigned this Feb 8, 2014
    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Feb 8, 2014

    New changeset f9f2c57f7d00 by Terry Jan Reedy in branch 'default':
    Issue bpo-20167: Suppress 3.4 specific 'Exception ignored' messages.
    http://hg.python.org/cpython/rev/f9f2c57f7d00

    @serhiy-storchaka
    Copy link
    Member Author

    Changes for _SimpleBinder.__del__ look doubtful. Any TclError exception is suppressed and "if" statement does nothing. Perhaps you forgot "else: raise"? And may be in other places too.

    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Feb 10, 2014

    New changeset b9e124851e47 by Terry Jan Reedy in branch 'default':
    Issue bpo-20167: Add missing else: break in 3 places as noticed by Serhiy.
    http://hg.python.org/cpython/rev/b9e124851e47

    @taleinat
    Copy link
    Contributor

    Good catches, Terry and Serhiy!

    @terryjreedy
    Copy link
    Member

    This has been left open to see if the undlying problem can be found. If bpo-20567 is a guide, there might be a root.destroy that should be followed by del root.

    @RusiMody
    Copy link
    Mannequin

    RusiMody mannequin commented Oct 10, 2014

    Just confirming:
    idle 3.4.1-1 on debian testing

    Start idle3
    Open recent file -> some file
    Close file
    Close interpreter (and idle)
    Get this

    Exception ignored in: <bound method _ComplexBinder.__del__ of <idlelib.MultiCall._ComplexBinder object at 0x7fc53638f4e0>>
    Traceback (most recent call last):
      File "/usr/lib/python3.4/idlelib/MultiCall.py", line 244, in __del__
        self.widget.unbind(self.widgetinst, seq, id)
      File "/usr/lib/python3.4/tkinter/__init__.py", line 1071, in unbind
        self.tk.call('bind', self._w, sequence, '')
    _tkinter.TclError: can't invoke "bind" command: application has been destroyed
    Exception ignored in: <bound method _ComplexBinder.__del__ of <idlelib.MultiCall._ComplexBinder object at 0x7fc5363b7240>>
    Traceback (most recent call last):
      File "/usr/lib/python3.4/idlelib/MultiCall.py", line 244, in __del__
        self.widget.unbind(self.widgetinst, seq, id)
      File "/usr/lib/python3.4/tkinter/__init__.py", line 1071, in unbind
        self.tk.call('bind', self._w, sequence, '')
    _tkinter.TclError: can't invoke "bind" command: application has been destroyed
    Exception ignored in: <bound method _ComplexBinder.__del__ of <idlelib.MultiCall._ComplexBinder object at 0x7fc5363b75f8>>
    Traceback (most recent call last):
      File "/usr/lib/python3.4/idlelib/MultiCall.py", line 244, in __del__
        self.widget.unbind(self.widgetinst, seq, id)
      File "/usr/lib/python3.4/tkinter/__init__.py", line 1071, in unbind
        self.tk.call('bind', self._w, sequence, '')
    _tkinter.TclError: can't invoke "bind" command: application has been destroyed
    Exception ignored in: <bound method _ComplexBinder.__del__ of <idlelib.MultiCall._ComplexBinder object at 0x7fc5363b79b0>>
    Traceback (most recent call last):
      File "/usr/lib/python3.4/idlelib/MultiCall.py", line 244, in __del__
        self.widget.unbind(self.widgetinst, seq, id)
      File "/usr/lib/python3.4/tkinter/__init__.py", line 1071, in unbind
        self.tk.call('bind', self._w, sequence, '')
    _tkinter.TclError: can't invoke "bind" command: application has been destroyed

    @terryjreedy
    Copy link
    Member

    The problem is the change in the exception message between these two lines
    can't invoke "bind" command: application has been destroyed
    can't invoke "bind" command: application has been destroyed

    In your copy of MultiCall.py, line 63-4, change
    APPLICATION_GONE = '''\
    can't invoke "bind" command: application has been destroyed'''
    by changing ' ' to ' '.

    I will reduce the string to "application has been destroyed" and the check from equality to containment.

    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Oct 10, 2014

    New changeset ce0316007b21 by Terry Jan Reedy in branch '3.4':
    Issue bpo-20167: revise condition to accomodate message change.
    https://hg.python.org/cpython/rev/ce0316007b21

    @terryjreedy
    Copy link
    Member

    I verified that problem had returned on Windows as well. It would be good to have a test that would fail if the tcl error message changed again.

    @RusiMody
    Copy link
    Mannequin

    RusiMody mannequin commented Oct 10, 2014

    On Fri, Oct 10, 2014 at 8:46 AM, Terry J. Reedy <report@bugs.python.org> wrote:

    Terry J. Reedy added the comment:

    I verified that problem had returned on Windows as well. It would be good to have a test that would fail if the tcl error message changed again.

    ----------
    resolution: fixed ->
    stage: needs patch -> test needed
    versions: +Python 3.5

    It is failing again!!

    Version 3.4.1-1: checked that removing that space makes the error go away.
    Then saw that there is a new version 3.4.2~rc1-1 (almost certainly not
    containing your change) in the debian repos.

    Upgrading to that has made the error return!

    @RusiMody
    Copy link
    Mannequin

    RusiMody mannequin commented Oct 10, 2014

    On Fri, Oct 10, 2014 at 8:49 AM, Rustom Mody <rustompmody@gmail.com> wrote:

    On Fri, Oct 10, 2014 at 8:46 AM, Terry J. Reedy <report@bugs.python.org> wrote:
    >
    > Terry J. Reedy added the comment:
    >
    > I verified that problem had returned on Windows as well. It would be good to have a test that would fail if the tcl error message changed again.
    >
    > ----------
    > resolution: fixed ->
    > stage: needs patch -> test needed
    > versions: +Python 3.5

    It is failing again!!

    Version 3.4.1-1: checked that removing that space makes the error go away.
    Then saw that there is a new version 3.4.2~rc1-1 (almost certainly not
    containing your change) in the debian repos.

    Upgrading to that has made the error return!

    By which I meant to say not that it has 'returned'
    But that its there in 3.4.2 as well

    @terryjreedy
    Copy link
    Member

    I know.  3.4.2.rc1 was released over 2 weeks ago and 3.4.2 just yesterday (essentially without change).  So you have to delete space again. Since 3.4.1 worked for many people, perhaps you have an ancient version of tk.  You can get major/minor number with
    >>> import tkinter as tk
    >>> tk.TclVersion
    8.6
    but 8.5 covers a long time span and many fixes.  I forget how to get the third, micro number.

    @serhiy-storchaka
    Copy link
    Member Author

    I forget how to get the third, micro number.

    tkinter.Tcl().call('info', 'patchlevel')

    @serhiy-storchaka
    Copy link
    Member Author

    On 3.5 the issue is gone, but on 3.4 and 2.7 following traceback are generated:

    $ ./python -m idlelib.idle Lib/decimal.py
    Traceback (most recent call last):
      File "/home/serhiy/py/cpython-3.4/Lib/runpy.py", line 170, in _run_module_as_main
        "__main__", mod_spec)
      File "/home/serhiy/py/cpython-3.4/Lib/runpy.py", line 85, in _run_code
        exec(code, run_globals)
      File "/home/serhiy/py/cpython-3.4/Lib/idlelib/idle.py", line 11, in <module>
        idlelib.PyShell.main()
      File "/home/serhiy/py/cpython-3.4/Lib/idlelib/PyShell.py", line 1562, in main
        if flist.open(filename) is None:
      File "/home/serhiy/py/cpython-3.4/Lib/idlelib/FileList.py", line 36, in open
        edit = self.EditorWindow(self, filename, key)
      File "/home/serhiy/py/cpython-3.4/Lib/idlelib/PyShell.py", line 141, in __init__
        self.color_breakpoint_text()
      File "/home/serhiy/py/cpython-3.4/Lib/idlelib/PyShell.py", line 159, in color_breakpoint_text
        self.text.tag_config('BREAK', cfg)
    AttributeError: 'NoneType' object has no attribute 'tag_config'

    @terryjreedy
    Copy link
    Member

    The above appears to be a system-specific open issue, not a close issue. So it must be a different issue.

    On my Win7
    F:\Python\dev\4\py34\PCbuild>python_d -m idlelib ../Lib/decimal.py
    <close after open with freshly built 3.4.2+>
    F:\Python\dev\4\py34\PCbuild>

    C:\Programs\Python34>python -m idlelib Lib/decimal.py
    <ditto for installed 3.4.2
    C:\Programs\Python34>

    With a bad file name, I get a blank editor.

    In any case, the traceback makes no sense. Before "self.color_breakpoint_text()" in __init__ are 3 "self.text.bind" statements. So self.text = None seems implausible.

    @serhiy-storchaka
    Copy link
    Member Author

    In any case, the traceback makes no sense. Before
    "self.color_breakpoint_text()" in __init__ are 3 "self.text.bind"
    statements. So self.text = None seems implausible.

    The code runs up to self.text.update() in restore_file_breaks() and runs
    further only after windows closing. At this time self.text already is None.

    @serhiy-storchaka
    Copy link
    Member Author

    Changing update() to update_idletasks() fixes the issue. But we should
    investigate why all works on 3.5 and is there downside of this change.

    @terryjreedy
    Copy link
    Member

    Serhiy, I responded to your report and followups on a new issue, bpo-22614.
    Tal, if you can, please test ./python -m idlelib.idle Lib/decimal.py on OSX and respond there.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    topic-IDLE type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    3 participants