How to raise an exception and catch it after function execution complete? - python-3.x

I have a line in my code that has the potential to raise an exception, i would like to handle this execption and continue execution.
def foo():
#good code
if thingThatHappensSometimes:
raise CustomException
#code i want to execute
return resultIwant
def bar():
resultIwant = None
try:
#good code
resultIwant = foo()
except CustomException:
#code that should run if an exception was raised
finally:
print(resultIwant)
print('All done!')
My issue here is that there are situations where foo will raise an exception but there is nothing in the code logic preventing it from generating a result for resultIwant. Currently, if I handle the exception in bar I will not reach the end of execution for foo but If I handle the exception in foo the exception will already be handled and I will not reach the except block in bar. is there a way to solve this issue?

raising an exception stops execution at this point, so you cannot both raise an exception and return a value. But you can return a (value, exception_or_none) tuple:
def foo():
error = None
#good code
if thingThatHappensSometimes:
error = CustomException()
#code i want to execute
return resultIwant, error
def bar():
#good code
resultIwant, error = foo()
if error:
#code that should run if an exception was raised
print(resultIwant)
print('All done!')

Related

catch exception while a calling a function from different .py file

I have a main_etl.py and watcher.py
main_etl.py
def test_function():
try:
do sometihng....
except Exception as e:
raise e
def main(arg1='', arg2='', arg3=''):
try:
test_function()
except Exception as e:
raise e
watcher.py
import main_etl
def main_function():
try:
main_etl.main(arg1='', arg2='', arg3='')
except Exception as e:
raise e
else:
do something....
And I am running - python watcher.py
I tried the above code, But it didn't catch any exception in the watcher.py file
I wanted to catch the exception in the calling function script (watcher.py).
Can anyone suggest me like how to catch this error or is there any other way of implementing this.
I know os.system() and subprocess.call(), But how can I call the main() function inside main_etl.py ?

capturing the error when using sys.exit()

Is it possible to capture the errors generated and return them to the caller script?
p1.py
def fun1():
try:
...
except Exception as err:
print('ERROR from p1.py')
sys.exit(1)
p2.py
import p1
def fun2():
try:
...
except Exception as err:
print('ERROR from p2.py')
sys.exit(1)
p3.py
import p2
#want to catch the errors here
errors1 = 'ERROR from p1.py'
errors2 = 'ERROR from p2.py'
Is there a way to get the errors from either p1.py ('ERROR from p1.py') or p2.py ('ERROR from p2.py') in p3.py?
One way could be chaging where you handle the exception when you execute the functions of the p1.py and p2.py file in the p3.py file, you can do a try-except in p3.py(instead of catching the exception inside of every function):
p3.py
from p1 import fun1
from p2 import fun2
# calling the function from p1.py
try:
fun1()
except Exception as err:
print('You have the error from fun1 in p1.py, you can make your logic here')
sys.exit(1)
# calling function from p2.py
try:
fun2()
except Exception as err:
print('You have the error from fun2 in p2.py, you can make your logic here')
sys.exit(1)

why is it not catching index errror?

def dict_items(dict1):
try:
d2={}
for k in dict1.keys():
d2[k+1]=dict1[k]+k
dict1[k]=d2[k]
except IndexError:
print("IndexError")
except ValueError:
print("ValueError")
finally:
print("Finally done")
try:
dict_items({1:1,2:22,3:33})
print("function call done")
except:
print("Exception")
finally:
print("inside finally")
The above executes correctly and prints
Finally done
Exception
inside finally
why is not catching index error is there anything I am missing?
A bare except: isn't very useful. Catch the exception as print its type to see your error:
def dict_items(dict1):
try:
d2={}
for k in dict1.keys():
d2[k+1]=dict1[k]+k
dict1[k]=d2[k]
except IndexError:
print("IndexError")
except ValueError:
print("ValueError")
finally:
print("Finally done")
try:
dict_items({1:1,2:22,3:33})
print("function call done")
except Exception as e:
print("Exception",type(e))
finally:
print("inside finally")
Output:
Finally done
Exception <class 'KeyError'>
inside finally
Dictionaries throw KeyError not IndexError when the key doesn't exist, so the dict_items try block catches nothing and prints Finally done, then the outer try catches the KeyError in the bare except. Exception is the base class of most exceptions so you can see what type and value it has, then the final finally prints inside finally.

How do I catch only a specific error in Python3?

