views:

227

answers:

3

Hi Everyone,

I'm very new to Python and multithreaded programming in general. Basically, I have a script that will copy files to another location. I would like this to be placed in another thread so I can output .... to indicate that the script is still running.

The problem that I am having is that if the files cannot be copied it will throw an exception. This is ok if running in the main thread; however, having the following code does not work:

try:
    threadClass = TheThread(param1, param2, etc.)
    threadClass.start()   ##### **Exception takes place here**
except:
    print "Caught an exception"

In the thread class itself, I tried to re-throw the exception, but it does not work. I have seen people on here ask similar questions, but they all seem to be doing something more specific than what I am trying to do (and I don't quite understand the solutions offered). I have seen people mention the usage of sys.exc_info(), however I do not know where or how to use it.

All help is greatly appreciated!

EDIT: The code for the thread class is below:

class TheThread(threading.Thread):
    def __init__(self, sourceFolder, destFolder):
        threading.Thread.__init__(self)
        self.sourceFolder = sourceFolder
        self.destFolder = destFolder

    def run(self):
        try:
           shul.copytree(self.sourceFolder, self.destFolder)
        except:
           raise
A: 

Using naked excepts is not a good practice because you usually catch more than you bargain for.

I would suggest modifying the except to catch ONLY the exception that you would to handle. I don't think that raising it has the desired effect, because when you go to instantiate TheThread in the outer try, if it raises an exception, the assignment is never going to happen.

Instead you might want to just alert on it and move on, such as:

def run(self):
    try:
       shul.copytree(self.sourceFolder, self.destFolder)
    except OSError, err:
       print err

Then when that exception is caught, you can handle it there. Then the outer try catches an exception from TheThread, you know it won't be the one you already handled, and will help you isolate your process flow.

jathanism
Well, if there is an error in that thread at all, I want the full program to inform the user that there was an issue and gracefully end. For that reason, I want the main thread to catch and handle all the exceptions. However, the problem still exists where if TheThread throws an exception, the main thread's try/except still won't catch it. I could have the thread detect the exception and return a false indicating that the operation was unsuccessful. That would achieve the same desired result, but I would still like to know how to properly catch a sub-thread exception.
Phanto
+2  A: 

The problem is that thread_obj.start() returns immediately. The child thread that you spawned executes in its own context, with its own stack. Any exception that occurs there is in the context of the child thread, and it is in its own stack. One way I can think of right now to communicate this information to the parent thread is by using some sort of message passing, so you might look into that.

Try this on for size:

import sys
import threading
import Queue


class ExcThread(threading.Thread):

    def __init__(self, bucket):
        threading.Thread.__init__(self)
        self.bucket = bucket

    def run(self):
        try:
            raise Exception('An error occured here.')
        except Exception:
            self.bucket.put(sys.exc_info())


def main():
    bucket = Queue.Queue()
    thread_obj = ExcThread(bucket)
    thread_obj.start()

    while True:
        try:
            exc = bucket.get(block=False)
        except Queue.Empty:
            pass
        else:
            exc_type, exc_obj, exc_trace = exc
            # deal with the exception
            print exc_type, exc_obj
            print exc_trace

        thread_obj.join(0.1)
        if thread_obj.isAlive():
            continue
        else:
            break


if __name__ == '__main__':
    main()
Santa
This looks like it will work. I will need to try this when I get a chance. For now, I'm marking your post as the answer. Thank you!
Phanto
I just tried this, and it does work. Thank you! :)
Phanto
+3  A: 

You have to think of threads in terms of phone calls.

Consider this.

You call up the local city council and ask a question. While they find the answer for you, you hold. When they have the answer, they'll tell it to you, and then you hang up. If for some reason they can't find the answer (exception), they will tell you that.

This is how a synchronous, normal, method call works. You call a method, when it returns, you have the answer (good or bad.)

However, a thread goes more like this:

You call up the local city council and ask a question, and ask them to call you back when they have the answer. You then hang up.

At this point, you don't know if they will find the answer or not, so any attempt, now, to try to handle the result of the inquiry, will fail, as you simply don't have the results yet.

Instead, you have to react to the incoming call, and take the news, good or bad, and then process it.

In terms of your code, you need to have code that reacts to your thread failing, and logs or processes the exception. The code you have in your question, which you say doesn't work, is just like trying to process the results of the phone call right after hanging up, when you still don't have the answer.

Lasse V. Karlsen
That is perhaps one of the best explanations of multithreading I have ever read.If my reputation were higher, I would definitely uprank your post.
Phanto