stam
stam

Reputation: 339

python display type of raised exception

I define my own exception

class MyException(Exception):
    pass

somewhere deep in project folder structure, i.e. project\subfolder_1\subfolder_2\etc...\exceptions.py and then use it from project.subproject.subfolder_1.subfolder_2.\etc ... .exceptions import MyException as MyException and then raise it as raise MyException('bad stuff happened')

it is then displayed in output as

project.subproject.subfolder_1.subfolder_2.etc... .exceptions.MyException: bad stuff happened

can I somehow get rid of the full namespace? Since it's anyway 'imported as' and in code referred only as MyException, to display just

MyException: bad stuff happened

as with other built in exceptions?

Upvotes: 0

Views: 51

Answers (1)

Lenormju
Lenormju

Reputation: 4368

When a Python exception has to be printed, its __str__ method gets called (or those of its parents if has not defined one).

file: so75195149/deeply_nested.py

class MyCustomException(Exception): pass

file: main.py

from so75195149.deeply_nested import MyCustomException
raise MyCustomException("bad stuff happened")

gives

Traceback (most recent call last):
  File "main.py", line 3, in <module>
    raise MyCustomException("bad stuff happened")
so75195149.deeply_nested.MyCustomException: bad stuff happened

If you define a __str__ method :

file: so75195149/deeply_nested.py

class MyCustomException(Exception):
    def __str__(self) -> str:
        return "Hello"

you get instead

Traceback (most recent call last):
  File "/home/stack_overflow/main.py", line 3, in <module>
    raise MyCustomException("bad stuff happened")
so75195149.deeply_nested.MyCustomException: Hello

because you can only choose how the exception is printed, but all the rest comes from the fact that your program crashed (uncaught exception), so Python nicely give you information about that by calling its sys.excepthook :

This function prints out a given traceback and exception to sys.stderr.
When an exception is raised and uncaught, the interpreter calls sys.excepthook with three arguments, the exception class, exception instance, and a traceback object. [...] in a Python program this happens just before the program exits. The handling of such top-level exceptions can be customized by assigning another three-argument function to sys.excepthook.

Here we go :

file: main.py

import sys

def my_custom_excepthook(exception_class, exception_instance, traceback_object):
    print(f"sys.excepthook called\n{exception_class=!r}\n{exception_instance=!s}\n{traceback_object=!s}", file=sys.stderr)

sys.excepthook = my_custom_excepthook


from so75195149.deeply_nested import MyCustomException

raise MyCustomException("bad stuff happened")

produces

sys.excepthook called
exception_class=<class 'so75195149.deeply_nested.MyCustomException'>
exception_instance=Hello
traceback_object=<traceback object at 0x7f79515fe200>

So if you want something that looks like the usual way Python prints, using previous answers :

file: main.py

import sys
import traceback

def my_custom_excepthook(exception_class, exception_instance, traceback_object):
    # do not use traceback.format_exception
    print("Traceback (most recent call last):", file=sys.stderr)
    traceback.print_tb(traceback_object, file=sys.stderr)
    print(f"{exception_class.__name__}: {exception_instance!s}", file=sys.stderr)

sys.excepthook = my_custom_excepthook


from so75195149.deeply_nested import MyCustomException

raise MyCustomException("bad stuff happened")

gives

Traceback (most recent call last):
  File "/home/stack_overflow/main.py", line 15, in <module>
    raise MyCustomException("bad stuff happened")
MyCustomException: Hello

versus the original :

Traceback (most recent call last):
  File "/home/stack_overflow/main.py", line 13, in <module>
    raise MyCustomException("bad stuff happened")
so75195149.deeply_nested.MyCustomException: Hello

See traceback.format_exception and qualified name (__qualname__) for more information.

But I advise you to be careful in setting a custom excepthook. If another exception crashes the program, you won't get its qualified name, which is very often useful (the reason it is included by default). It is global for the whole Pyhton app.

I recommend instead to wrap your custom exceptions in a non-deeply-neested-defined exception. Or do not use custom-defined exceptions, use the standard ones.

Upvotes: 1

Related Questions