From 3b5b49daaca1ad760b546a7f724aeaec38befa3f Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 6 May 2015 08:51:44 +0200 Subject: [PATCH] Move quitter/signal/crash_handler out of qApp. --- qutebrowser/app.py | 64 +++++++++++++++++++-------------- qutebrowser/misc/crashsignal.py | 18 ++++++---- 2 files changed, 49 insertions(+), 33 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 31ed52278..e5dba1fac 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -66,8 +66,22 @@ def run(args): print(version.GPL_BOILERPLATE.strip()) sys.exit(0) + quitter = Quitter(args) + objreg.register('quitter', quitter) + global qApp qApp = Application(args) + qApp.lastWindowClosed.connect(quitter.on_last_window_closed) + + crash_handler = crashsignal.CrashHandler( + app=qApp, quitter=quitter, args=args, parent=qApp) + crash_handler.activate() + objreg.register('crash-handler', crash_handler) + + signal_handler = crashsignal.SignalHandler(app=qApp, quitter=quitter, + parent=qApp) + signal_handler.activate() + objreg.register('signal-handler', signal_handler) try: sent = ipc.send_to_running_instance(args.command) @@ -93,7 +107,7 @@ def run(args): # We didn't really initialize much so far, so we just quit hard. sys.exit(1) - init(args) + init(args, crash_handler) ret = qt_mainloop() return ret @@ -107,8 +121,13 @@ def qt_mainloop(): return qApp.exec_() -def init(args): - """Initialize everything.""" +def init(args, crash_handler): + """Initialize everything. + + Args: + args: The argparse namespace. + crash_handler: The CrashHandler instance. + """ log.init.debug("Starting init...") qApp.setQuitOnLastWindowClosed(False) qApp.setOrganizationName("qutebrowser") @@ -118,7 +137,7 @@ def init(args): utils.actute_warning() try: - _init_modules(args) + _init_modules(args, crash_handler) except (OSError, UnicodeDecodeError) as e: msgbox = QMessageBox( QMessageBox.Critical, "Error while initializing!", @@ -143,7 +162,7 @@ def init(args): QDesktopServices.setUrlHandler('qute', open_desktopservices_url) log.init.debug("Init done!") - qApp.crash_handler.raise_crashdlg() + crash_handler.raise_crashdlg() def _init_icon(): @@ -342,8 +361,13 @@ def _maybe_hide_mouse_cursor(): qApp.restoreOverrideCursor() -def _init_modules(args): - """Initialize all 'modules' which need to be initialized.""" +def _init_modules(args, crash_handler): + """Initialize all 'modules' which need to be initialized. + + Args: + args: The argparse namespace. + crash_handler: The CrashHandler instance. + """ # pylint: disable=too-many-statements log.init.debug("Initializing save manager...") save_manager = savemanager.SaveManager(qApp) @@ -362,7 +386,7 @@ def _init_modules(args): log.init.debug("Initializing web history...") history.init(qApp) log.init.debug("Initializing crashlog...") - qApp.crash_handler.handle_segfault() + crash_handler.handle_segfault() log.init.debug("Initializing sessions...") sessions.init(qApp) log.init.debug("Initializing js-bridge...") @@ -573,6 +597,9 @@ class Quitter: def _shutdown(self, status): # noqa """Second stage of shutdown.""" log.destroy.debug("Stage 2 of shutting down...") + if qApp is None: + # No QApplication exists yet, so quit hard. + sys.exit(status) # Remove eventfilter try: log.destroy.debug("Removing eventfilter...") @@ -602,14 +629,14 @@ class Quitter: "Error while saving {}: {}".format(key, e)) msgbox.exec_() # Re-enable faulthandler to stdout, then remove crash log - log.destroy.debug("Deactiving crash log...") - qApp.crash_handler.destroy_crashlogfile() + log.destroy.debug("Deactivating crash log...") + objreg.get('crash-handler').destroy_crashlogfile() # If we don't kill our custom handler here we might get segfaults log.destroy.debug("Deactiving message handler...") qInstallMessageHandler(None) # Now we can hopefully quit without segfaults log.destroy.debug("Deferring QApplication::exit...") - qApp.signal_handler.deactivate() + objreg.get('signal-handler').deactivate() # We use a singleshot timer to exit here to minimize the likelihood of # segfaults. QTimer.singleShot(0, functools.partial(qApp.exit, status)) @@ -630,11 +657,7 @@ class Application(QApplication): """Main application instance. Attributes: - quitter: The Quitter objet. - crash_handler: The CrashHandler being used. - signal_handler: The SignalHandler being used. _args: ArgumentParser instance. - _shutting_down: True if we're currently shutting down. """ def __init__(self, args): @@ -648,17 +671,6 @@ class Application(QApplication): super().__init__(qt_args) log.init.debug("Initializing application...") - self.quitter = Quitter(args) - self.lastWindowClosed.connect(self.quitter.on_last_window_closed) - objreg.register('quitter', self.quitter) - - self.crash_handler = crashsignal.CrashHandler(app=self, args=args, - parent=self) - self.crash_handler.activate() - objreg.register('crash-handler', self.crash_handler) - - self.signal_handler = crashsignal.SignalHandler(app=self, parent=self) - self.signal_handler.activate() self._args = args objreg.register('args', args) diff --git a/qutebrowser/misc/crashsignal.py b/qutebrowser/misc/crashsignal.py index 53a5ec4bf..76e14edd9 100644 --- a/qutebrowser/misc/crashsignal.py +++ b/qutebrowser/misc/crashsignal.py @@ -43,14 +43,16 @@ class CrashHandler(QObject): Attributes: _app: The QApplication instance. + _quitter: The Quitter instance. _args: The argparse namespace. _crash_dialog: The CrashDialog currently being shown. _crash_log_file: The file handle for the faulthandler crash log. """ - def __init__(self, app, args, parent=None): + def __init__(self, *, app, quitter, args, parent=None): super().__init__(parent) self._app = app + self._quitter = quitter self._args = args self._crash_log_file = None self._crash_dialog = None @@ -160,7 +162,7 @@ class CrashHandler(QObject): exc = (exctype, excvalue, tb) qapp = QApplication.instance() - if not qapp.quitter.quit_status['crash']: + if not self._quitter.quit_status['crash']: log.misc.error("ARGH, there was an exception while the crash " "dialog is already shown:", exc_info=exc) return @@ -185,7 +187,7 @@ class CrashHandler(QObject): qapp.quit() return - qapp.quitter.quit_status['crash'] = False + self._quitter.quit_status['crash'] = False try: pages = self._recover_pages(forgiving=True) @@ -212,7 +214,7 @@ class CrashHandler(QObject): try: self._app.lastWindowClosed.disconnect( - self._app.quitter.on_last_window_closed) + self._quitter.on_last_window_closed) except TypeError: log.destroy.exception("Error while preventing shutdown") self._app.closeAllWindows() @@ -220,7 +222,7 @@ class CrashHandler(QObject): self._args.debug, pages, cmd_history, exc, objects) ret = self._crash_dialog.exec_() if ret == QDialog.Accepted: # restore - self._app.quitter.restart(pages) + self._quitter.restart(pages) # We might risk a segfault here, but that's better than continuing to # run in some undefined state, so we only do the most needed shutdown @@ -241,6 +243,7 @@ class SignalHandler(QObject): Attributes: _app: The QApplication instance. + _quitter: The Quitter instance. _activated: Whether activate() was called. _notifier: A QSocketNotifier used for signals on Unix. _timer: A QTimer used to poll for signals on Windows. @@ -248,9 +251,10 @@ class SignalHandler(QObject): _orig_wakeup_fd: The original wakeup filedescriptor. """ - def __init__(self, app, parent=None): + def __init__(self, *, app, quitter, parent=None): super().__init__(parent) self._app = app + self._quitter = quitter self._notifier = None self._timer = usertypes.Timer(self, 'python_hacks') self._orig_handlers = {} @@ -330,7 +334,7 @@ class SignalHandler(QObject): signal.signal(signal.SIGTERM, self.interrupt_forcefully) # If we call shutdown directly here, we get a segfault. QTimer.singleShot(0, functools.partial( - self._app.quitter.shutdown, 128 + signum)) + self._quitter.shutdown, 128 + signum)) def interrupt_forcefully(self, signum, _frame): """Interrupt forcefully on the second SIGINT/SIGTERM request.