From e5cdb2e8caa99175728f0b4858155dac46878a74 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sun, 2 Aug 2020 14:34:09 +0200
Subject: [PATCH 1/4] Added error.py file for error handling functions
---
nw/error.py | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 89 insertions(+)
create mode 100644 nw/error.py
diff --git a/nw/error.py b/nw/error.py
new file mode 100644
index 00000000..dd0cb109
--- /dev/null
+++ b/nw/error.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+"""novelWriter Init
+
+ novelWriter – Exception Handling
+==================================
+ Error handling functions
+
+ File History:
+ Created: 2020-08-02 [0.10.2]
+
+ This file is a part of novelWriter
+ Copyright 2020, Veronica Berglyd Olsen
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+"""
+
+def formatHtmlErrMsg(exType, exValue, exTrace):
+ """Generates a HTML version of an exception.
+ """
+ try:
+ from traceback import format_tb
+ from nw import __issuesurl__
+
+ fmtTrace = ""
+ for trEntry in format_tb(exTrace):
+ for trLine in trEntry.split("\n"):
+ stripLine = trLine.lstrip(" ")
+ nIndent = len(trLine) - len(stripLine)
+ fmtTrace += " "*nIndent + stripLine + "
"
+
+ theMessage = (
+ "
Please report this error by submitting an issue report on "
+ "GitHub, providing a description and this error message.
"
+ "Issue Tracker
%s
"
+ "Error Type
%s: %s
"
+ "Traceback
%s
"
+ ) % (__issuesurl__, exType.__name__, str(exValue), fmtTrace)
+
+ return theMessage
+
+ except Exception as e:
+ return "Could not generate error message.
%s" % str(e)
+
+ return "Could not generate error message."
+
+
+def exceptionHandler(exType, exValue, exTrace):
+ """Function to catch unhandled global exceptions.
+ """
+ import logging
+ from traceback import print_tb, format_tb
+ from nw import __issuesurl__
+ from PyQt5.QtWidgets import qApp, QApplication, QErrorMessage, QMessageBox
+
+ logger = logging.getLogger(__name__)
+ logger.error("%s: %s" % (exType.__name__, str(exValue)))
+ print_tb(exTrace)
+
+ try:
+ nwGUI = None
+ for qWin in qApp.topLevelWidgets():
+ if qWin.objectName() == "GuiMain":
+ nwGUI = qWin
+ break
+
+ if nwGUI is None:
+ logger.warning("Could not find main GUI window so cannot open error dialog")
+ return
+
+ errMsg = QErrorMessage(nwGUI)
+ errMsg.setWindowTitle("Unhandled Error")
+ errMsg.resize(800, 400)
+ errMsg.showMessage((
+ "An unhandled error has been encountered
%s"
+ ) % formatHtmlErrMsg(exType, exValue, exTrace))
+
+ except Exception as e:
+ logger.error(str(e))
From 95a5ee7b8aadb3c0833854e248690536a2f21e6f Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sun, 2 Aug 2020 14:34:44 +0200
Subject: [PATCH 2/4] Set up global error handler and modified error handling
for initial GUI build
---
nw/__init__.py | 58 ++++++++++++++++++++++++++++++--------------------
nw/guimain.py | 1 +
2 files changed, 36 insertions(+), 23 deletions(-)
diff --git a/nw/__init__.py b/nw/__init__.py
index 2271157a..5ffae592 100644
--- a/nw/__init__.py
+++ b/nw/__init__.py
@@ -34,6 +34,7 @@ from os import path, remove, rename
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QErrorMessage
+from nw.error import exceptionHandler
from nw.config import Config
__package__ = "nw"
@@ -249,11 +250,12 @@ def main(sysArgs=None):
if errorData:
errApp = QApplication([])
errMsg = QErrorMessage()
- errMsg.setMinimumWidth(500)
- errMsg.setMinimumHeight(300)
+ errMsg.resize(500, 300)
errMsg.showMessage((
- "ERROR: novelWriter cannot start due to the following issues:
"
- " - %s
Exiting."
+ "A critical error has been encountered
"
+ "novelWriter cannot start due to the following issues:
"
+ "
- %s
"
+ "Shutting down ...
"
) % (
"
- ".join(errorData)
))
@@ -276,34 +278,44 @@ def main(sysArgs=None):
nwApp.setWindowIcon(QIcon(CONFIG.appIcon))
nwApp.setOrganizationDomain(__domain__)
+ # We try to catch critical errors while setting up the main GUI
+ # by wrapping the main GUI in a try/except structure. This will
+ # not catch all exceptions for other parts of the application.
+ # For all other unhandled exceptions, we use a custom exception
+ # handler that pops a dialog box with the error message.
+ sys.excepthook = exceptionHandler
+
try:
nwGUI = GuiMain()
sys.exit(nwApp.exec_())
except Exception:
- # novelWriter has crashed!
- from traceback import print_tb, format_tb
- eInfo = sys.exc_info()
- logger.critical("%s: %s" % (eInfo[0].__name__, eInfo[1]))
- print_tb(eInfo[2])
+ from traceback import print_tb
+ from nw.error import formatHtmlErrMsg
- del nwApp
+ exType, exValue, exTrace = sys.exc_info()
- errApp = QApplication([])
- errMsg = QErrorMessage()
- errMsg.setWindowTitle("Critical Error")
- errMsg.setMinimumWidth(500)
- errMsg.setMinimumHeight(300)
- errMsg.showMessage((
- "novelWriter has encountered a critical error!
"
- "%s:
%s
"
- "Traceback:
%s
"
- "Shutting down ...
"
- ) % (eInfo[0].__name__, eInfo[1], "
".join(format_tb(eInfo[2]))))
- errApp.exec_()
+ logger.critical("%s: %s" % (exType.__name__, str(exValue)))
+ print_tb(exTrace)
- del eInfo
+ try:
+ del nwApp
+
+ errApp = QApplication([])
+ errMsg = QErrorMessage()
+ errMsg.setWindowTitle("Critical Error")
+ errMsg.resize(800, 400)
+ errMsg.showMessage((
+ "A critical error has been encountered
"
+ "%s"
+ "Shutting down ...
"
+ ) % formatHtmlErrMsg(exType, exValue, exTrace))
+ errApp.exec_()
+
+ except Exception as e:
+ logger.critical("Could not create error message dialog.")
+ logger.critical(str(e))
sys.exit(1)
diff --git a/nw/guimain.py b/nw/guimain.py
index 6ea48c9b..12655f20 100644
--- a/nw/guimain.py
+++ b/nw/guimain.py
@@ -56,6 +56,7 @@ class GuiMain(QMainWindow):
QMainWindow.__init__(self)
logger.debug("Initialising GUI ...")
+ self.setObjectName("GuiMain")
self.mainConf = nw.CONFIG
# Some runtime info useful for debugging
From 46c9f0fb5f5c435c59ca63d87534a9b87ce20f3f Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sun, 2 Aug 2020 15:03:17 +0200
Subject: [PATCH 3/4] Added more infor to the error dialog
---
nw/error.py | 29 +++++++++++++++++++++++------
1 file changed, 23 insertions(+), 6 deletions(-)
diff --git a/nw/error.py b/nw/error.py
index dd0cb109..cb276752 100644
--- a/nw/error.py
+++ b/nw/error.py
@@ -29,8 +29,11 @@ def formatHtmlErrMsg(exType, exValue, exTrace):
"""Generates a HTML version of an exception.
"""
try:
+ import sys
from traceback import format_tb
- from nw import __issuesurl__
+ from nw import __issuesurl__, __version__
+ from PyQt5.Qt import PYQT_VERSION_STR
+ from PyQt5.QtCore import QT_VERSION_STR, QSysInfo
fmtTrace = ""
for trEntry in format_tb(exTrace):
@@ -41,11 +44,25 @@ def formatHtmlErrMsg(exType, exValue, exTrace):
theMessage = (
"Please report this error by submitting an issue report on "
- "GitHub, providing a description and this error message.
"
- "Issue Tracker
%s
"
- "Error Type
%s: %s
"
- "Traceback
%s
"
- ) % (__issuesurl__, exType.__name__, str(exValue), fmtTrace)
+ "GitHub, providing a description and this error message. "
+ "URL: <{issueUrl}>."
+ "Environment
Version: {nwVersion}, OS: {osType} ({osKernel}),"
+ "Python: {pyVersion} ({pyHexVer:#x}), Qt: {qtVers}, PyQt: {pyqtVers}
"
+ "Error Type
{exType}: {exMessage}
"
+ "Traceback
{exTrace}
"
+ ).format(
+ nwVersion = __version__,
+ osType = sys.platform,
+ osKernel = QSysInfo.kernelVersion(),
+ pyVersion = sys.version.split()[0],
+ pyHexVer = sys.hexversion,
+ qtVers = QT_VERSION_STR,
+ pyqtVers = PYQT_VERSION_STR,
+ issueUrl = __issuesurl__,
+ exType = exType.__name__,
+ exMessage = str(exValue),
+ exTrace = fmtTrace
+ )
return theMessage
From d3ab1caf3a3f5a6206b50e944e478cededa91bb7 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sun, 2 Aug 2020 15:14:46 +0200
Subject: [PATCH 4/4] Block the error handler dialog in test mode
---
nw/error.py | 7 ++++++-
nw/gui/build.py | 1 -
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/nw/error.py b/nw/error.py
index cb276752..0301f3ba 100644
--- a/nw/error.py
+++ b/nw/error.py
@@ -77,13 +77,16 @@ def exceptionHandler(exType, exValue, exTrace):
"""
import logging
from traceback import print_tb, format_tb
- from nw import __issuesurl__
+ from nw import CONFIG
from PyQt5.QtWidgets import qApp, QApplication, QErrorMessage, QMessageBox
logger = logging.getLogger(__name__)
logger.error("%s: %s" % (exType.__name__, str(exValue)))
print_tb(exTrace)
+ if not CONFIG.showGUI:
+ return
+
try:
nwGUI = None
for qWin in qApp.topLevelWidgets():
@@ -104,3 +107,5 @@ def exceptionHandler(exType, exValue, exTrace):
except Exception as e:
logger.error(str(e))
+
+ return
diff --git a/nw/gui/build.py b/nw/gui/build.py
index 34a01f05..93aa8271 100644
--- a/nw/gui/build.py
+++ b/nw/gui/build.py
@@ -432,7 +432,6 @@ class GuiBuildNovel(QDialog):
def _buildPreview(self):
"""Build a preview of the project in the document viewer.
"""
-
# Get Settings
fmtTitle = self.fmtTitle.text().strip()
fmtChapter = self.fmtChapter.text().strip()