Merge pull request #378 from vkbo/exceptions

Exception Handling
This commit is contained in:
Veronica K. Berglyd Olsen
2020-08-02 19:17:20 +02:00
committed by GitHub
4 changed files with 147 additions and 24 deletions
+35 -23
View File
@@ -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:<br><br>"
"&nbsp;-&nbsp;%s<br><br>Exiting."
"<h3>A critical error has been encountered</h3>"
"<p>novelWriter cannot start due to the following issues:<p>"
"<p>&nbsp;-&nbsp;%s</p>"
"<p>Shutting down ...</p>"
) % (
"<br>&nbsp;-&nbsp;".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((
"<h3>novelWriter has encountered a critical error!</h3>"
"<p><b>%s:</b><br>%s</p>"
"<p><b>Traceback:</b><br>%s</p>"
"<p>Shutting down ...</p>"
) % (eInfo[0].__name__, eInfo[1], "<br>".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((
"<h3>A critical error has been encountered</h3>"
"%s"
"<p>Shutting down ...</p>"
) % 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)
+111
View File
@@ -0,0 +1,111 @@
# -*- 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 <https://www.gnu.org/licenses/>.
"""
def formatHtmlErrMsg(exType, exValue, exTrace):
"""Generates a HTML version of an exception.
"""
try:
import sys
from traceback import format_tb
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):
for trLine in trEntry.split("\n"):
stripLine = trLine.lstrip(" ")
nIndent = len(trLine) - len(stripLine)
fmtTrace += "&nbsp;"*nIndent + stripLine + "<br>"
theMessage = (
"<p>Please report this error by submitting an issue report on "
"GitHub, providing a description and this error message. "
"URL: &lt;{issueUrl}&gt;.</p>"
"<p><b>Environment</b><br>Version: {nwVersion}, OS: {osType} ({osKernel}),"
"Python: {pyVersion} ({pyHexVer:#x}), Qt: {qtVers}, PyQt: {pyqtVers}</p>"
"<p><b>Error Type</b><br>{exType}: {exMessage}</p>"
"<p><b>Traceback</b><br>{exTrace}</p>"
).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
except Exception as e:
return "Could not generate error message.<br>%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 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():
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((
"<h3>An unhandled error has been encountered</h3>%s"
) % formatHtmlErrMsg(exType, exValue, exTrace))
except Exception as e:
logger.error(str(e))
return
-1
View File
@@ -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()
+1
View File
@@ -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