2

This might be a silly question, so I did some research on these questions:

How do I raise the same Exception with a custom message in Python?
Proper way to declare custom exceptions in modern Python?

But none of these are matches what I'm trying to do for my CLI script, namely:

1.) If a certain Exception is raised, I want to re-raise the same Exception, with a tailored message instead of the default one.
2.) I am not looking to redefine a custom Exception type, just the same Exception.
3.) I am not looking to print a console text. I want to actually raise Exception because I need the exit code to be as close as possible as if the original Exception was raised since another process relies on this code.
4.) I want the error to be as short as possible, straight and to the point. A full trace back is not necessary.

So for example, these are what I've tried:

Attempt 1:

def func():    
    try:
        1/0
    except ZeroDivisionError:
        raise ZeroDivisionError("Don't do that you numbnut!")

Result 1:

Traceback (most recent call last):
  File "c:\Users\Dude\test.py", line 3, in <module>
    1/0
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "c:\Users\Dude\test.py", line 5, in <module>
    raise ZeroDivisionError("Don't do that you numbnut!")
ZeroDivisionError: Don't do that you numbnut!

[Done] exited with code=1 in 2.454 seconds

This meets my goal of #1, 2 and 3 are met, but the trace back is way too long... I don't need the original Exception at all, just the custom message.

Attempt 2:

def catch():
    try:
        1/0
        return None
    except ZeroDivisionError:
        return ZeroDivisionError("Don't do that you numbnut!")

error = catch()
if error:
    raise error

Result 2:

Traceback (most recent call last):
  File "c:\Users\Dude\test.py", line 10, in <module>
    raise error
ZeroDivisionError: Don't do that you numbnut!

[Done] exited with code=1 in 2.458 seconds

This gets me very close to what I want and is what I'm doing, however it feels quite unpythonic and pylint complains about the raise error line:

Raising NoneType while only classes or instances are allowed pylint(raising-bad-type)

I also tried the methods in my linked questions, but they are unsatisfactory to all of my requirements as well. For the purpose of succinctness I have not included my attempts of those here.

My question is thus: is there a better, more obvious way to catch an Exception and re-raise it with a custom message that I'm simply missing?

r.ook
  • 13,466
  • 2
  • 22
  • 39
  • The first one is closer to what you want. Don't raise a second error, you want to reraise the same error. I think it would be `except ZeroDivisionError as err: raise err("Don't Do ...") – user1558604 Dec 11 '19 at 20:28
  • As mentioned in my question, it doesn't meet my "shortest traceback" requirement. Anything above the "During handling..." line is unnecessary and pollutes the output. – r.ook Dec 11 '19 at 20:30
  • That is because you are returning the error rather than re raising it. – user1558604 Dec 11 '19 at 20:30
  • In my first attempt, I'm "re-raising" it though? Unless you're talking about the second attempt, which I already said is close to what I want in behaviour but feels unpythonic in code. – r.ook Dec 11 '19 at 20:32
  • no, you are raising a second exception, not the same exception. – user1558604 Dec 11 '19 at 20:33
  • Ah, I missed the `... as err: raise err(...)` part. However, that doesn't seem to work. It gives a `TypeError: 'ZeroDivisionError' object is not callable`. – r.ook Dec 11 '19 at 20:37
  • yeah, I realized that doesn't work after I commented. Not an expert on exceptions, but the form is something like that. Not quite sure – user1558604 Dec 11 '19 at 20:39

3 Answers3

2

This all seems quite unpythonic to me to begin with - but if it is really what you want why not raise from None in your first example in order not to get a larger traceback.

def func():
    try:
        1/0
    except ZeroDivisionError:
        raise ZeroDivisionError("Don't do that you numbnut!") from None


func()

Giving

Traceback (most recent call last):
  File "/Users/dmodesitt/Dev/the.py", line 9, in <module>
    func()
  File "/Users/dmodesitt/Dev/the.py", line 6, in func
    raise ZeroDivisionError("Don't do that you numbnut!") from None
ZeroDivisionError: Don't do that you numbnut!
modesitt
  • 7,052
  • 2
  • 34
  • 64
2

This feature is called "chained exceptions" and was added in Python 3.

This block

try:
    1 / 0
except ZeroDivisionError:
    raise ZeroDivisionError("Don't do that you numbnut!")

>>>>

Traceback (most recent call last):
  File "test123.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test123.py", line 4, in <module>
    raise ZeroDivisionError("Don't do that you numbnut!")
ZeroDivisionError: Don't do that you numbnut!

Is similar to

try:
    1 / 0
except ZeroDivisionError as e:
    raise ZeroDivisionError("Don't do that you numbnut!") from e
=>>>>>>>>>
raceback (most recent call last):
  File "test123.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero

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

Traceback (most recent call last):
  File "test123.py", line 4, in <module>
    raise ZeroDivisionError("Don't do that you numbnut!") from e
ZeroDivisionError: Don't do that you numbnut!

But you can disable exceptions chaining using from None statement.

try:
    1 / 0
except ZeroDivisionError:
    raise ZeroDivisionError("Don't do that you numbnut!") from None
=>>>>>>>>
Traceback (most recent call last):
  File "test123.py", line 4, in <module>
    raise ZeroDivisionError("Don't do that you numbnut!") from None
ZeroDivisionError: Don't do that you numbnut!

More information about the feature at https://www.python.org/dev/peps/pep-3134/

Alexandr Shurigin
  • 3,921
  • 1
  • 13
  • 25
0

I was able to also use exit(...) to replicate very similar to what I want, but again it feels rather unpythonic:

def func():
    try:
        1/0
    except ZeroDivisionError as err:
        exit(f"{err.__class__.__name__}: Don't do that you numbnut!")

Result:

ZeroDivisionError: Don't do that you numbnut!

[Done] exited with code=1 in 3.433 seconds

For the purpose of my script, I think this might be the simplest solution. But from a wider stand point, I believe the other answers are better.

r.ook
  • 13,466
  • 2
  • 22
  • 39