Home > Software engineering >  Replace a Python exception with another one
Replace a Python exception with another one

Time:10-04

I expect a python function I use to raise a KeyError, in case the user of my script entered a wrong value.

When this happens, I want to raise a ValueError, not a KeyError. I tried to catch the KeyError, then raise a ValueError instead. I then get both.

For example:

def func(my_dict):
    return my_dict["my_key"]

a = {}

try:
    func(a)
except KeyError as e:
    raise ValueError("Replaced exception")

results in this:

$ python3 except.py 
Traceback (most recent call last):
  File "except.py", line 7, in <module>
    func(a)
  File "except.py", line 2, in func
    return my_dict["my_key"]
KeyError: 'my_key'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "except.py", line 9, in <module>
    raise ValueError("Replaced exception")
ValueError: Replaced exception

I don't care about the KeyError itself, I just want to tell the user about the ValueError.

How can I catch the KeyError, but discard it? Is it bad practice? How should I organize this instead?

CodePudding user response:

One way, described here, is to raise ... from None

def func(my_dict):
    return my_dict["my_key"]

a = {}

try:
    func(a)
except KeyError as e:
    raise ValueError("Replaced exception") from None

But I'm still not sure if this is a good idea or not.

CodePudding user response:

You don't have a try/except block to manage the ValueError. So, what you're seeing is a stack trace that's telling you where the ValueError originated.

Consider this:

def func(my_dict):
    return my_dict["my_key"]

a = {}
try:
    try:
        func(a)
    except KeyError as e:
        raise ValueError("Replaced exception")
except ValueError as e:
    print(e)

Output:

Replaced exception

i.e.,

No stack trace because you've caught the ValueError

CodePudding user response:

What you stumbled upon is the difference between an exceptions cause and it's context. There are three slightly different things you can do, and each have different semantics:

Version 1:

try:
    raise KeyError()
except KeyError as ex:
    raise ValueError()

This will show both trackbacks separated by the line

During handling of the above exception, another exception occurred:

This is saying: There was one exception, and in the catch block there was another (unrelated, usually unexpected) exception. This is not what you want here.

Version 2:

try:
    raise KeyError()
except KeyError as ex:
    raise ValueError() from None

This will just show the traceback for the ValueError. This might be what you want, if you are really sure you never ever have to debug the KeyError. Otherwise, read on.

Version 3

try:
    raise KeyError()
except KeyError as ex:
    raise ValueError() from ex

Again, this will show both tracebacks, this time separated by the line

The above exception was the direct cause of the following exception:

Notice the difference from Version 1? Semantically this says: On this level of abstraction this is a ValueError. But in case you need to debug it, here's the traceback of the original KeyError, too. Pretty neat, don't you think?

  • Related