Skip to content

Commit

Permalink
Add queue of ExceptionHandlerDialogs
Browse files Browse the repository at this point in the history
Fixes #1093.

This commit adds a queue of ExceptionHandlerDialogs, so we only show one
at a time. This avoids the scenario where GTG creates dialogs in a loop,
making them impossible to close.

If an exception happens while the dialog is opened, its own dialog will
pop up when the current one gets closed.

First test with a repro of #1093, raising an exception in the TaskBox
constructor. 3 dialogs pop up consecutively, and it's always possible to
click on "Continue" to keep GTG running. After that, clicking on a
different pane also makes 3 error dialogs pop up in sequence.
Interestingly, GTG hangs on the click on the third Continue. I'm not too
bothered about it because this emulates a rare case of the system being
in a really bad state and it's probably a good idea to exit anyway.

We could consider setting `ignorable` to False when there is already an
exception in the queue, forcing an exit in the "exception while handling
an exception" scenario.

I'm also not sure why there are 3 dialogs and not 5 (the size of the
queue) or however many times the exception is raised (it seems to stop
at some point). But I don't think it matters much.

I also confirmed that I'm seeing "Caught AttributeError ('ListItem'
object has no attribute 'bindings') but not showing dialog for it
because too many exceptions are happening." in the logs.

Second test with the more common case of a single exception. I added an
exception inside `on_search_toggled()`. GTG was working fine until I
clicked on the magnifying glass, at which point the dialog popped up. I
could click on Continue and keep using GTG.
  • Loading branch information
SqAtx committed Jul 7, 2024
1 parent 3774871 commit 69f1fdc
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 17 deletions.
14 changes: 5 additions & 9 deletions GTG/gtk/browser/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# -----------------------------------------------------------------------------

""" The main window for GTG, listing tags, and open and closed tasks """
from __future__ import annotations

import datetime
import logging
Expand Down Expand Up @@ -113,19 +114,14 @@ def __init__(self, app):
self.sidebar = Sidebar(app, app.ds, self)
self.sidebar_vbox.append(self.sidebar)

self.panes = {
'active': None,
'workview': None,
'closed': None,
self.panes: dict[str, TaskPane] = {
'active': TaskPane(self, 'active'),
'workview': TaskPane(self, 'workview'),
'closed': TaskPane(self, 'closed')
}

self.panes['active'] = TaskPane(self, 'active')
self.open_pane.append(self.panes['active'])

self.panes['workview'] = TaskPane(self, 'workview')
self.actionable_pane.append(self.panes['workview'])

self.panes['closed'] = TaskPane(self, 'closed')
self.closed_pane.append(self.panes['closed'])

self._init_context_menus()
Expand Down
2 changes: 1 addition & 1 deletion GTG/gtk/browser/task_pane.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class TaskBox(Gtk.Box):

def __init__(self, config, is_actionable=False):
self.config = config
super(TaskBox, self).__init__()
super().__init__()

self.expander = Gtk.TreeExpander()
self.expander.set_margin_end(6)
Expand Down
51 changes: 44 additions & 7 deletions GTG/gtk/errorhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import functools
import enum
import logging
import queue

from GTG.core import info
from GTG.core.system_info import SystemInfo
Expand Down Expand Up @@ -144,17 +145,53 @@ def handle_response(dialog: ExceptionHandlerDialog, response: int):
log.info("Unhandled response: %r, interpreting as continue instead", response)
dialog.close()

# Discard dialog from the queue, now that it has been closed
exception_dialog_queue.get()

def do_error_dialog(exception, context: str = None, ignorable: bool = True, main_msg=None):
"""
Show (and return) the error dialog.
It does NOT block execution, but should lock the UI
(by being a modal dialog).
# Another exception happened while the dialog was opened
# Open the dialog for that new exception
if not exception_dialog_queue.empty():
next_dialog = exception_dialog_queue.get()
next_dialog.show()


# Queue of ExceptionHandlerDialogs to be shown
# Since GTG keeps running while the dialog is shown, it's possible that
# another exception will be thrown. Its dialog needs to be opened when the first
# one closes.
# Capping the size of the queue, to avoid trying to queue an infinite number of
# dialogs. This has happened in #1093.
exception_dialog_queue = queue.Queue(maxsize=5)


def do_error_dialog(exception, context: str = None, ignorable: bool = True, main_msg=None) -> None:
"""Show (and return) the error dialog.
It does NOT block execution, but should lock the UI (by being a modal dialog).
Only show one exception dialog at a time, to avoid creating an infinity of
them in a loop (see #1093)
If an exception happens while the dialog is active for a previous
exception, add the new one to the exception queue. Its dialog will open when
the previous one gets closed.
"""
# If the queue is empty, we just show the dialog
# It it's not, it means that a dialog is already being shown. In that case,
# the response handler of the current dialog will show the next one.
need_to_show_dialog = exception_dialog_queue.empty()

dialog = ExceptionHandlerDialog(exception, main_msg, ignorable, context)
dialog.connect('response', handle_response)
dialog.show()
return dialog

try:
exception_dialog_queue.put_nowait(dialog)
except queue.Full:
log.warning("Caught %s (%s) but not showing dialog for it because too"
" many exceptions are happening.",
type(exception).__name__, exception)

if need_to_show_dialog:
dialog.show()


def errorhandler(func, context: str = None, ignorable: bool = True, reraise: bool = True):
Expand Down

0 comments on commit 69f1fdc

Please sign in to comment.