#####################################################################
# #
# __init__.py #
# #
# Copyright 2013, Monash University #
# #
# This file is part of the labscript suite (see #
# http://labscriptsuite.org) and is licensed under the Simplified #
# BSD License. See the license.txt file in the root of the project #
# for the full license. #
# #
#####################################################################
import sys
import os
import threading
import traceback
import subprocess
import warnings
# The maximum number of windows the excepthook will spawn:
MAX_WINDOWS = 10
subprocess_script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tk_exception.py')
child_processes = []
[docs]def install_thread_excepthook():
"""
Workaround for sys.excepthook thread bug
(https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1230540&group_id=5470).
Call once from __main__ before creating any threads.
"""
run_old = threading.Thread.run
def run(*args, **kwargs):
try:
run_old(*args, **kwargs)
except (KeyboardInterrupt, SystemExit):
raise
except:
if sys is None:
# Interpreter is shutting down. Don't display graphical error.
# Let the threading module's code handle this however it normally does.
raise
exc_type, exc_value, exc_traceback = sys.exc_info()
# Cull the top frame so the user doesn't see this wrapping code in their traceback:
exc_traceback = exc_traceback.tb_next
sys.excepthook(exc_type, exc_value, exc_traceback)
threading.Thread.run = run
[docs]def tkhandler(exceptclass, exception, exec_info, reraise=True):
script = os.path.basename(sys.argv[0])
if not script:
script = 'python interactive shell'
shortmessage = '%s: %s' % (exceptclass.__name__, exception)
longmessage = ''.join(traceback.format_exception(exceptclass, exception, exec_info))
if l.logger:
l.logger.error('Got an exception:\n%s' % longmessage)
if exceptclass in [KeyboardInterrupt, SystemExit]:
sys.__excepthook__(exceptclass, exception, exec_info)
else:
for previous_process in child_processes[:]:
if previous_process.poll() is not None:
child_processes.remove(previous_process)
if len(child_processes) >= MAX_WINDOWS:
shortmessage = "Too many errors"
longmessage = ("Too many errors: Further errors will " +
"not be shown graphically until some error windows are closed")
if len(child_processes) < MAX_WINDOWS + 1:
process = subprocess.Popen([sys.executable, subprocess_script_path, script, shortmessage, longmessage])
child_processes.append(process)
if reraise:
sys.__excepthook__(exceptclass, exception, exec_info)
[docs]def logwarning(message, category, filename, lineno, file=None, line=None):
logmessage = warnings.formatwarning(message, category, filename, lineno, line)
l.logger.warn(logmessage)
warnings._showwarning(message, category, filename, lineno, file, line)
[docs]def set_logger(logger):
l.logger = logger
warnings._showwarning = warnings.showwarning
warnings.showwarning = logwarning
# Check for tkinter availability. Tkinter is frustratingly not available
# by default for python 3.x on Debian systems, despite being considered
# part of the Python standard library. I'll make it a dependency for
# packaging, but this is an extra check at runtime so that if something
# goes wrong with that we get an error at import rather than later:
import tkinter
sys.excepthook = tkhandler
install_thread_excepthook()