How do I catch only a specific error in Python3? - python-3.x

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.

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 ?

Correct order for exception handling in python

I want to check for an exception and an error in the except statements - WebDriverException and NameError. What would be the correct order of except statements?
Case 1. Check first for WebDriverException and then for NameError
try:
// do something
except WebDriverException:
pass
except NameError:
pass
Case 2. Check first for NameErrorand then for WebDriverException
try:
// do something
except NameError:
pass
except WebDriverException:
pass

logging usability in python

I am implementing a simple logger in python.Find my code below, Sometimes it runs fine,at times I get ValueError at self.log('INFO','Append operation successful.')
Where am I doing wrong? I am new to python and have also referred to the doc. Unable to understand the inconsistency, also I am using python 3.9, tried using force parameter too. It is also not logging the level, like Critical, Info or Error.
import logging as logger
class data:
def __init__(self, filename,filetype,filesize,date):
self.filename=filename
self.filetype=filetype
self.filesize=filesize
self.date=date
def log(self,logtype,msg):
logger.basicConfig(filename="datalog.log",level=logger.INFO,force=True,format='%(asctime)s %(message)s')
if logtype == "INFO":
logger.info(msg)
elif logtype == "ERROR":
logger.ERROR(msg)
elif logtype == "CRITICAL":
logger.CRITICAL(msg)
else:
logger.info(msg)
def openFile(self):
try:
filename=self.filename+self.filetype
f=open(filename,'w+')
f.write("Hello, Lets learn OOPs in Python")
f.close()
self.log('INFO','Write operation successful.')
except Exception as e:
self.log('ERROR','Writing operation failed.')
def appendFile(self):
try:
f=open(self.filename+self.filetype,'a')
f.write("Appending some extra content")
f.close()
self.log('INFO','Append operation successful.')
except Exception as e:
self.log('ERROR','Append operation failed.')
sample = data('sample','.txt','1Mb','2021-08-20')
sample.appendFile()
sample.openFile()
Error:
ValueError: Unrecognised argument(s): force
During handling of the above exception, another exception occurred:
TypeError Traceback (most recent call last)
<ipython-input-1-e095529812fd> in log(self, logtype, msg)
12 logger.info(msg)
13 elif logtype == "ERROR":
---> 14 logger.ERROR(msg)
15 elif logtype == "CRITICAL":
16 logger.CRITICAL(msg)
TypeError: 'int' object is not callable
The force option to basicConfig is only available in Python 3.8+. Check your Python version.
Also, it's probably not a good idea to force reconfigure the logging system on each and every log event. Just configure it once, at the main entry point of your application.

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.

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