Say I have a function that can produce a vast variety of errors.
I have a ValueError that I need to catch, a specific AttributeError, and then I also need to handle any other type of error.
try:
func()
except AttributeError as e:
if "specific error text" in str(e):
print("The specific AttributeError occurred")
else:
raise
except ValueError:
print("A value error occurred")
except Exception as e:
print("Another error occurred: {}".format(str(e)))
Problem: If func() bubbles an AttributeError that's not the specific one I'm looking for, in this case, it'll be re-raised and not handled how I want it to be handled (via the general Exception handler).
How do I force non-specific errors to be handled further down in the chain, without duplicating code from the Exception section into the AttributeError section?
As an option you can process AttributeError and ValueError in one try-except block and all other Exceptions on the top level like
try:
try:
func()
except AttributeError as e:
if "specific error text" in str(e):
print("The specific AttributeError occurred")
else:
raise
except ValueError:
print("A value error occurred")
except Exception as e:
print("Another error occurred: {}".format(str(e)))
this may look a bit ugly though, so we can extract inner try-except block in a separate function like
def func_with_expected_exceptions_handling():
try:
func()
except AttributeError as e:
if "specific error text" in str(e):
print("The specific AttributeError occurred")
else:
raise
except ValueError:
print("A value error occurred")
and after that
try:
func_with_expected_exceptions_handling()
except Exception as e:
print("Another error occurred: {}".format(str(e)))
this doesn't save us from an actual nested structure, but it may come in handy if this kind of func processing arises in other places.
BTW, I don't think checking for a specific error message in exception is a good idea, we need a little bit more context to see if it can be done easier.
EDIT
If I understood correctly your func looks like
def func(...):
getattr(COMMANDS, cmd.command).command(irc_c, msg, cmd)
and you want to handle error from getattr call.
I can see next options here:
Wrap getattr call in try-except and process AttributeError in-place
def func(...):
try:
commander = getattr(COMMANDS, cmd.command)
except AttributeError:
print('Command {} not found'.format(cmd.command))
else:
commander.command(irc_c, msg, cmd)
Wrap getattr call in try-except, re-raise a custom exception (or ValueError) and process it afterwards in OP try-except
class CommandNotFound(Exception): pass
def func(...):
try:
commander = getattr(COMMANDS, cmd.command)
except AttributeError:
raise CommandNotFound() # or we can use `ValueError` instead
else:
commander.command(irc_c, msg, cmd)
Use default parameter of getattr function and make some kind of logging there like
class DefaultCommand:
def command(self, irc_c, msg, cmd):
print("Command {} is not found".format(cmd.command))
and after that used like
getattr(COMMANDS, cmd.command, DefaultCommand()).command(irc_c, msg, cmd)
Basically you need to handle the specific error first. From more general to more specific, i.e Exception => AttributeError => YourError
>>> try:
... raise MyCustomAttrErr("Hey, this failed!")
... except MyCustomAttrErr as e:
... print(e)
... except AttributteError as e:
... print("Attribute error raised")
... except Exception as e:
... print("Base exception raised")
...
Hey, this failed!
Python handled the except blocks in order from top to bottom and stops in the first block that captures it.

Exception handling with method chaining

class A:
def __init__(self, data):
self.data = data
def first():
# Some functionality
# raise Exception on some condition
return self
def second():
#Some functionality
a = A('test string').first().second()
In this case I want that if first() raises an error then the chaining breaks and that error is raised. Currently it just fails silently.
How can I fix this?
Probably the best you can do is localize the type of errors that are possible in the chain and then ladder the exception handlers:
class A:
def __init__(self, data):
self.data = data
def first(self):
# Some functionality
# raise Exception on some condition
if self.data=='A':
1/0
return self
def second(self):
next(0)
pass
#Some functionality
You would know that first is numerical, and second is an iterable function that might have a TypeError.
So try this:
try:
A('A').first().second()
except ZeroDivisionError as e:
print('Likely first:', e)
except TypeError as e:
print('Likely second:', e)
Would print Likely first: division by zero with A('A') and print Likely second: 'int' object is not an iterator if you use A('something_else'). You would use this construct if you wanted to stop the chain as executed.
You can also add raise to re-raise the error and get Python's diagnosis of when are where:
try:
A('B').first().second()
except ZeroDivisionError as e:
print('Likely first:', e)
raise
except TypeError as e:
print('Likely second:', e)
raise
Prints:
Likely second: 'int' object is not an iterator
Traceback (most recent call last):
File "Untitled 79.py", line 19, in <module>
A('B').first().second()
File "Untitled 79.py", line 14, in second
next(0)
TypeError: 'int' object is not an iterator
Or, better still, use the try and except in each method:
class A:
def __init__(self, data):
self.data = data
def first(self):
try:
1/0
except ZeroDivisionError as e:
print('DEFINITELY first:', e)
return self
def second(self):
try:
next(0)
except TypeError as e:
print('DEFINITELY second:', e)
return self
>>> A('').first().second()
DEFINITELY first: division by zero
DEFINITELY second: 'int' object is not an iterator
I don't think it could fail silently if its actually failing, as this would error out python. However, you might want something like this, maybe it will jog your thinking.
class A:
def __init__(self, data):
self.data = data
def first():
try:
# Some functionality
raise ERROR_YOU WANT_RAISE:
# raise Exception on some condition
except ERROR_TO_CATCH_FROM_RAISE:
# This is where you would handle the exception after its caught
# and break your chaining possibly. Set a flag for second()
# for example that handles the error.
return self
def second():
#Some functionality

Resources