Finish pulling all the cruft out of pyfa.py relating to logging and error handling. All that is now done in separate areas.

Also finally did some major reworking on the error dialog. Now it doesn't spawn a new wxApp and wxFrame for each and every error - attaches to MainFrame and sticks around, having exceptions append to it rather than spawn a new one. In the case that an error happens before MainFrame is available, it spins up a new wxApp. Yay cleanup!
This commit is contained in:
blitzmann
2017-11-26 03:53:38 -05:00
parent 1da127c898
commit c000b19986
4 changed files with 179 additions and 293 deletions

234
pyfa.py
View File

@@ -24,19 +24,8 @@ import sys
import traceback
from optparse import AmbiguousOptionError, BadOptionError, OptionParser
from service.prereqsCheck import PreCheckException, PreCheckMessage, version_precheck, version_block
from logbook import CRITICAL, DEBUG, ERROR, FingersCrossedHandler, INFO, Logger, NestedSetup, NullHandler, StreamHandler, TimedRotatingFileHandler, WARNING, \
__version__ as logbook_version
import config
try:
import sqlalchemy
except ImportError:
sqlalchemy = None
pyfalog = Logger(__name__)
ascii_text = '''
++++++++++++++++++++++++++++++++++++++++++++++++++
__
@@ -67,104 +56,9 @@ class PassThroughOptionParser(OptionParser):
try:
OptionParser._process_args(self, largs, rargs, values)
except (BadOptionError, AmbiguousOptionError) as e:
pyfalog.error("Bad startup option passed.")
# pyfalog.error("Bad startup option passed.")
largs.append(e.opt_str)
#
# class LoggerWriter(object):
# def __init__(self, level):
# # self.level is really like using log.debug(message)
# # at least in my case
# self.level = level
#
# def write(self, message):
# # if statement reduces the amount of newlines that are
# # printed to the logger
# if message not in {'\n', ' '}:
# self.level(message.replace("\n", ""))
#
# def flush(self):
# # create a flush method so things can be flushed when
# # the system wants to. Not sure if simply 'printing'
# # sys.stderr is the correct way to do it, but it seemed
# # to work properly for me.
# self.level(sys.stderr)
#
#
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)
try:
# Try and output to our log handler
with logging_setup.threadbound():
module_list = list(set(sys.modules) & set(globals()))
if module_list:
pyfalog.info("Imported Python Modules:")
for imported_module in module_list:
module_details = sys.modules[imported_module]
pyfalog.info("{0}: {1}", imported_module, getattr(module_details, '__version__', ''))
pyfalog.critical("Exception in main thread: {0}", str(exc_value))
# Print the base level traceback
traceback.print_tb(exc_traceback)
if wx and ErrorFrame:
pyfa_gui = wx.App(False)
if exc_type == PreCheckException:
msgbox = wx.MessageBox(str(exc_value), 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP)
msgbox.ShowModal()
else:
ErrorFrame(exc_value, tb)
pyfa_gui.MainLoop()
pyfalog.info("Exiting.")
except Exception as ex:
# Most likely logging isn't available. Try and output to the console
module_list = list(set(sys.modules) & set(globals()))
if module_list:
pyfalog.info("Imported Python Modules:")
for imported_module in module_list:
module_details = sys.modules[imported_module]
print((str(imported_module) + ": " + str(getattr(module_details, '__version__', ''))))
print(("Exception in main thread: " + str(exc_value)))
traceback.print_tb(exc_traceback)
if wx and ErrorFrame:
pyfa_gui = wx.App(False)
if exc_type == PreCheckException:
wx.MessageBox(str(exc_value), 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP)
else:
ErrorFrame(exc_value, tb)
pyfa_gui.MainLoop()
print("Exiting.")
finally:
# TODO: Add cleanup when exiting here.
pass
# sys.exit()
from gui.errorDialog import ErrorFrameHandler
# Replace the uncaught exception handler with our own handler.
sys.excepthook = ErrorFrameHandler.HandleException
# Parse command line options
usage = "usage: %prog [--root]"
parser = PassThroughOptionParser(usage=usage)
@@ -175,31 +69,27 @@ parser.add_option("-s", "--savepath", action="store", dest="savepath", help="Set
parser.add_option("-l", "--logginglevel", action="store", dest="logginglevel", help="Set desired logging level [Critical|Error|Warning|Info|Debug]", default="Error")
(options, args) = parser.parse_args()
#
# if options.logginglevel == "Critical":
# options.logginglevel = CRITICAL
# elif options.logginglevel == "Error":
# options.logginglevel = ERROR
# elif options.logginglevel == "Warning":
# options.logginglevel = WARNING
# elif options.logginglevel == "Info":
# options.logginglevel = INFO
# elif options.logginglevel == "Debug":
# options.logginglevel = DEBUG
# else:
# options.logginglevel = ERROR
if __name__ == "__main__":
# Configure paths
print ('starting')
try:
# first and foremost - check required libraries
version_precheck()
except PreCheckException as ex:
# do not pass GO, go directly to jail (and then die =/)
PreCheckMessage(str(ex))
sys.exit()
# from here, we can assume we have the libraries that we need, including wx
import wx
from logbook import Logger
pyfalog = Logger(__name__)
from gui.errorDialog import ErrorHandler
# Replace the uncaught exception handler with our own handler.
sys.excepthook = ErrorHandler.HandleException
if options.rootsavedata is True:
config.saveInRoot = True
@@ -208,108 +98,25 @@ if __name__ == "__main__":
options.title = "pyfa %s%s - Python Fitting Assistant" % (config.version, "" if config.tag.lower() != 'git' else " (git)")
config.debug = options.debug
config.loggingLevel = config.LOGLEVEL_MAP.get(options.logginglevel.lower(), config.LOGLEVEL_MAP['error'])
config.defPaths(options.savepath)
config.defLogging()
# Basic logging initialization
with config.logging_setup.threadbound():
# Logging levels:
'''
logbook.CRITICAL
logbook.ERROR
logbook.WARNING
logbook.INFO
logbook.DEBUG
logbook.NOTSET
'''
if options.debug:
savePath_filename = "Pyfa_debug.log"
else:
savePath_filename = "Pyfa.log"
config.logPath = os.path.join(config.savePath, savePath_filename)
try:
if options.debug:
logging_mode = "Debug"
logging_setup = NestedSetup([
# make sure we never bubble up to the stderr handler
# if we run out of setup handling
NullHandler(),
StreamHandler(
sys.stdout,
bubble=False,
level=options.logginglevel
),
TimedRotatingFileHandler(
config.logPath,
level=0,
backup_count=3,
bubble=True,
date_format='%Y-%m-%d',
),
])
else:
logging_mode = "User"
logging_setup = NestedSetup([
# make sure we never bubble up to the stderr handler
# if we run out of setup handling
NullHandler(),
FingersCrossedHandler(
TimedRotatingFileHandler(
config.logPath,
level=0,
backup_count=3,
bubble=False,
date_format='%Y-%m-%d',
),
action_level=ERROR,
buffer_size=1000,
# pull_information=True,
# reset=False,
)
])
except:
print("Critical error attempting to setup logging. Falling back to console only.")
logging_mode = "Console Only"
logging_setup = NestedSetup([
# make sure we never bubble up to the stderr handler
# if we run out of setup handling
NullHandler(),
StreamHandler(
sys.stdout,
bubble=False
)
])
with logging_setup.threadbound():
pyfalog.info("Starting Pyfa")
pyfalog.info(version_block)
pyfalog.info("Logbook version: {0}", logbook_version)
pyfalog.info("Running in logging mode: {0}", logging_mode)
# move this to the log set up - if it fails, can't say that we're writing it
pyfalog.info("Writing log file to: {0}", config.logPath)
# Output all stdout (print) messages as warnings
# try:
# sys.stdout = LoggerWriter(pyfalog.warning)
# 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:
# pyfalog.critical("Cannot redirect. Continuing without writing stderr to log.")
if hasattr(sys, 'frozen'):
pyfalog.info("Running in a frozen state.")
else:
pyfalog.info("Running in a thawed state.")
# Lets get to the good stuff, shall we?
import eos.db
import eos.events # todo: move this to eos initialization
import eos.events # todo: move this to eos initialization?
# noinspection PyUnresolvedReferences
import service.prefetch # noqa: F401
@@ -323,7 +130,8 @@ if __name__ == "__main__":
from gui.mainFrame import MainFrame
pyfa = wx.App(False)
MainFrame(options.title)
mf = MainFrame(options.title)
ErrorHandler.SetParent(mf)
pyfa.MainLoop()
# TODO: Add some thread cleanup code here. Right now we bail, and that can lead to orphaned threads or threads not properly exiting.