How to manually log exception to be used with ereporter? - python

I use ereporter in my GAE app to be informed about all uncaught exceptions thru email. But I also would like to be informed other (handled) issues, so, I use the following code:
if something:
pass
else:
logging.exception('something is wrong')
But ereporter fails with such cases with AttributeError:
Traceback (most recent call last):
File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/ext/ereporter/ereporter.py", line 227, in emit
signature = self.__GetSignature(record.exc_info)
File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/ext/ereporter/ereporter.py", line 175, in __GetSignature
fulltype = '%s.%s' % (ex_type.__module__, ex_type.__name__)
AttributeError: 'NoneType' object has no attribute '__module__'
I understand why it happens - there is no real Exception. But is there any way still to log such cases with ereporter?

As you said yourself - there's no exception to report. Since ereporter seems to only report exceptions, the solution is clear:
if something:
pass
else:
try:
raise Exception()
except Exception:
logging.exception('something is wrong')

Related

Is there a way to access the "another exceptions" that happen in a python3 traceback?

Let's assume you have some simple code that you don't control (eg: it's in a module you're using):
def example():
try:
raise TypeError("type")
except TypeError:
raise Exception("device busy")
How would I go about accessing the TypeError in this traceback in order to handle it?
Traceback (most recent call last):
File "/usr/local/google/home/dthor/dev/pyle/pyle/fab/visa_instrument/exception_helpers.py", line 3, in example
raise TypeError("type")
TypeError: type
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/google/home/dthor/dev/pyle/pyle/fab/visa_instrument/exception_helpers.py", line 7, in <module>
example()
File "/usr/local/google/home/dthor/dev/pyle/pyle/fab/visa_instrument/exception_helpers.py", line 5, in example
raise Exception("device busy")
Exception: device busy
I can do the below, but i'm not happy with it because I'm doing string comparison - meaning things would break if the underlying module changes what string they raise (I don't control example()):
try:
example()
except Exception as err:
if "device busy" in str(err):
# do the thing
pass
# But raise any other exceptions
raise err
I'd much rather have:
try:
example()
except Exception as err:
if TypeError in err.other_errors: # magic that doesn't exist
# do the thing
pass
raise err
or even
try:
example()
except TypeError in exception_stack: # Magic that doesn't exist
# do the thing
pass
except Exception:
I'm investigating the traceback module and sys.exc_info(), but don't have anything concrete yet.
Followup: would things be different if the exception was chained? Eg: raise Exception from the_TypeError_exception
Check the __context__ of the exception:
>>> try:
... example()
... except Exception as e:
... print(f"exception: {e!r}")
... print(f"context: {e.__context__!r}")
...
exception: Exception('device busy')
context: TypeError('type')
If you use a chained exception, the original exception will also be accessible via the __cause__ attribute.

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.

What's the benefit of using "raise ... from ..." in Python 3? [duplicate]

What's the difference between raise and raise from in Python?
try:
raise ValueError
except Exception as e:
raise IndexError
which yields
Traceback (most recent call last):
File "tmp.py", line 2, in <module>
raise ValueError
ValueError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "tmp.py", line 4, in <module>
raise IndexError
IndexError
and
try:
raise ValueError
except Exception as e:
raise IndexError from e
which yields
Traceback (most recent call last):
File "tmp.py", line 2, in <module>
raise ValueError
ValueError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "tmp.py", line 4, in <module>
raise IndexError from e
IndexError
The difference is that when you use from, the __cause__ attribute is set and the message states that the exception was directly caused by. If you omit the from then no __cause__ is set, but the __context__ attribute may be set as well, and the traceback then shows the context as during handling something else happened.
Setting the __context__ happens if you used raise in an exception handler; if you used raise anywhere else no __context__ is set either.
If a __cause__ is set, a __suppress_context__ = True flag is also set on the exception; when __suppress_context__ is set to True, the __context__ is ignored when printing a traceback.
When raising from a exception handler where you don't want to show the context (don't want a during handling another exception happened message), then use raise ... from None to set __suppress_context__ to True.
In other words, Python sets a context on exceptions so you can introspect where an exception was raised, letting you see if another exception was replaced by it. You can also add a cause to an exception, making the traceback explicit about the other exception (use different wording), and the context is ignored (but can still be introspected when debugging). Using raise ... from None lets you suppress the context being printed.
See the raise statement documenation:
The from clause is used for exception chaining: if given, the second expression must be another exception class or instance, which will then be attached to the raised exception as the __cause__ attribute (which is writable). If the raised exception is not handled, both exceptions will be printed:
>>> try:
... print(1 / 0)
... except Exception as exc:
... raise RuntimeError("Something bad happened") from exc
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened
A similar mechanism works implicitly if an exception is raised inside an exception handler or a finally clause: the previous exception is then attached as the new exception’s __context__ attribute:
>>> try:
... print(1 / 0)
... except:
... raise RuntimeError("Something bad happened")
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened
Also see the Built-in Exceptions documentation for details on the context and cause information attached to exceptions.
PEP 3134, Exception Chaining and Embedded Tracebacks introduced chaining of exceptions (implicitly chained with explicit raise EXCEPTION or implicit raise, and explicitly chained with explicit raise EXCEPTION from CAUSE). Here are the relevant paragraphs to understand their usage:
Motivation
During the handling of one exception (exception A), it is possible that another exception (exception B) may occur. In today’s Python (version 2.4), if this happens, exception B is propagated outward and exception A is lost. In order to debug the problem, it is useful to know about both exceptions. The __context__ attribute retains this information automatically.
Sometimes it can be useful for an exception handler to intentionally re-raise an exception, either to provide extra information or to translate an exception to another type. The __cause__ attribute provides an explicit way to record the direct cause of an exception.
[…]
Implicit Exception Chaining
Here is an example to illustrate the __context__ attribute:
def compute(a, b):
try:
a/b
except Exception, exc:
log(exc)
def log(exc):
file = open('logfile.txt') # oops, forgot the 'w'
print >>file, exc
file.close()
Calling compute(0, 0) causes a ZeroDivisionError. The compute() function catches this exception and calls log(exc), but the log() function also raises an exception when it tries to write to a file that wasn’t opened for writing.
In today’s Python, the caller of compute() gets thrown an IOError. The ZeroDivisionError is lost. With the proposed change, the instance of IOError has an additional __context__ attribute that retains the ZeroDivisionError.
[…]
Explicit Exception Chaining
The __cause__ attribute on exception objects is always initialized to None. It is set by a new form of the raise statement:
raise EXCEPTION from CAUSE
which is equivalent to:
exc = EXCEPTION
exc.__cause__ = CAUSE
raise exc
In the following example, a database provides implementations for a few different kinds of storage, with file storage as one kind. The database designer wants errors to propagate as DatabaseError objects so that the client doesn’t have to be aware of the storage-specific details, but doesn’t want to lose the underlying error information.
class DatabaseError(Exception):
pass
class FileDatabase(Database):
def __init__(self, filename):
try:
self.file = open(filename)
except IOError, exc:
raise DatabaseError('failed to open') from exc
If the call to open() raises an exception, the problem will be reported as a DatabaseError, with a __cause__ attribute that reveals the IOError as the original cause.
Enhanced Reporting
The default exception handler will be modified to report chained exceptions. The chain of exceptions is traversed by following the __cause__ and __context__ attributes, with __cause__ taking priority. In keeping with the chronological order of tracebacks, the most recently raised exception is displayed last; that is, the display begins with the description of the innermost exception and backs up the chain to the outermost exception. The tracebacks are formatted as usual, with one of the lines:
The above exception was the direct cause of the following exception:
or
During handling of the above exception, another exception occurred:
between tracebacks, depending whether they are linked by __cause__ or __context__ respectively. Here is a sketch of the procedure:
def print_chain(exc):
if exc.__cause__:
print_chain(exc.__cause__)
print '\nThe above exception was the direct cause...'
elif exc.__context__:
print_chain(exc.__context__)
print '\nDuring handling of the above exception, ...'
print_exc(exc)
[…]
PEP 415, Implement Context Suppression with Exception Attributes then introduced suppression of exception contexts (with explicit raise EXCEPTION from None). Here is the relevant paragraph to understand its usage:
Proposal
A new attribute on BaseException, __suppress_context__, will be introduced. Whenever __cause__ is set, __suppress_context__ will be set to True. In particular, raise exc from cause syntax will set exc.__suppress_context__ to True. Exception printing code will check for that attribute to determine whether context and cause will be printed. __cause__ will return to its original purpose and values.
There is precedence for __suppress_context__ with the print_line_and_file exception attribute.
To summarize, raise exc from cause will be equivalent to:
exc.__cause__ = cause
raise exc
where exc.__cause__ = cause implicitly sets exc.__suppress_context__.
So in PEP 415, the sketch of the procedure given in PEP 3134 becomes the following:
def print_chain(exc):
if exc.__cause__:
print_chain(exc.__cause__)
print '\nThe above exception was the direct cause...'
elif exc.__context__ and not exc.__suppress_context__:
print_chain(exc.__context__)
print '\nDuring handling of the above exception, ...'
print_exc(exc)
The shortest answer. PEP-3134 says it all. raise Exception from e sets the __cause__ filed of the new exception.
A longer answer from the same PEP:
__context__ field would be set implicitly to the original error inside except: block unless told not to with __suppress_context__ = True.
__cause__ is just like context but has to be set explicitly by using from syntax
traceback will always chain when you call raise inside an except block. You can get rid of traceback by a) swallowing an exception except: pass or by messing with sys.exc_info() directly.
The long answer
import traceback
import sys
class CustomError(Exception):
def __init__(self):
super().__init__("custom")
def print_exception(func):
print(f"\n\n\nEXECURTING FUNCTION '{func.__name__}' \n")
try:
func()
except Exception as e:
"Here is result of our actions:"
print(f"\tException type: '{type(e)}'")
print(f"\tException message: '{e}'")
print(f"\tException context: '{e.__context__}'")
print(f"\tContext type: '{type(e.__context__)}'")
print(f"\tException cause: '{e.__cause__}'")
print(f"\tCause type: '{type(e.__cause__)}'")
print("\nTRACEBACKSTART>>>")
traceback.print_exc()
print("<<<TRACEBACKEND")
def original_error_emitter():
x = {}
print(x.does_not_exist)
def vanilla_catch_swallow():
"""Nothing is expected to happen"""
try:
original_error_emitter()
except Exception as e:
pass
def vanilla_catch_reraise():
"""Nothing is expected to happen"""
try:
original_error_emitter()
except Exception as e:
raise e
def catch_replace():
"""Nothing is expected to happen"""
try:
original_error_emitter()
except Exception as e:
raise CustomError()
def catch_replace_with_from():
"""Nothing is expected to happen"""
try:
original_error_emitter()
except Exception as e:
raise CustomError() from e
def catch_reset_trace():
saw_an_error = False
try:
original_error_emitter()
except Exception as e:
saw_an_error = True
if saw_an_error:
raise CustomError()
print("Note: This will print nothing")
print_exception(vanilla_catch_swallow)
print("Note: This will print AttributeError and 1 stack trace")
print_exception(vanilla_catch_reraise)
print("Note: This will print CustomError with no context but 2 stack traces")
print_exception(catch_replace)
print("Note: This will print CustomError with AttributeError context and 2 stack traces")
print_exception(catch_replace_with_from)
print("Note: This will brake traceback chain")
print_exception(catch_reset_trace)
Will result in the following output:
Note: This will print nothing
EXECURTING FUNCTION 'vanilla_catch_swallow'
Note: This will print AttributeError and 1 stack trace
EXECURTING FUNCTION 'vanilla_catch_reraise'
Exception type: '<class 'AttributeError'>'
Exception message: ''dict' object has no attribute 'does_not_exist''
Exception context: 'None'
Context type: '<class 'NoneType'>'
Exception cause: 'None'
Cause type: '<class 'NoneType'>'
TRACEBACKSTART>>>
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
func()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 41, in vanilla_catch_reraise
raise e
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 39, in vanilla_catch_reraise
original_error_emitter()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'
<<<TRACEBACKEND
Note: This will print CustomError with no context but 2 stack traces
EXECURTING FUNCTION 'catch_replace'
Exception type: '<class '__main__.CustomError'>'
Exception message: 'custom'
Exception context: ''dict' object has no attribute 'does_not_exist''
Context type: '<class 'AttributeError'>'
Exception cause: 'None'
Cause type: '<class 'NoneType'>'
TRACEBACKSTART>>>
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 46, in catch_replace
original_error_emitter()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
func()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 48, in catch_replace
raise CustomError()
CustomError: custom
<<<TRACEBACKEND
Note: This will print CustomError with AttributeError context and 2 stack traces
EXECURTING FUNCTION 'catch_replace_with_from'
Exception type: '<class '__main__.CustomError'>'
Exception message: 'custom'
Exception context: ''dict' object has no attribute 'does_not_exist''
Context type: '<class 'AttributeError'>'
Exception cause: ''dict' object has no attribute 'does_not_exist''
Cause type: '<class 'AttributeError'>'
TRACEBACKSTART>>>
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 53, in catch_replace_with_from
original_error_emitter()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
func()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 55, in catch_replace_with_from
raise CustomError() from e
CustomError: custom
<<<TRACEBACKEND
Note: This will brake traceback chain
EXECURTING FUNCTION 'catch_reset_trace'
Exception type: '<class '__main__.CustomError'>'
Exception message: 'custom'
Exception context: 'None'
Context type: '<class 'NoneType'>'
Exception cause: 'None'
Cause type: '<class 'NoneType'>'
TRACEBACKSTART>>>
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
func()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 64, in catch_reset_trace
raise CustomError()
CustomError: custom
<<<TRACEBACKEND

