Catching Outer Exceptions in Python - python-3.x

My code tries to do something, but it triggers an Error... which triggers another Error. So, the error message looks something like this:
SillyError: you can`t do that becuz blablabla
The above exception was the direct cause of the following exception:
LoopyError: you can`t do that becuz blobloblo
I want to create a try except block that only catches this specific duo of errors. However, I am only able to catch the first one, because once I do, the second one never gets a chance to trigger.
This other question is about catching either exception, but I want to catch only if both are triggered in succession. Is there a way?

If you have a try\except, you will always catch the error based on the outer exception. However you do have the option to pass on any exceptions you don't want to process.
In this code, the ZeroDivisionError is caught and wrapped in another exception which is then caught by the calling code. The calling code checks for the inner exception and decides whether to re-raise the exception up the stack.
def xtest():
try:
a = 1/0 # exception - division by zero
except ZeroDivisionError as e:
raise Exception("Outer Exception") from e # wrap exception (e not needed for wrap)
try:
xtest()
except Exception as ex:
print(ex) # Outer Exception
print(ex.__cause__) # division by zero
if (str(ex) == "Outer Exception" and str(ex.__cause__) == "division by zero"):
print("Got both exceptions")
else:
raise # pass exception up the stack
Just for completion, you can do the check based on the exception class name also:
if (type(ex).__name__ == "Exception" and type(ex.__cause__).__name__ == "ZeroDivisionError"):
print("Got both exceptions")
#ShadowRanger pointed out that it may be quicker to just check the class type instead of the class name:
if (type(ex) == Exception and type(ex.__cause__) == ZeroDivisionError):
print("Got both exceptions")

Related

how to throw an error if certain condition evaluates to true

I have below code block:
try:
if str(symbol.args[0]) != str(expr.args[0]):
print('true')
raise SyntaxError('====error')
except:
pass
Here I am trying to raise Syntax error if certain condition is true.I am testing this code block, I can see 'true' is getting printed which means condition is met but even after that also the code is not throwing syntax error.
I am trying to understand what is wrong in the above code.
You're putting pass in the except: block which is swallowing the exception. Either remove the code from the try-except block or change pass to raise
Above answer is pointing the issue, I just want to give some examples to help you better understand how try/except works:
# Just raise an exception (no try/except is needed)
if 1 != 2:
raise ValueError("Values do not match")
# Catch an exception and handle it
a = "1"
b = 2
try:
a += b
except TypeError:
print("Cannot add an int to a str")
# Catch an exception, do something about it and re-raise it
a = "1"
b = 2
try:
a += b
except TypeError:
print("Got to add an int to a str. I'm re-raising the exception")
raise
try/except can also be followed by else and finally, you can check more about these here: try-except-else-finally

Retrieve Python exception object

I have a piece of code that I don't control and while running it raises an error. I'd like to capture the value of exc object inside the exc_func method.
As you can see exc_func raises two exceptions, one of which is handled. What I care about is the value of the exc object, but so far have little luck retrieving it. The value does not exist in exc_traceback object and the exception message is not very helpful.
import traceback
import sys
def exc_func():
try:
a = 1
a.length()
except Exception as exc:
exc.with_not_existing()
def main():
try:
exc_func()
except Exception as exc:
exc_type, exc_value, exc_traceback = sys.exc_info()
tb_walk = traceback.walk_tb(exc_traceback)
# Need this in order to pickle traceback
tb_summary = traceback.StackSummary.extract(tb_walk, capture_locals=True)
if __name__ == '__main__':
main()
EDIT:
For instance, the exc object in main is AttributeError("'AttributeError' object has no attribute 'with_not_existing'"). What I really want to see is the exc object inside exc_func. Just to be clear, I need the exc object itself, something like traceback.format_exc() is not helpful in my case, due to the nature of the exception (it's a C lib that raises this exception)
When an exception is raised during handling another exception, the initial exception is stored as the __context__. It can be extracted when handling the new exception.
try:
exc_func()
except Exception as exc:
parent = exc.__context__ # the previously handled exception
print(type(parent), parent)
Note that an exception handler may also explicitly chain exceptions via __cause__.
Built-in Exceptions
[...]
When raising (or re-raising) an exception in an except or finally clause __context__ is automatically set to the last exception caught; if the new exception is not handled the traceback that is eventually displayed will include the originating exception(s) and the final exception.
The raise statement
[...]
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).
[...]
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:

Can't reach except block in python

I want to loop through a certain line of code until any exception or keyboard interruption occurs. But I can not reach the exception block whenever any exception occurs or due to keyboard interruption.
How can I modify my code so that I could actually reach in case of exception being thrown?
def run():
lidar = RPLidar(PORT_NAME)
iterator = lidar.iter_scans(50000)
time.sleep(2)
environment(iterator)
while True:
try:
print('Hi')
update_line(iterator)
except Exception or KeyboardInterrupt:
print("exception occur. Run again")
#lidar = RPLidar(PORT_NAME)
lidar.stop_motor()
lidar.stop()
lidar.disconnect()
break
if __name__ == '__main__':
run()
I'm surprised that code actually runs. When you say except Exception or KeyboardInterrupt you are saying only take the first thing here that evaluates to True. Since bool(Exception) is True you are only going to catch Exceptions. To catch multiple types of exceptions you would write it like this:
try:
except (Exception, KeyboardInterrupt):
It might not be triggering or non-keyboard exceptions because the exception you are trying to catch derives from BaseException and not Exception. To fix that change Exception to BaseException.
def run():
while True:
try:
print('Hi')
function_doesnt_exist(iterator)
except Exception or KeyboardInterrupt:
print("exception occur. Run again")
break
if __name__ == '__main__':
run()
When I intentionally call function that doesn't exist in your while loop it calls exception : exception occur. Run again
Perhaps you didn't generate an error properly therefore no exception is called
Also , Exception or KeyboardInterrupt means Exception since Exception includes KeyboardInterrupt and u handle them in same manner
so If you just want to catch Keyboard Interrupt then go for:
except KeyboardInterrupt:
pass
Or if you want to handle general exception and Keyboard one different do something like this:
except KeyboardInterrupt:
print("don't press ctrl+C")
pass
except:
print("exception occured")
pass

