classification
Title: sys.excepthook doesn't work in threads
Type: Stage: test needed
Components: Interpreter Core Versions: Python 3.2, Python 2.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: ellisj, eric.araujo, mwh, nikratio, tiagoaoa, undercoveridiot
Priority: normal Keywords:

Created on 2005-06-30 19:06 by ellisj, last changed 2011-09-11 18:55 by nikratio.

Messages (6)
msg25694 - (view) Author: Jonathan Ellis (ellisj) Date: 2005-06-30 19:06
simple script to reproduce:

import sys, threading

def log_exception(*args):
    print 'got exception %s' % (args,)
sys.excepthook = log_exception

def foo():
    a = 1 / 0
threading.Thread(target=foo).start()

Note that a normal traceback is printed instead of "got
exception."
msg25695 - (view) Author: Michael Hudson (mwh) (Python committer) Date: 2007-06-06 11:11
I've just run into this, and it's very annoying.  The stupid part is that threads started with the thread module don't have this problem, it's just a problem with threading.Thread()s trying to be too clever.

I would vote for deleting all the code to do with exception printing in threading.py and letting the C machinery take care of it.
msg25696 - (view) Author: Jonathan Ellis (ellisj) Date: 2007-06-15 02:04
Here is a workaround in the meantime:

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.
    If using psyco, call psycho.cannotcompile(threading.Thread.run)
    since this replaces a new-style class method.
    """
    import sys
    run_old = threading.Thread.run
    def run(*args, **kwargs):
        try:
            run_old(*args, **kwargs)
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            sys.excepthook(*sys.exc_info())
    threading.Thread.run = run
msg62933 - (view) Author: Tiago Alves (tiagoaoa) Date: 2008-02-24 20:40
I don't see it as a problem or as the threading module trying to be
"clever". It clearly was a design choice to make the module have its own
exception treatment in order to make it clear in which thread the
exception occurred.
IMHO the decision here should be to implement per thread excepthook's.
msg91243 - (view) Author: Ian Beaver (undercoveridiot) Date: 2009-08-03 21:59
I found that the workaround suggested doesn't work when you have a
subclass of threading.Thread and you want to catch everything in the
module that contains the class to a common log.

Say you have a module with a socket server that spawns a thread on
accept and you want to log anything that tracebacks in the module. This
seems to work:

import sys
import logging
from functools import wraps

def myExceptHook(type, value, tb):
    """ Redirect tracebacks to error log """
    import traceback
    rawreport = traceback.format_exception(type, value, tb)
    report = '\n'.join(rawreport)
    log.error(report)

sys.excepthook = myExceptHook
    
def use_my_excepthook(view):
    """ Redirect any unexpected tracebacks """ 
    @wraps(view)
    def run(*args, **kwargs):
        try:
            return view(*args, **kwargs)
        except:
            sys.excepthook(*sys.exc_info())
    return run


Then in your thread subclass:


class MyThread(threading.Thread):
    def __init__(self, socket_conn):
        threading.Thread.__init__(self)
        self.my_conn = socket_conn

    @use_my_excepthook
    def run(self):
        """ Do stuff """
msg91244 - (view) Author: Ian Beaver (undercoveridiot) Date: 2009-08-03 22:24
Instead of using decorators, this is a slightly simpler modification to
the proposed workaround that allows for any subclassed run method to
also be caught.


def installThreadExcepthook():
    """
    Workaround for sys.excepthook thread bug
    From
http://spyced.blogspot.com/2007/06/workaround-for-sysexcepthook-bug.html
   
(https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1230540&group_id=5470).
    Call once from __main__ before creating any threads.
    If using psyco, call psyco.cannotcompile(threading.Thread.run)
    since this replaces a new-style class method.
    """
    init_old = threading.Thread.__init__
    def init(self, *args, **kwargs):
        init_old(self, *args, **kwargs)
        run_old = self.run
        def run_with_except_hook(*args, **kw):
            try:
                run_old(*args, **kw)
            except (KeyboardInterrupt, SystemExit):
                raise
            except:
                sys.excepthook(*sys.exc_info())
        self.run = run_with_except_hook
    threading.Thread.__init__ = init
History
Date User Action Args
2011-09-11 18:55:28nikratiosetnosy: + nikratio
2010-08-04 04:44:18terry.reedysetstage: test needed
versions: + Python 2.7, Python 3.2, - Python 2.6, Python 2.5
2010-02-23 21:22:52eric.araujosetnosy: + eric.araujo
2009-08-03 22:24:03undercoveridiotsetmessages: + msg91244
2009-08-03 21:59:32undercoveridiotsetnosy: + undercoveridiot
messages: + msg91243
2008-02-24 20:40:08tiagoaoasetnosy: + tiagoaoa
messages: + msg62933
2007-11-23 09:20:55christian.heimessetversions: + Python 2.6, Python 2.5, - Python 2.4
2005-06-30 19:06:10ellisjcreate