How do you log a manually raised exception in an if statement in python

Put simply, I have a piece of code that looks like this:
if some_condition_that_evals_to_True:
raise ValueError("Error message")
What I want to do is to insert a logging statement that logs this entire exception but doing it just this way only saves the logger message:
if some_condition_that_evals_to_True:
logger.error("Logged error message")
raise ValueError("Error message")
Any one know how to save the entire error message including the ValueError?
EDIT:
The following is what I am trying to recreate:
if some_condition_that_evals_to_True:
try:
raise ValueError("Error with value")
except ValueError:
logger.exception("Logging Error with Value")
raise
But this seems like a roundabout way to get the behavior I want, so another way to phrase my question: Is there a more elegant way get the same behavior as the above codeblock?
Try the stack_info keyword argument when using the logging module:
import logging
logging.basicConfig(filename='example.log',level=logging.DEBUG)
if True:
logging.error('Error Message', stack_info=True)
raise ValueError('Custom Error Message')
Running this shows the following:
J:\>python log_stack.py
Traceback (most recent call last):
File "log_stack.py", line 5, in <module>
raise ValueError('Custom Error Message')
ValueError: Custom Error Message
J:\>more example.log
ERROR:root:Error Message
Stack (most recent call last):
File "log_stack.py", line 4, in <module>
logging.error('Error Message', stack_info=True)

