python display type of raised exception - python

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?

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.

Related

what is the purpose of using the `from exc` in `raise RuntimeError('Failed to open database') from exc`?

https://docs.python.org/3/tutorial/errors.html#exception-chaining
>>>
>>> def func():
... raise IOError
...
>>> try:
... func()
... except IOError as exc:
... raise RuntimeError('Failed to open database') from exc
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 2, in func
OSError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError: Failed to open database
Exception chaining happens automatically when an exception is raised inside an except or finally section. So what is the purpose of using the from exc in raise RuntimeError('Failed to open database') from exc? Isn't it implicit?
You can always check the PEP which introduces a feature. In this case, you may read this section of PEP 3141.
The use of the syntax raise EXCEPTION from CAUSE allows for an explicit chaining of exceptions. Like in the example given on that page, if you want to raise only a certain error in an API, but also want to highlight the original exception raising the issue, you can use this way of chaining exceptions.
So it just offers a more explicit way of doing that, while the "normal" syntax raise EXCEPTION only implicitly chains the exceptions.
Is this understandable? Let me know if you don't understand it.

Ignore and log error with contextlib contextmanager

I want a context manager to catch an exception, print the stack trace, and then allow execution to continue.
I want to know if I can do this with the contextlib contextmanager decorator. If not, how can I do it?
Documentation suggests the following:
At the point where the generator yields, the block nested in the with statement is executed. The generator is then resumed after the block is exited. If an unhandled exception occurs in the block, it is reraised inside the generator at the point where the yield occurred. Thus, you can use a try…except…finally statement to trap the error (if any), or ensure that some cleanup takes place. If an exception is trapped merely in order to log it or to perform some action (rather than to suppress it entirely), the generator must reraise that exception.
So I try the obvious approach that the documentation leads me to:
import contextlib
import logging
#contextlib.contextmanager
def log_error():
try:
yield
except Exception as e:
logging.exception('hit exception')
finally:
print 'done with contextmanager'
def something_inside_django_app():
with log_error():
raise Exception('alan!')
something_inside_django_app()
print 'next block of code'
This produces the output
ERROR:root:hit exception
Traceback (most recent call last):
File "exception_test.py", line 8, in log_error
yield
File "exception_test.py", line 17, in something_inside_django_app
raise Exception('alan!')
Exception: alan!
done with contextmanager
next block of code
This loses critical information about where the exception was raised from. Consider what you get when you adjust the context manager to not supress the exception:
Traceback (most recent call last):
File "exception_test.py", line 20, in <module>
something_inside_django_app()
File "exception_test.py", line 17, in something_inside_django_app
raise Exception('alan!')
Exception: alan!
Yes, it was able to tell me that the exception was raised from line 17, thank you very much, but the prior call at line 20 is lost information. How can I have the context manager give me the actual full call stack and not its truncated version of it? To recap, I want to fulfill two requirements:
have a python context manager suppress an exception raised in the code it wraps
print the stack trace that would have been generated by that code, had I not been using the context manager
If this cannot be done with the decorator, then I'll use the other style of context manager instead. If this cannot be done with context managers, period, I would like to know what a good pythonic alternative is.
I have updated my solution for this problem here:
https://gist.github.com/AlanCoding/288ee96b60e24c1f2cca47326e2c0af1
There was more context that the question missed. In order to obtain the full stack at the point of exception, we need both the traceback returned to the context manager, and the current context. Then we can glue together the top of the stack with the bottom of the stack.
To illustrate the use case better, consider this:
def err_method1():
print [1, 2][4]
def err_method2():
err_method1()
def outside_method1():
with log_error():
err_method2()
def outside_method2():
outside_method1()
outside_method2()
To really accomplish what this question is looking for, we want to see both outer methods, and both inner methods in the call stack.
Here is a solution that does appear to work for this:
class log_error(object):
def __enter__(self):
return
def __exit__(self, exc_type, exc_value, exc_traceback):
if exc_value:
# We want the _full_ traceback with the context, so first we
# get context for the current stack, and delete the last 2
# layers of context, saying that we're in the __exit__ method...
top_stack = StringIO.StringIO()
tb.print_stack(file=top_stack)
top_lines = top_stack.getvalue().strip('\n').split('\n')[:-4]
top_stack.close()
# Now, we glue that stack to the stack from the local error
# that happened within the context manager
full_stack = StringIO.StringIO()
full_stack.write('Traceback (most recent call last):\n')
full_stack.write('\n'.join(top_lines))
full_stack.write('\n')
tb.print_tb(exc_traceback, file=full_stack)
full_stack.write('{}: {}'.format(exc_type.__name__, str(exc_value)))
sinfo = full_stack.getvalue()
full_stack.close()
# Log the combined stack
logging.error('Log message\n{}'.format(sinfo))
return True
The traceback looks like:
ERROR:root:Log message
Traceback (most recent call last):
File "exception_test.py", line 71, in <module>
outside_method2()
File "exception_test.py", line 69, in outside_method2
outside_method1()
File "exception_test.py", line 65, in outside_method1
err_method2()
File "exception_test.py", line 60, in err_method2
err_method1()
File "exception_test.py", line 56, in err_method1
print [1, 2][4]
IndexError: list index out of range
This is the same information that you would expect from doing logging.exception in a try-except over the same code that you wrap in the context manager.

