How to suppress displaying the parent exception (the cause) for subsequent exceptions

北城以北 提交于 2019-12-05 20:15:48

问题


I'm aware of raise ... from None and have read How can I more easily suppress previous exceptions when I raise my own exception in response?.

However, how can I achieve that same effect (of suppressing the "During handling of the above exception, another exception occurred" message) without having control over the code that is executed from the except clause? I thought that sys.exc_clear() could be used for this, but that function doesn't exist in Python 3.

Why am I asking this? I have some simple caching code that looks like (simplified):

try:
    value = cache_dict[key]
except KeyError:
    value = some_api.get_the_value_via_web_service_call(key)
    cache_dict[key] = value

When there's an exception in the API call, the output will be something like this:

Traceback (most recent call last):
  File ..., line ..., in ...
KeyError: '...'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File ..., line ..., in ...
some_api.TheInterestingException: ...

This is misleading, as the original KeyError is not really an error at all. I could of course avoid the situation by changing the try/except (EAFP) into a test for the key's presence (LBYL) but that's not very Pythonic and less thread-friendly (not that the above is thread-safe as is, but that's beside the point).

It's unreasonable to expect all code in some_api to change their raise X to raise X from None (and it wouldn't even make sense in all cases). Is there a clean solution to avoid the unwanted exception chain in the error message?

(By the way, bonus question: the cache thing I used in the example is basically equivalent to cache_dict.setdefault(key, some_api.get_the_value_via_web_service_call(key)), if only the second argument to setdefault could be a callable that would only be called when the value needs to be set. Isn't there a better / canonical way to do it?)


回答1:


You have a few options here.

First, a cleaner version of what orlp suggested:

try:
    value = cache_dict[key]
except KeyError:
    try:
        value = some_api.get_the_value(key)
    except Exception as e:
        raise e from None
    cache_dict[key] = value

For the second option, I'm assuming there's a return value hiding in there somewhere that you're not showing:

try:
    return cache_dict[key]
except KeyError:
    pass
value = cache_dict[key] = some_api.get_the_value(key)
return value

Third option, LBYL:

if key not in cache_dict:
    cache_dict[key] = some_api.get_the_value(key)
return cache_dict[key]

For the bonus question, define your own dict subclass that defines __missing__:

class MyCacheDict(dict):

    def __missing__(self, key):
        value = self[key] = some_api.get_the_value(key)
        return value

Hope this helps!




回答2:


You can try suppressing the context yourself:

try:
    value = cache_dict[key]
except KeyError:
    try:
        value = some_api.get_the_value_via_web_service_call(key)
    except Exception as e:
        e.__context__ = None
        raise

    cache_dict[key] = value


来源:https://stackoverflow.com/questions/30235516/how-to-suppress-displaying-the-parent-exception-the-cause-for-subsequent-excep

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!