How would i make a custom error message in python

In a python program I am making, I want it to only take integers, and if it gets a string say "There has been an error in the system." instead of murmering sensless information the user will not understand
Use a try-except block to capture the error and use the raise statement to say the error message of your choice:
try:
a = int(input())
except:
raise Exception('There has been an error in the system')
Using try, except and raise
Since ValueError inherits from the Exception class, the first parameter when creating a ValueError object is the message it prints:
try:
int("string") #the code that raises the error
except ValueError:
raise ValueError("Your custom message here.")
This prints:
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ValueError: invalid literal for int() with base 10: 'string'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
ValueError: Your custom message here.
If you don't want the previous error chain to print, put from None in the raise statement:
try:
int("string") #the code that raises the error
except ValueError:
raise ValueError("Your custom message here.") from None
This prints:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
ValueError: Your custom message here.
I suggest you leave the chain because it gives more information, like what was inputted that raised the error. If you want to include the information from the original error in the custom message, use the error's attributes:
from traceback import format_tb
try:
int("string") #the code that raises the error
except ValueError as err:
raise ValueError("Custom message with traceback and original message\n" + format_tb(err.__traceback__)[0] + err.args[0] + "\nEnd of error message.") from None
This prints
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
ValueError: Custom message with traceback and message
File "<stdin>", line 2, in <module>
invalid literal for int() with base 10: 'string'
End of error message.
Though this allows for customization of the error's print, the code is a little unpythonic.
Using assert
Because in the question you said you wanted to block all strings, you can use assert and isinstance():
obj = "a string" #the input you want to raise the error on
assert not isinstance(obj, str), "Your custom message here."
This prints:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError: Your custom message here.
Though using assert looks clean, the error won't carry as much information because it would be a generic AssertionError. Raising a ValueError tells more information about what caused the error at a glance.
You need to use a try except block to catch the error - see the documentation. Then you could just print a message, and, if necessary, exit the program:
try:
value = int(input("Enter an integer: "))
except ValueError:
print("There has been an error in the system.")
input() # To let the user see the error message
# if you want to then exit the program
import sys
sys.exit(1)
If you want to make an error, you use raise. Here is an example:
raise SyntaxError('MUHAHA THIS IS A ERROR')
If you do not want to add another indentation level by using a try-except block, you can change the handling of all errors by adding the following to the beginning of your code:
import sys
def my_except_hook(exctype, value, traceback):
print('There has been an error in the system')
sys.excepthook = my_except_hook
In case of an error, only your specified error message is printed out. In addition, this prevents the stack trace from being displayed.
Use raise Exception('There has been an error in the system') may be useful for most cases but you may also need create meaningful errors for specific systems.
Eg.
class ExpiredTaxIdException(Exception):
def __init__(self):
Exception.__init__(self, 'Tax ID expired')
Then you can call it on your code, sample:
from your_execeptions_file import ExpiredTaxIdException
class ClientsController:
def is_profile_valid(client):
if client.tax_id.is_valid == False:
raise ExpiredTaxIdException()
return True
# eg. you call it on your API on another place of your code that can't accept invalid tax_id
try:
ClientsController().verify_client(client)
except ExpiredTaxIdException as e:
return {'error': str(e)}
>>> { "error": "Tax ID expired" }
You can try this..
import ctypes
ctypes.windll.user32.MessageBoxW(None, u"CUSTOM MESSAGE", u"TITLE BAR", 0)

Categories