8
votes

Python does not print traceback messages from exceptions raised in daemon threads.

For example, this code creates a daemonic thread and raises an exception in the new thread:

def error_raiser():
    raise Exception

import threading
thread = threading.Thread(target=error_raiser)
thread.daemon = True
thread.start()

but does not print a traceback. (It gives no output).

However, if the thread is not set as a daemon thread, Python will print the traceback. Here is the same code with one line commented out:

def error_raiser():
    raise Exception

import threading
thread = threading.Thread(target=error_raiser)
# thread.daemon = True
thread.start()

and the output:

Exception in Thread-1:
Traceback (most recent call last):
  File "C:\Python26\lib\threading.py", line 525, in __bootstrap_inner
    self.run()
  File "C:\Python26\lib\threading.py", line 477, in run
    self.__target(*self.__args, **self.__kwargs)
  File "test.py", line 2, in error_raiser
    raise Exception
Exception

Executing this code in both Python 2.6.2 and Python 3.0.1 and gives the same results. Interestingly, however, if I execute the code by importing it in the IPython shell, the exception is displayed whether the thread is daemonic or not.

According to the documentation, the only significance of the 'daemon' flag is that "the entire Python program exits when only daemon threads are left." This would make me believe that not printing a traceback after an exception is a bug in Python, unless I missed something in the documentation.

Is this a bug, or did I miss something in the documentation and this behavior is intentional? If it is intentional, how can I force Python to print the traceback in daemon threads without using IPython?

2

2 Answers

7
votes

According to Wikipedia, by definition a daemon should detach itself from the controlling tty, so I think it's correct that no exception is shown (and after all, a daemon should keep working even if you close the shell that launched it)..
See here.

As for how to print the traceback, I think that a simple try/except_then_log_to_file would do the trick :)

2
votes

Is this a bug, or did I miss something in the documentation and this behavior is intentional?

You have basically stated the reason yourself, without realising it:

According to the documentation, the only significance of the 'daemon' flag is that "the entire Python program exits when only daemon threads are left."

If you have a non-daemonic thread, Python waits for it after thread.start(). This waiting includes anything it does, including raising and handling exceptions.

If you have a daemonic thread, Python does not wait for it after thread.start(). Instead, in the absence of further instructions Python exits immediately. That means your thread never has a chance to raise or handle the exception.


If it is intentional, how can I force Python to print the traceback in daemon threads without using IPython?

For a daemon thread, it is inconsequential what your thread is supposed to do. If it was instructed to print something, the same would have happened.

That also means you have no way to conditionally wait for an action of the daemon. Either you set thread.daemon = False and get all tracebacks - as well as all prints, I/O, and other actions. Or you set thread.daemon = True and get no traceback - and no prints, I/O or other actions - after all other threads are dead.


Interestingly, however, if I execute the code by importing it in the IPython shell, the exception is displayed whether the thread is daemonic or not.

The thing about shells is that they never exit unless you kill them. Since the shell's interpreter does not exit while it waits for your input, any daemon threads started remain alive.