Python logging exceptions with traceback, but without displaying messages twice

If I run the following code:
import logging
logger = logging.getLogger('creator')
try:
# some stuff
except Exception as exception:
logger.exception(exception)
I get the following output on the screen:
creator : ERROR division by zero
Traceback (most recent call last):
File "/graph_creator.py", line 21, in run
1/0
ZeroDivisionError: division by zero
Are there ways to get such a output?
creator : ERROR ZeroDivisionError: division by zero
Traceback (most recent call last):
File "/graph_creator.py", line 21, in run
1/0
Of course, I can get this (but I don't like it):
creator : ERROR Сaught exception (and etc...)
Traceback (most recent call last):
File "/graph_creator.py", line 21, in run
1/0
ZeroDivisionError: division by zero
If you called exception like this:
logger.exception('%s: %s', exception.__class__.__name__, exception)
then you could get the exception class name in the initial line.
If you need more precise changes, you can use a custom Formatter subclass which formats things exactly as you like. This would need to override format_exception to change the formatting of the traceback.

Raise an exception with traceback starting from caller

I'm trying to make an automated test framework for a side-project and could use some help creating the assertion checks.
Running this in python...
assert(False)
Gives you this...
Traceback (most recent call last):
File "test.py", line 1, in <module>
assert(False)
AssertionError
As you can see the traceback lowest level is assert(False). So I made my custom assert that prints when the assert succeeds.
def custom_assert(condition):
if condition:
print("Yay! It werks!")
else:
raise Exception("Nay, it don't werks...")
custom_assert(False)
But instead of what assert gives, custom_assert gives me this.
Traceback (most recent call last):
File "test.py", line 14, in <module>
custom_assert(False)
File "test.py", line 12, in custom_assert
raise Exception("Nay, it don't werks...")
Exception: Nay, it don't werks...
Which is of course the default behavior. Perfectly useful 99.9999% of the time, but this is that one time it could be improved. It's not useful to know that the method I called to raise an error when the condition is false raised the error.
How can I make my custom_assert raise an exception with a traceback starting from the caller, the same way assert does?
P.S.: I don't want to print it, I want the exception to have properly modified traceback so it works properly with debuggers and other tools too!
Edit
To clarify, the traceback I want would be like this.
Traceback (most recent call last):
File "test.py", line 14, in <module>
custom_assert(False)
Exception: Nay, it don't werks...
Essentially what you want to do is something similar to this:
tb = None
try:
raise Exception('foo')
except Exception:
tb = sys.exc_info()[2]
tb.tb_frame = tb.tb_frame.f_back # This line doesn't work
raise Exception('Nay it doesnt werks').with_traceback(tb)
but you can't assign tb_frame, and from mucking around in the CPython code, this is C-generated data structures (not python) (see sys._getframe())
So your only option left is to mock the entire machinery and then convince python to use your stack. This looks like what jinja2 is doing. If that's what you choose to do, good luck! (It's out of my scope at that point)

Nested causes in nested exceptions in python [duplicate]

This question already has answers here:
"Inner exception" (with traceback) in Python?
(9 answers)
Closed 8 years ago.
Is there a way to provide information about the cause of an inner exception when passing it up the chain (like it's possible in java with the cause property of Exception class).
Please consider the following "python pseudo code" (without 100 % correct and invented function and class names)
try:
clientlib.receive_data_chunk()
except ClientException as clientException:
raise RuntimeError("reading from client failed"
+ " (see nested exceptions for details)", cause=clientException)
and in clientlib.py
def receive_data_chunk():
try:
chunk = socket.read(20)
return chunk
except IOException as iOException:
raise ClientException("couldn't read from client", cause = iOException)
If not within native python what would be best practice to achieve what I want to do?
Please note that I want to preserve both stacktraces of the inner and the outer exception, i.e. the following solution is not satisfying:
import sys
def function():
try:
raise ValueError("inner cause")
except Exception:
_, ex, traceback = sys.exc_info()
message = "outer explanation (see nested exception for details)"
raise RuntimeError, message, traceback
if __name__ == "__main__":
function()
produces only the following output:
Traceback (most recent call last):
File "a.py", line 13, in <module>
function()
File "a.py", line 6, in function
raise ValueError("inner cause")
RuntimeError: outer explanation (see nested exception for details)
I cannot see where the RuntimeError occured, so in my understanding the outer stacktrace is lost.
In Python 3, you can use the from keyword to specify an inner exception:
raise ClientException(...) from ioException
You get a traceback that looks like this:
Traceback (most recent call last):
...
IOException: timeout
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
...
ClientException: couldn't read from client

Categories