Catching a specific exception in a chain when reraising it is not an option

40 Views Asked by At

Consider the following Python code:

def say(words):
    for word in words:
        if word == "Ni":
            raise ValueError("We are no longer the knights who say Ni!")
        print(word)

def blackbox(function, sentence):
    words = sentence.split()
    try:
        function(words)
    except Exception as e:
        raise RuntimeError("Generic Error")

blackbox(say, "Foo Ni Bar")

It prints the following traceback:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [35], in blackbox(function, sentence)
      9 try:
---> 10     function(words)
     11 except Exception as e:

Input In [35], in say(words)
      3 if word == "Ni":
----> 4     raise ValueError("We are no longer the knights who say Ni!")
      5 print(word)

ValueError: We are no longer the knights who say Ni!

During handling of the above exception, another exception occurred:

RuntimeError                              Traceback (most recent call last)
Input In [35], in <cell line: 14>()
     11     except Exception as e:
     12         raise RuntimeError("Generic Error")
---> 14 blackbox(say, "Foo Ni Bar")

Input In [35], in blackbox(function, sentence)
     10     function(words)
     11 except Exception as e:
---> 12     raise RuntimeError("Generic Error")

RuntimeError: Generic Error

Assume I am only interested in the first error. I could simply reraise it by replacing raise RuntimeError("Generic Error") by raise e in blackbox(): end of the story.

Except (!) that I cannot modify the code of blackbox(), which belongs to an external library.

How can I obtain the same result without touching it? My guess is that I could wrap the call to blackbox() in a try... except..., retrieve the chain of exceptions, and select the one I am interested in. But I failed to find anywhere how to do such a thing.

Edit: changed the name and the signature of the second function to make the constraints clearer.

1

There are 1 best solutions below

0
Aristide On BEST ANSWER

Answering my own question. It is enough to catch the final exception and raise its context, i.e., replace the last line by:

try:
    blackbox(say, "Foo Ni Bar")
except Exception as e:
    raise e.__context__

Traceback:

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Input In [105], in <cell line: 14>()
     14 try:
---> 15     blackbox(say, "Foo Ni Bar")
     16 except Exception as e:

Input In [105], in blackbox(function, sentence)
     11 except Exception as e:
---> 12     raise RuntimeError("Generic Error")

RuntimeError: Generic Error

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
Input In [105], in <cell line: 14>()
     15     blackbox(say, "Foo Ni Bar")
     16 except Exception as e:
---> 17     raise e.__context__

Input In [105], in blackbox(function, sentence)
      8 words = sentence.split()
      9 try:
---> 10     function(words)
     11 except Exception as e:
     12     raise RuntimeError("Generic Error")

Input In [105], in say(words)
      2 for word in words:
      3     if word == "Ni":
----> 4         raise ValueError("We are no longer the knights who say Ni!")
      5     print(word)

ValueError: We are no longer the knights who say Ni!