From 03f12bfca1840dd869388b7a85525dba49318f44 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Mon, 27 Mar 2017 20:39:47 -0700 Subject: [PATCH] Finish implementing wx 2.8 selector. Add a bunch of error catching. General cleanup. (cherry picked from commit b0ec69e) --- gui/errorDialog.py | 49 +++++++++++++---- pyfa.py | 131 +++++++++++++++++++++++++-------------------- 2 files changed, 113 insertions(+), 67 deletions(-) diff --git a/gui/errorDialog.py b/gui/errorDialog.py index 8a610c8b8..f0a8c3bce 100644 --- a/gui/errorDialog.py +++ b/gui/errorDialog.py @@ -20,9 +20,13 @@ import platform import sys +# noinspection PyPackageRequirements import wx -import config +try: + import config +except: + config = None try: import sqlalchemy @@ -31,16 +35,21 @@ try: except: sqlalchemy_version = "Unknown" +try: + from logbook import __version__ as logbook_version +except: + logbook_version = "Unknown" + class ErrorFrame(wx.Frame): - def __init__(self, exception, tb): + def __init__(self, exception=None, tb=None, error_title='Error!'): v = sys.version_info wx.Frame.__init__(self, None, id=wx.ID_ANY, title="pyfa error", pos=wx.DefaultPosition, size=wx.Size(500, 600), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER | wx.STAY_ON_TOP) - desc = "pyfa has experienced an unexpected error. Below is the Traceback that contains crucial \n" \ - "information about how this error was triggered. Please contact the developers with the \n" \ + desc = "pyfa has experienced an unexpected issue. Below is a message that contains crucial\n" \ + "information about how this was triggered. Please contact the developers with the\n" \ "information provided through the EVE Online forums or file a GitHub issue." self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) @@ -51,7 +60,7 @@ class ErrorFrame(wx.Frame): mainSizer = wx.BoxSizer(wx.VERTICAL) headSizer = wx.BoxSizer(wx.HORIZONTAL) - headingText = wx.StaticText(self, wx.ID_ANY, "Error!", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE) + headingText = wx.StaticText(self, wx.ID_ANY, error_title, wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE) headingText.SetFont(wx.Font(14, 74, 90, 92, False)) headSizer.Add(headingText, 1, wx.ALL, 5) @@ -79,24 +88,46 @@ class ErrorFrame(wx.Frame): errorTextCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL)) mainSizer.Add(errorTextCtrl, 0, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, 5) - errorTextCtrl.AppendText("OS version: \t" + str(platform.platform())) + try: + errorTextCtrl.AppendText("OS version: \t" + str(platform.platform())) + except: + errorTextCtrl.AppendText("OS version: Unknown") errorTextCtrl.AppendText("\n") - errorTextCtrl.AppendText("Python: \t" + '{}.{}.{}'.format(v.major, v.minor, v.micro)) + + try: + errorTextCtrl.AppendText("Python: \t" + '{}.{}.{}'.format(v.major, v.minor, v.micro)) + except: + errorTextCtrl.AppendText("Python: Unknown") errorTextCtrl.AppendText("\n") - errorTextCtrl.AppendText("wxPython: \t" + str(wx.__version__)) + + try: + errorTextCtrl.AppendText("wxPython: \t" + wx.VERSION_STRING) + except: + errorTextCtrl.AppendText("wxPython: Unknown") errorTextCtrl.AppendText("\n") + errorTextCtrl.AppendText("SQLAlchemy: \t" + str(sqlalchemy_version)) errorTextCtrl.AppendText("\n") - errorTextCtrl.AppendText("pyfa version: {0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName, config.expansionVersion)) + + errorTextCtrl.AppendText("Logbook: \t" + str(logbook_version)) + errorTextCtrl.AppendText("\n") + + try: + errorTextCtrl.AppendText("pyfa version: {0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName, config.expansionVersion)) + except: + errorTextCtrl.AppendText("pyfa version: Unknown") errorTextCtrl.AppendText('\n') + errorTextCtrl.AppendText("pyfa root: " + str(config.pyfaPath or "Unknown")) errorTextCtrl.AppendText('\n') errorTextCtrl.AppendText("save path: " + str(config.savePath or "Unknown")) errorTextCtrl.AppendText('\n') errorTextCtrl.AppendText("fs encoding: " + str(sys.getfilesystemencoding() or "Unknown")) errorTextCtrl.AppendText('\n\n') + errorTextCtrl.AppendText("EXCEPTION: " + str(exception or "Unknown")) errorTextCtrl.AppendText('\n\n') + if tb: for line in tb: errorTextCtrl.AppendText(line) diff --git a/pyfa.py b/pyfa.py index af01c1033..1609c8e19 100755 --- a/pyfa.py +++ b/pyfa.py @@ -18,6 +18,7 @@ # along with pyfa. If not, see . # ============================================================================== +import inspect import os import platform import re @@ -30,19 +31,10 @@ from logbook import CRITICAL, DEBUG, ERROR, FingersCrossedHandler, INFO, Logger, import config - -class PreCheckException(Exception): - pass - - try: import wxversion - import wx - from gui.errorDialog import ErrorFrame except ImportError: wxversion = None - wx = None - ErrorFrame = None try: import sqlalchemy @@ -68,7 +60,7 @@ class PassThroughOptionParser(OptionParser): largs.append(e.opt_str) -class LoggerWriter: +class LoggerWriter(object): def __init__(self, level): # self.level is really like using log.debug(message) # at least in my case @@ -88,7 +80,27 @@ class LoggerWriter: self.level(sys.stderr) +class PreCheckException(Exception): + def __init__(self, msg): + try: + ln = sys.exc_info()[-1].tb_lineno + except AttributeError: + ln = inspect.currentframe().f_back.f_lineno + self.message = "{0.__name__} (line {1}): {2}".format(type(self), ln, msg) + self.args = self.message, + + def handleGUIException(exc_type, exc_value, exc_traceback): + try: + # Try and import wx in case it's missing. + # noinspection PyPackageRequirements + import wx + from gui.errorDialog import ErrorFrame + except: + # noinspection PyShadowingNames + wx = None + # noinspection PyShadowingNames + ErrorFrame = None tb = traceback.format_tb(exc_traceback) @@ -100,25 +112,31 @@ def handleGUIException(exc_type, exc_value, exc_traceback): # Print the base level traceback traceback.print_tb(exc_traceback) - pyfa = wx.App(False) - if exc_type == PreCheckException: - msgbox = wx.MessageBox(exc_value.message, 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP) - msgbox.ShowModal() - else: - ErrorFrame(exc_value, tb) - pyfa.MainLoop() + if wx and ErrorFrame: + pyfa_gui = wx.App(False) + if exc_type == PreCheckException: + ErrorFrame(exc_value, None, "Missing Prerequisite") + else: + ErrorFrame(exc_value, tb) + + pyfa_gui.MainLoop() + + pyfalog.info("Exiting.") except: # Most likely logging isn't available. Try and output to the console print("Exception in main thread: " + str(exc_value.message)) traceback.print_tb(exc_traceback) - pyfa = wx.App(False) - if exc_type == PreCheckException: - msgbox = wx.MessageBox(exc_value.message, 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP) - msgbox.ShowModal() - else: - ErrorFrame(exc_value, tb) - pyfa.MainLoop() + if wx and ErrorFrame: + pyfa_gui = wx.App(False) + if exc_type == PreCheckException: + ErrorFrame(exc_value, None, "Missing Prerequisite") + else: + ErrorFrame(exc_value, tb) + + pyfa_gui.MainLoop() + + print("Exiting.") finally: # TODO: Add cleanup when exiting here. @@ -190,7 +208,7 @@ if __name__ == "__main__": else: savePath_filename = "Pyfa.log" - savePath_Destination = os.path.join(config.savePath, savePath_filename) + config.logPath = os.path.join(config.savePath, savePath_filename) try: if options.debug: @@ -205,7 +223,7 @@ if __name__ == "__main__": level=options.logginglevel ), TimedRotatingFileHandler( - savePath_Destination, + config.logPath, level=0, backup_count=3, bubble=True, @@ -220,7 +238,7 @@ if __name__ == "__main__": NullHandler(), FingersCrossedHandler( TimedRotatingFileHandler( - savePath_Destination, + config.logPath, level=0, backup_count=3, bubble=False, @@ -248,26 +266,28 @@ if __name__ == "__main__": with logging_setup.threadbound(): pyfalog.info("Starting Pyfa") + pyfalog.info("Logbook version: {0}", logbook_version) + pyfalog.info("Running in logging mode: {0}", logging_mode) - pyfalog.info("Writing log file to: {0}", savePath_Destination) + pyfalog.info("Writing log file to: {0}", config.logPath) # Output all stdout (print) messages as warnings try: sys.stdout = LoggerWriter(pyfalog.warning) - except ValueError, Exception: + except: pyfalog.critical("Cannot redirect. Continuing without writing stdout to log.") # Output all stderr (stacktrace) messages as critical try: sys.stderr = LoggerWriter(pyfalog.critical) - except ValueError, Exception: + except: pyfalog.critical("Cannot redirect. Continuing without writing stderr to log.") pyfalog.info("OS version: {0}", platform.platform()) pyfalog.info("Python version: {0}", sys.version) if sys.version_info < (2, 7) or sys.version_info > (3, 0): - exit_message = "Pyfa requires python 2.x branch ( >= 2.7 ).\nExiting." + exit_message = "Pyfa requires python 2.x branch ( >= 2.7 )." raise PreCheckException(exit_message) if hasattr(sys, 'frozen'): @@ -275,34 +295,29 @@ if __name__ == "__main__": else: pyfalog.info("Running in a thawed state.") - # FIX THIS - try: - if options.force28 is True: - wxversion.select('2.8') - else: - wxversion.select(['3.0', '2.8']) - - if hasattr(sys, 'frozen'): - pyfalog.info("Running in frozen state. Skipping wx validation.") - pyfalog.debug("wxPython version: {0}.", wxversion.getInstalled()) - elif options.force28 is True: - # Remove existing wxPython - wxversion.select('2.8') - - if hasattr(sys, 'frozen') and wx is not None: - pyfalog.info("Running in frozen state with wx installed. Skipping wx validation.") - pyfalog.debug("wxPython version: {0}.", wxversion.getInstalled()) - elif wx is None or wxversion is None: - exit_message = "\nCannot find wxPython\nYou can download wxPython (2.8+) from http://www.wxpython.org/" - raise PreCheckException(exit_message) + if not hasattr(sys, 'frozen') and wxversion: + try: + if options.force28 is True: + pyfalog.info("Selecting wx version: 2.8. (Forced)") + wxversion.select('2.8') + else: + pyfalog.info("Selecting wx versions: 3.0, 2.8") + wxversion.select(['3.0', '2.8']) + except: + pyfalog.warning("Unable to select wx version. Attempting to import wx without specifying the version.") else: - if options.force28 is True and wxversion.checkInstalled('2.8'): - pyfalog.info("wxPython is installed. Version: {0} (forced).", wxversion.getInstalled()) - elif options.force28 is not True and (wxversion.checkInstalled('2.8') or wxversion.checkInstalled('3.0')): - pyfalog.info("wxPython is installed. Version: {0}.", wxversion.getInstalled()) - else: - pyfalog.warning("\nInstalled wxPython version doesn't meet requirements.\nYou can download wxPython 2.8 or 3.0 from http://www.wxpython.org/") - pyfalog.critical("Attempting to run with unsupported version of wx. Version: {0}", wxversion.getInstalled()) + if not wxversion: + pyfalog.warning("wxVersion not found. Attempting to import wx without specifying the version.") + + try: + # noinspection PyPackageRequirements + import wx + from gui.errorDialog import ErrorFrame + except: + exit_message = "Cannot import wxPython. You can download wxPython (2.8+) from http://www.wxpython.org/" + raise PreCheckException(exit_message) + + pyfalog.info("wxPython version: {0}.", str(wx.VERSION_STRING)) if sqlalchemy is None: exit_message = "\nCannot find sqlalchemy.\nYou can download sqlalchemy (0.6+) from http://www.sqlalchemy.org/"