pyqtgraph/pyqtgraph/exceptionHandling.py
Luke Campagnola 753ac9b4c4 Squashed commit of the following:
commit ca3fbe2ff9
Author: Luke Campagnola <luke.campagnola@gmail.com>
Date:   Thu Aug 7 08:41:30 2014 -0400

    Merged numerous updates from acq4:
    * Added HDF5 exporter
    * CSV exporter gets (x,y,y,y) export mode
    * Updates to SVG, Matplotlib exporter
    * Console can filter exceptions by string
    * Added tick context menu to GradientEditorItem
    * Added export feature to imageview
    * Parameter trees:
        - Option to save only user-editable values
        - Option to set visible title of parameters separately from name
        - Added experimental ParameterSystem for handling large systems of
            interdependent parameters
        - Auto-select editable portion of spinbox when editing
    * Added Vector.__abs__
    * Added replacement garbage collector for avoiding crashes on multithreaded Qt
    * Fixed "illegal instruction" caused by closing file handle 7 on OSX
    * configfile now reloads QtCore objects, Point, ColorMap, numpy arrays
    * Avoid triggering recursion issues in exception handler
    * Various bugfies and performance enhancements
2014-08-07 09:03:26 -04:00

107 lines
4.1 KiB
Python

# -*- coding: utf-8 -*-
"""This module installs a wrapper around sys.excepthook which allows multiple
new exception handlers to be registered.
Optionally, the wrapper also stops exceptions from causing long-term storage
of local stack frames. This has two major effects:
- Unhandled exceptions will no longer cause memory leaks
(If an exception occurs while a lot of data is present on the stack,
such as when loading large files, the data would ordinarily be kept
until the next exception occurs. We would rather release this memory
as soon as possible.)
- Some debuggers may have a hard time handling uncaught exceptions
The module also provides a callback mechanism allowing others to respond
to exceptions.
"""
import sys, time
#from lib.Manager import logMsg
import traceback
#from log import *
#logging = False
callbacks = []
clear_tracebacks = False
def register(fn):
"""
Register a callable to be invoked when there is an unhandled exception.
The callback will be passed the output of sys.exc_info(): (exception type, exception, traceback)
Multiple callbacks will be invoked in the order they were registered.
"""
callbacks.append(fn)
def unregister(fn):
"""Unregister a previously registered callback."""
callbacks.remove(fn)
def setTracebackClearing(clear=True):
"""
Enable or disable traceback clearing.
By default, clearing is disabled and Python will indefinitely store unhandled exception stack traces.
This function is provided since Python's default behavior can cause unexpected retention of
large memory-consuming objects.
"""
global clear_tracebacks
clear_tracebacks = clear
class ExceptionHandler(object):
def __call__(self, *args):
## Start by extending recursion depth just a bit.
## If the error we are catching is due to recursion, we don't want to generate another one here.
recursionLimit = sys.getrecursionlimit()
try:
sys.setrecursionlimit(recursionLimit+100)
## call original exception handler first (prints exception)
global original_excepthook, callbacks, clear_tracebacks
try:
print("===== %s =====" % str(time.strftime("%Y.%m.%d %H:%m:%S", time.localtime(time.time()))))
except Exception:
sys.stderr.write("Warning: stdout is broken! Falling back to stderr.\n")
sys.stdout = sys.stderr
ret = original_excepthook(*args)
for cb in callbacks:
try:
cb(*args)
except Exception:
print(" --------------------------------------------------------------")
print(" Error occurred during exception callback %s" % str(cb))
print(" --------------------------------------------------------------")
traceback.print_exception(*sys.exc_info())
## Clear long-term storage of last traceback to prevent memory-hogging.
## (If an exception occurs while a lot of data is present on the stack,
## such as when loading large files, the data would ordinarily be kept
## until the next exception occurs. We would rather release this memory
## as soon as possible.)
if clear_tracebacks is True:
sys.last_traceback = None
finally:
sys.setrecursionlimit(recursionLimit)
def implements(self, interface=None):
## this just makes it easy for us to detect whether an ExceptionHook is already installed.
if interface is None:
return ['ExceptionHandler']
else:
return interface == 'ExceptionHandler'
## replace built-in excepthook only if this has not already been done
if not (hasattr(sys.excepthook, 'implements') and sys.excepthook.implements('ExceptionHandler')):
original_excepthook = sys.excepthook
sys.excepthook = ExceptionHandler()