How can I mock Connection & Timeout Error in Python 3?

I am newbie to Python. I am trying to mock exception in my unit test and test my block of code; however exception message is always empty. Is this below recommended way to mock exception? Also how can I make sure exception message is not empty?
import pytest
import requests
from unittest.mock import patch
#Unit Test
#patch('requests.get')
def test_exception(mock_run):
mock_run.side_effect = requests.exceptions.ConnectionError()
with pytest.raises(SystemExit) as sys_exit:
method_to_test()
assert 'Error ' in str(sys_exit.value) # Here sys_exit.value is always empty
#Method to Test
def method_to_test():
try:
response = requests.get('some_url', verify=False, stream=True)
response.raise_for_status()
except (requests.exceptions.HTTPError,
requests.exceptions.ConnectionError,
requests.exceptions.Timeout) as err:
msg = f'Failure: {err}' # Here err is always empty
raise SystemExit(msg)
Long story short: You don't get a message because you don't specify one.
You probably want to check for 'Failure: ' instead of 'Error: ', since this is what you prefix the original exception message with. This might be the real problem in your code, not the empty string representation of the exception you raise in your test.
Why is str(err) empty?
Take a look at the class hierarchy:
BaseException
Exception
IOError
requests.RequestException
requests.ConnectionError
IOError overrides __str__ if more than one argument is given to the constructor, but otherwise the behavior of BaseException applies:
If str() is called on an instance of this class, the representation of the argument(s) to the instance are returned, or the empty string when there were no arguments.
https://docs.python.org/3/library/exceptions.html#BaseException
>>> import requests
>>> str(requests.exceptions.ConnectionError())
''
>>> str(requests.exceptions.ConnectionError('foo'))
'foo'
>>> str(requests.exceptions.ConnectionError('foo', 'bar'))
'[Errno foo] bar'
The last example is the behavior defined by the IOError exception.
If you want to simulate the exception, raise the exception in your try block, just like you're raising an exception in your except block. When you do this, you can pass a string argument to be the exception message -
raise requests.exceptions.HTTPError("Test this error")
That will filter the message to the except block.
With your example:
def method_to_test():
try:
# ADD EXCEPTION HERE
raise requests.exceptions.HTTPError('Throwing an exception here!')
response = requests.get('some_url', verify=False, stream=True)
response.raise_for_status()
except (requests.exceptions.HTTPError,
requests.exceptions.ConnectionError,
requests.exceptions.Timeout) as err:
# err will now be 'Throwing an exception here!'
msg = f'Failure: {err}' # Here err is always empty
raise SystemExit(msg)

Async socket.send() exception

Hello I have the following for my async loop
async def start_process(restore_items, args, loop):
with GlacierRestorer(args.temp_dir, args.error_log_bucket, loop) as restorer:
restorer.initiate_restore_all(restore_items)
tasks = []
semaphore = asyncio.BoundedSemaphore(4)
for item in restore_items:
tasks.append(asyncio.ensure_future(restorer.transfer(item, semaphore)))
await asyncio.gather(*tasks)
def main():
args = get_args()
restore_items = get_restore_items(args)
for item in restore_items:
print(item.source, ':', item.destination)
try:
loop = asyncio.get_event_loop()
loop.run_until_complete(start_process(restore_items, args, loop))
except KeyboardInterrupt:
pass
My job and files get larger I see that I keep getting an
socket.send() exception
After reading the documentation it seems to be coming from loop.run_until_complete
The exception doesn't come the program to crash, but eventually bogs it down so much it gets stuck printing the exception.
How do I modify the current code to fix this?
run_until_complete only propagates the exception raised inside start_process. This means that if an exception happens at any point during start_process, and start_process doesn't catch it, run_until_complete(start_process()) will re-raise the same exception.
In your case the exception likely originally gets raised somewhere in restorer.transfer(). The call to gather returns the results of the coroutines, which includes raising an exception, if one occurred.
The exception doesn't come the program to crash, but eventually bogs it down so much it gets stuck printing the exception. How do I modify the current code to fix this?
Ideally you would fix the cause of the exception - perhaps you are sending in too many requests at once, or you are using the GlacierRestorer API incorrectly. But some exceptions cannot be avoided, e.g. ones caused by a failing network. To ignore such exceptions, you can wrap the call to restorer.transfer in a separate coroutine:
async def safe_transfer(restorer, item, semaphore):
try:
return await restorer.transfer(item, semaphore)
except socket.error as e:
print(e) # here you can choose not to print exceptions you
# don't care about if doing so bogs down the program
In start_process you would call this coroutine instead of restorer_transfer:
coros = []
for item in restore_items:
coros.append(safe_transfer(restorer, item, semaphore))
await asyncio.gather(*coros)
Note that you don't need to call asyncio.ensure_future() to pass a coroutine to asyncio.gather; it will be called automatically.

Resources