开发者

How can I call sys.exc_clear() for another part of a Python program?

I have a class (see this previous question if you are interested) which tracks, amongst other things, errors. The class is called in a variety of situations, one of them is during an exception. Even though my class calls sys.exc_clear() as part of the regular course of events, the next time the class is called (even when there is no error, such as when I am just throwing some statistical information in one of the class functions) the sys.exc_info() tuple is still full of the original non-None objects.

I can call sys.exc_clear() and sys.exc_info() is just a bunch of Nones while the thread is executing in that class, but as soon as execution returns to the main program, this ceases to be valid. My reading of the documentation suggests that this is because the execution stack has returned to another frame. This seems to be a situation tangentially mentioned in this question previously.

So, my only option appears to be tacking sys.exc_clear() after each except in my main program. I have tried it in a few places and it works. I can do this, but it seems tedious and ugly. Is another way?

ADDITION:

Imagine the main program as

import tracking
def Important_Function():
    try:
        something that fails
    except:
        myTrack.track(level='warning', technical='Failure in Important_Function' ...)

    return

def Other_Function():
    myTrack.track(level='info', technical='Total=0' ...)

    return

myTrack = tracking.Tracking()
myTrack.track(level='debug', parties=['operator'], technical='Started the program.')
Important_Function()
Other_Function()

Then the Tracking code as:

import sys
import inspect
import traceback

... lots of initialization stuff
    def track(self, level='info', technical=None, parties=None, clear=True ...):

        # What are our errors?
        errors = {}
        errortype, errorvalue, errortraceback = sys.exc_info()
        errortype, errorvalue = sys.exc_info()[:2]
        errors['type'] = None
        errors['class'] = errortype
        errors['value'] = errorvalue
        errors['arguments'] = None
        errors['traceback'] = None

        try:
            errors['type'] = str(errortype.__name__)
            try:
                errors['arguments'] = str(errorvalue.__dict__['args'])
            except KeyError:
                pass
            errors['traceback'] = traceback.format_tb(errortraceback, maxTBlevel)
        except:
            pass

        if clear == True:
            sys.exc_clear()

No multi-threading that I'm aware of. If I print sys.exc_info() right after calling sys.exc_clear(), everything has been cleared. But once I return from the track function and then re-enter it, even without errors, sys.exc_info() is back with a tuple full of the previous开发者_JAVA百科, old errors.


Please note that last exception information is a per-thread construct. An excerpt from sys.exc_info:

This function returns a tuple of three values that give information about the exception that is currently being handled. The information returned is specific both to the current thread and to the current stack frame.

So, running sys.exc_clear in a thread does not affect other threads.

UPDATE:

Quoting from the documentation:

Warning
Assigning the traceback return value to a local variable in a function that is handling an exception will cause a circular reference. This will prevent anything referenced by a local variable in the same function or by the traceback from being garbage collected. Since most functions don’t need access to the traceback, the best solution is to use something like exctype, value = sys.exc_info()[:2] to extract only the exception type and value. If you do need the traceback, make sure to delete it after use (best done with a try ... finally statement) or to call exc_info() in a function that does not itself handle an exception.

You do assign the traceback to a local variable, and that is why I commented your question with the suggestion to remove the “offending” line.


I believe the error is caused by a misuse of sys.exc_clear() and exceptions. The actual problem is that you're calling it after handling another exception. This is the exception that actually gets cleared, not the one in the you recorded.

A solution to your problem would be to create a different method for tracking exceptions and call it in the except clause and call it only in the except clause -- that way the exception will always be the right one.

Problems I can see in the code above:

  1. You're calling exc.clear_exc() after another exception has been handled, clearing it not the one you want.
  2. You want to call exc.clear_exc() to clear someone else's exceptions - this is wrong and it can break the program (e.g. if a bare raise is called after the call to a fixed version track, it will fail). Whoever is handling the exception likes to use those values, clearing them like this can do no good.
  3. You're expecting that if there was no error, sys.exc_info() will not be set as long as you clear it each time. That is not true -- there might be data for a previous exception there in a completely unrelated call to track. Don't rely on that.

All of those are fixed by using separate methods and never using sys.clear_exc().

Oh, another thing, if those except: clauses without an exceptions are not just examples, it's advisable to handle only exceptions that you know about, and not all of them like that.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