Exception Handling: How to organize my code for specific question? - python

So, I'm currently taking an intro to programming class and we started learning about files and exception handling. I'm having trouble with a question that is based on exception handling.
This question is asking me to read the data from population.txt, checks to make sure the file exists and is in the right format (if it isn’t throw an appropriate exception), calculates the combined population of the countries in the text file and prints that total.
This is how the file looks like
Afghanistan:32738376
Albania:3619778
Algeria:33769669
Jamaica:2804332
Japan:127288419
Paraguay:6831306
Peru:29180899
Philippines:92681453
Pitcairn Islands:48
Tajikistan:7211884
Turkmenistan:5179571
Zambia:11669534
Zimbabwe:12382920
I did something like this. As you can see, I am confused as to how to organize my code, but I know I am onto something. Should I have put the if statements under the except clause? How would you guys have done this?
try:
infile=open("population..txt","r")
for line in infile:
line=line.rstrip()
wordList=line.split(":")
if type(word[0])!=str:
raise ValueError("This is not a string")
elif type(word[1])!=int:
raise ValueError("This not an integer")
else:
pass
except IOError as e:
print(e)
else:
finally:
infile.close()

Exceptions need to be raised inside a try block to be catched. That part of the code is correct. You need however to catch all the different exceptions that may be raised and definitely in this case you do not need to raise them by yourself:
try:
totpopulation = 0
with open("population.txt","r") as infile:
for line in infile:
line=line.rstrip()
wordList=line.split(":")
popul = int(wordList[1])
totpopulation += popul
print("Total population:", totpopulation)
except (OSError, ValueError, IndexError) as e:
print(e)
As you see here the raise statement is not used explicitly: exceptions are raised by the built-in functions when their argument is not valid:
OSError is the class of the exception raised by open if the file does not exists.
ValueError is the class of the exception raised by int(wordList[1]) if the string wordList[1] cannot be converted to an integer.
IndexError is raised by wordlist[1] if the length of wordlist is 0, hence Index out of range. It may happen if a line is not splitted because ':' is missing.
It seems to me that you do not need other exception classes in this code. However the list of all the built-in exceptions is in the docs, if you wish to know what other exceptions are available.

You can use With Statement Context Managers to avoid exceptions while reading data from the file.
with open('text_file', 'r') as f:
# pass your code there

Related

Difference between bare except and specifying a specific exception

In this piece of code, I could write a simple except clause without writing Exception in front of it. I mean the last line could be like this :
except:
print('Hit an exception other than KeyError or NameError!')
What is the point of writing Exception in front of an except clause ?
try:
discounted_price(instrument, discount)
except KeyError:
print("There is a keyerror in your code")
except NameError:
print('There is a TypeError in your code')
except Exception:
print('an exception occured')
I tried writing an except clause without Exception keyword and it worked the same.
Thank you guys for your answers . I know the point of catching specific errors. If I want to ask more clearly , what is the difference between two clauses :
except Exception:
print('an exception occured')
except :
print('an exception occured')
The point of specifying the Exception is that ONLY that Exception will be caught, if you do not specify any Exception, then ALL Errors and Exceptions will be caught potentially masking bugs.
For example let's say that I want to read a file if it exists or otherwise print a message to the user, I can write:
try:
with ope("example.txt", "r") as f:
print(f.read())
except:
print("File not found")
But while this code runs fine without raising any exceptions, this code will never read the file, even if it exists!!!
This is because I wrote ope instead of open and the NameError was caught by my bare except, If I write the except in the correct way:
try:
with ope("example.txt", "r") as f:
print(f.read())
except FileNotFoundError:
print("File not found")
Now I can properly debug my code:
Traceback (most recent call last):
File "/home/riccardo/types.py", line 4, in <module>
with ope("example.txt", "r") as f:
NameError: name 'ope' is not defined
A bare expect
try:
...
except:
pass
or catching any exception whatsoever
try:
...
except Exception:
pass
are bad practice, because you can be hiding bug or be interfering with the normal procedure of the program.
You should only catch exception that you know how to handle, everything else you should let it propagate.
For some example:
Hide bug: it can hide some typo in your code as Caridorc example show making you think that you had a problem different than the real problem
Interfering with the normal procedure: you can make it an unexpectedly unkillable program or get in the way of the normal procedure of the program by discarding an exception that another part of the code was expecting.
like for example
while True:
try:
print("running")
except:
print("I'm immortal muahahaha")
this piece of code now you can't stop with you usual control-z (control-z throw and KeyboardInterrupt exception into your program) so you now need to close the whole interpreter/kill it with the task admin just to stop it, and if this was unintended you just introduced a new bug and depending on what you're doing it can be catastrophic.
To illustrate how catastrophic it can be, consider the following hypothetical case: imagine you make so benign function for a medical device and you put something like this
try:
...
except:
print("some error happens")
now it just so happens that while you piece of code was running a HeartAttack exception was raised and your catch it all and ignore piece of code will do, well, just that, and here is the twist this device was a pacemaker... well, congratulation you just killed the poor guy.
And that is why you should only catch the exception you know how to deal with, everything else you let it pass and hope that somebody along the line know how to deal with it, like in the example above, you and your piece of code don't know how to deal with a HeartAttack, but the pacemaker do and the pacemaker was the one that call your piece of code let it deal with it...
for a no so extreme example consider this simple code
def get_number_from_user():
while True:
try:
return int(input("write a number: "))
except:
print("not a valid number try again")
if your user was done with your program and this happens to be the thing running he/she might want to kill it with a control-z as you usually do with any program, but it will find that it doesn't work, the correct way here is to catch the error we know how to deal with in this case, namely ValueError, everything else isn't this function business
def get_number_from_user():
while True:
try:
return int(input("write a number: "))
except ValueError:
print("not a valid number try again")
You also ask about the difference between
try:
...
except:
pass
and this
try:
...
except Exception:
pass
the difference is that a bare except can catch any and all kind of exception, that in python is anything that is or inherit from BaseException that sit at the top of the exception hierarchy, while except Exception will catch only Exception itself or anything that inherit from it (the same apply for any particular exception you put there), this small distinction allow to make some exceptions more special than other, like the aforementioned KeyboardInterrupt that inherit from BaseException instead of Exception, and that is used to signal that the user wants to terminate this program, so you should do so and this distinction is made basically so new programmers don't shoot themselves in the foot when using except Exception
Just to add to the answer provided by #Caridorc, by specifying each error separately, you can run specific error handling code pertaining to that error when the exception arises. If for example the file does not exist, you can print message to that effect. If however, it fails to print because you mistyped g instead of f, you can print a message to say that the variable is not recognised ( separate code for separate error captures). For exmple:
g = 100 # Unrelated variable declared previously
try:
with open("example.txt", "r") as f:
x=print(f.read())
except FileNotFoundError:
print("File not found")
except AttributeError:
print("Reading wrong variable")
except Exception as e:
print("Unknown Error", e)
Note also the last exception except Exception as e:. This is the same as just except: but allows you to handle all other errors that do not fit onto previous captures and retrieve e - the error message that is generated by compiler. There is effectively no difference between except: and except Exception: in terms of execution
Consider the code:
a = 5
b = 0
x = a / b
Executing this will alert you to the fact that you have attempted to divide a float by zero but it will crash your code.
Now consider:
a = 5
b = 0
try:
x = a / b
except: # or 'except Exception:'
print("An exception was raised")
This will raise an exception that is handled by printing a message that an error occurred. You code will not crash but you do not know how to properly handle the code because you do not know what the exception was, just that one occurred.
Now consider:
a = 5
b = 0
try:
x = a / b
except Exception as e:
print("An exception was raised, generating error", e)
Now your code does not crash. Is handled and you know what the error was.
The purpose of writing "Exception" in front of an except clause is to catch all possible exceptions that can occur in the code. By specifying "Exception", you are telling the interpreter to handle any type of exception that might be raised. The more specific the exception specified in the except clause, the more targeted the handling of the exception can be. For example, if you only want to handle "KeyError" exceptions, you can specify that explicitly in the except clause, as in the first example.

python ignore whichever line that causes error and continue running the code after that line

I understand the try/except method. What I'm trying to do is:
try:
some_func1() #potentially raises error too
do_something_else() #error was raised
continue_doing_something_else() #continues here after handling error
except:
pass
In the above code, when the error is raised at do_something_else(), the error is handled but then the try statement is exited.
What I want is for python to continue whatever code that is after the line that causes the error. Assuming that an error can happen anywhere in the try statement so I can't just wrap the try/except around do_something_else() itself, is there a way to do this in python?
What you want to do (try with restart) is not possible in Python. Lisp can do it (http://www.gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html), and you can implement it in Scheme using call/cc.
Just put the code that you want to do after the possible exception after the except. You may want to use finally (see https://docs.python.org/3/tutorial/errors.html for the documentation):
try:
some_func1()
do_something_else() #error was raised
except:
handle_exception()
finally:
continue_doing_something_else() #continues here after handling error or if no error occurs
if continue_doing_something_else() can also throw exceptions, then put that in a try/except too:
try:
some_func1()
do_something_else() #error was raised
except:
handle_exception1()
try:
continue_doing_something_else()
except:
handle_exception2()
finally:
any_cleanup()
as a general rule, your exception handling should be kept as small in scope as possible while remaining sensible, consider excepting only the 'expected' exceptions and not all of them (e.g. except OSError: when attempting to open files)

How to delete file created in with statement if with-block fails (python)

I have some code like this:
with open(output_path, 'w') as output_file:
#create and write output file
Upon running this, then even if there is an error somewhere, the file has been created and is in an incomplete state .
I would like it, that if there is an exception raised (and not handled) in the with-block, then the file be deleted. What's the best way to do this?
My understanding is if I try to do it in with a (try ...) finally statement, it will happen even if there is no exception, while if I put it after an except statement, the exception will not continue to bubble up, which is what I want it to do. (I don't want to handle the exception, just delete the file before the code stops running.)
You can simply call raise within your except block, and it will re-raise the exception to allow the exception to bubble up. See this relevant section of python documentation.
For your example, you could write something similar to the following:
try:
with open(output_path, 'w') as output_file:
# Create and write output file.
except:
# Delete the file.
# Re-raise the exception with all relevant information (message, stacktrace, etc).
raise

Python reraise/recatch exception

I would like to know if it is possible in python to raise an exception in one except block and catch it in a later except block. I believe some other languages do this by default.
Here is what it would look like"
try:
something
except SpecificError as ex:
if str(ex) = "some error I am expecting"
print "close softly"
else:
raise
except Exception as ex:
print "did not close softly"
raise
I want the raise in the else clause to trigger the final except statement.
In actuality I am not printing anything but logging it and I want to log more in the case that it is the error message that I am not expecting. However this additional logging will be included in the final except.
I believe one solution would be to make a function if it does not close softly which is called in the final except and in the else clause. But that seems unnecessary.
What about writing 2 try...except blocks like this:
try:
try:
something
except SpecificError as ex:
if str(ex) == "some error I am expecting"
print "close softly"
else:
raise ex
except Exception as ex:
print "did not close softly"
raise ex
Only a single except clause in a try block is invoked. If you want the exception to be caught higher up then you will need to use nested try blocks.
As per python tutorial there is one and only one catched exception per one try statement.
You can find pretty simple example in tutorial that will also show you how to correctly use error formatting.
Anyway why do you really need second one? Could you provide more details on this?
You can do this using the six package.
Six provides simple utilities for wrapping over differences between Python 2 and Python 3.
Specifically, see six.reraise:
Reraise an exception, possibly with a different traceback. In the simple case, reraise(*sys.exc_info()) with an active exception (in an except block) reraises the current exception with the last traceback. A different traceback can be specified with the exc_traceback parameter. Note that since the exception reraising is done within the reraise() function, Python will attach the call frame of reraise() to whatever traceback is raised.

What is the intended use of the optional "else" clause of the "try" statement in Python?

What is the intended use of the optional else clause of the try statement?
The statements in the else block are executed if execution falls off the bottom of the try - if there was no exception. Honestly, I've never found a need.
However, Handling Exceptions notes:
The use of the else clause is better
than adding additional code to the try
clause because it avoids accidentally
catching an exception that wasn’t
raised by the code being protected by
the try ... except statement.
So, if you have a method that could, for example, throw an IOError, and you want to catch exceptions it raises, but there's something else you want to do if the first operation succeeds, and you don't want to catch an IOError from that operation, you might write something like this:
try:
operation_that_can_throw_ioerror()
except IOError:
handle_the_exception_somehow()
else:
# we don't want to catch the IOError if it's raised
another_operation_that_can_throw_ioerror()
finally:
something_we_always_need_to_do()
If you just put another_operation_that_can_throw_ioerror() after operation_that_can_throw_ioerror, the except would catch the second call's errors. And if you put it after the whole try block, it'll always be run, and not until after the finally. The else lets you make sure
the second operation's only run if there's no exception,
it's run before the finally block, and
any IOErrors it raises aren't caught here
There is one big reason to use else - style and readability. It's generally a good idea to keep code that can cause exceptions near the code that deals with them. For example, compare these:
try:
from EasyDialogs import AskPassword
# 20 other lines
getpass = AskPassword
except ImportError:
getpass = default_getpass
and
try:
from EasyDialogs import AskPassword
except ImportError:
getpass = default_getpass
else:
# 20 other lines
getpass = AskPassword
The second one is good when the except can't return early, or re-throw the exception. If possible, I would have written:
try:
from EasyDialogs import AskPassword
except ImportError:
getpass = default_getpass
return False # or throw Exception('something more descriptive')
# 20 other lines
getpass = AskPassword
Note: Answer copied from recently-posted duplicate here, hence all this "AskPassword" stuff.
Python try-else
What is the intended use of the optional else clause of the try statement?
The intended use is to have a context for more code to run if there were no exceptions where it was expected to be handled.
This context avoids accidentally handling errors you did not expect.
But it's important to understand the precise conditions that cause the else clause to run, because return, continue, and break can interrupt the control flow to else.
In Summary
The else statement runs if there are no exceptions and if not interrupted by a return, continue, or break statement.
The other answers miss that last part.
From the docs:
The optional else clause is executed if and when control flows off the
end of the try clause.*
(Bolding added.) And the footnote reads:
*Currently, control “flows off the end” except in the case of an
exception or the execution of a return, continue, or break statement.
It does require at least one preceding except clause (see the grammar). So it really isn't "try-else," it's "try-except-else(-finally)," with the else (and finally) being optional.
The Python Tutorial elaborates on the intended usage:
The try ... except statement has an optional else clause, which, when
present, must follow all except clauses. It is useful for code that
must be executed if the try clause does not raise an exception. For
example:
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except OSError:
print('cannot open', arg)
else:
print(arg, 'has', len(f.readlines()), 'lines')
f.close()
The use of the else clause is better than adding additional code to
the try clause because it avoids accidentally catching an exception
that wasn’t raised by the code being protected by the try ... except
statement.
Example differentiating else versus code following the try block
If you handle an error, the else block will not run. For example:
def handle_error():
try:
raise RuntimeError('oops!')
except RuntimeError as error:
print('handled a RuntimeError, no big deal.')
else:
print('if this prints, we had no error!') # won't print!
print('And now we have left the try block!') # will print!
And now,
>>> handle_error()
handled a RuntimeError, no big deal.
And now we have left the try block!
One use: test some code that should raise an exception.
try:
this_should_raise_TypeError()
except TypeError:
pass
except:
assert False, "Raised the wrong exception type"
else:
assert False, "Didn't raise any exception"
(This code should be abstracted into a more generic test in practice.)
Try-except-else is great for combining the EAFP pattern with duck-typing:
try:
cs = x.cleanupSet
except AttributeError:
pass
else:
for v in cs:
v.cleanup()
You might think this naïve code is fine:
try:
for v in x.cleanupSet:
v.clenaup()
except AttributeError:
pass
This is a great way of accidentally hiding severe bugs in your code. I typo-ed cleanup there, but the AttributeError that would let me know is being swallowed. Worse, what if I'd written it correctly, but the cleanup method was occasionally being passed a user type that had a misnamed attribute, causing it to silently fail half-way through and leave a file unclosed? Good luck debugging that one.
I find it really useful when you've got cleanup to do that has to be done even if there's an exception:
try:
data = something_that_can_go_wrong()
except Exception as e: # yes, I know that's a bad way to do it...
handle_exception(e)
else:
do_stuff(data)
finally:
clean_up()
Even though you can't think of a use of it right now, you can bet there has to be a use for it. Here is an unimaginative sample:
With else:
a = [1,2,3]
try:
something = a[2]
except IndexError:
print("out of bounds")
else:
print(something)
Without else:
try:
something = a[2]
except IndexError:
print("out of bounds")
if "something" in locals():
print(something)
Here you have the variable something defined if no error is thrown. You can remove this outside the try block, but then it requires some messy detection if a variable is defined.
There's a nice example of try-else in PEP 380. Basically, it comes down to doing different exception handling in different parts of the algorithm.
It's something like this:
try:
do_init_stuff()
except:
handle_init_suff_execption()
else:
try:
do_middle_stuff()
except:
handle_middle_stuff_exception()
This allows you to write the exception handling code nearer to where the exception occurs.
From Errors and Exceptions # Handling exceptions - docs.python.org
The try ... except statement has an optional else clause, which,
when present, must follow all except clauses. It is useful for code
that must be executed if the try clause does not raise an exception.
For example:
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except IOError:
print 'cannot open', arg
else:
print arg, 'has', len(f.readlines()), 'lines'
f.close()
The use of the else clause is better than adding additional code to
the try clause because it avoids accidentally catching an exception
that wasn’t raised by the code being protected by the try ... except
statement.
try:
statements # statements that can raise exceptions
except:
statements # statements that will be executed to handle exceptions
else:
statements # statements that will be executed if there is no exception
Example :
try:
age=int(input('Enter your age: '))
except:
print ('You have entered an invalid value.')
else:
if age <= 21:
print('You are not allowed to enter, you are too young.')
else:
print('Welcome, you are old enough.')
The Output :
>>>
Enter your age: a
You have entered an invalid value.
>>> RESTART
>>>
Enter your age: 25
Welcome, you are old enough.
>>> RESTART
>>>
Enter your age: 13
You are not allowed to enter, you are too young.
>>>
Copied from : https://geek-university.com/python/the-try-except-else-statements/
Looking at Python reference it seems that else is executed after try when there's no exception.
The optional else clause is executed if and when control flows off the end of the try clause. 2 Exceptions in the else clause are not handled by the preceding except clauses.
Dive into python has an example where, if I understand correctly, in try block they try to import a module, when that fails you get exception and bind default but when it works you have an option to go into else block and bind what is required (see link for the example and explanation).
If you tried to do work in catch block it might throw another exception - I guess that's where the else block comes handy.
That's it. The 'else' block of a try-except clause exists for code that runs when (and only when) the tried operation succeeds. It can be used, and it can be abused.
try:
fp= open("configuration_file", "rb")
except EnvironmentError:
confdata= '' # it's ok if the file can't be opened
else:
confdata= fp.read()
fp.close()
# your code continues here
# working with (possibly empty) confdata
Personally, I like it and use it when appropriate. It semantically groups statements.
I would add another use case that seems straight forward when handling DB sessions:
# getting a DB connection
conn = db.engine.connect()
# and binding to a DB session
session = db.get_session(bind=conn)
try:
# we build the query to DB
q = session.query(MyTable).filter(MyTable.col1 == 'query_val')
# i.e retrieve one row
data_set = q.one_or_none()
# return results
return [{'col1': data_set.col1, 'col2': data_set.col2, ...}]
except:
# here we make sure to rollback the transaction,
# handy when we update stuff into DB
session.rollback()
raise
else:
# when no errors then we can commit DB changes
session.commit()
finally:
# and finally we can close the session
session.close()
Perhaps a use might be:
#debug = []
def debuglog(text, obj=None):
" Simple little logger. "
try:
debug # does global exist?
except NameError:
pass # if not, don't even bother displaying
except:
print('Unknown cause. Debug debuglog().')
else:
# debug does exist.
# Now test if you want to log this debug message
# from caller "obj"
try:
if obj in debug:
print(text) # stdout
except TypeError:
print('The global "debug" flag should be an iterable.')
except:
print('Unknown cause. Debug debuglog().')
def myfunc():
debuglog('Made it to myfunc()', myfunc)
debug = [myfunc,]
myfunc()
Maybe this will lead you too a use.
I have found the try: ... else: construct useful in the situation where you are running database queries and logging the results of those queries to a separate database of the same flavour/type. Let's say I have lots of worker threads all handling database queries submitted to a queue
#in a long running loop
try:
query = queue.get()
conn = connect_to_db(<main db>)
curs = conn.cursor()
try:
curs.execute("<some query on user input that may fail even if sanitized">)
except DBError:
logconn = connect_to_db(<logging db>)
logcurs = logconn.cursor()
logcurs.execute("<update in DB log with record of failed query")
logcurs.close()
logconn.close()
else:
#we can't put this in main try block because an error connecting
#to the logging DB would be indistinguishable from an error in
#the mainquery
#We can't put this after the whole try: except: finally: block
#because then we don't know if the query was successful or not
logconn = connect_to_db(<logging db>)
logcurs = logconn.cursor()
logcurs.execute("<update in DB log with record of successful query")
logcurs.close()
logconn.close()
#do something in response to successful query
except DBError:
#This DBError is because of a problem with the logging database, but
#we can't let that crash the whole thread over what might be a
#temporary network glitch
finally:
curs.close()
conn.close()
#other cleanup if necessary like telling the queue the task is finished
Of course if you can distinguish between the possible exceptions that might be thrown, you don't have to use this, but if code reacting to a successful piece of code might throw the same exception as the successful piece, and you can't just let the second possible exception go, or return immediately on success (which would kill the thread in my case), then this does come in handy.
Here is another place where I like to use this pattern:
while data in items:
try
data = json.loads(data)
except ValueError as e:
log error
else:
# work on the `data`
An else block can often exist to complement functionality that occurs in every except block.
try:
test_consistency(valuable_data)
except Except1:
inconsistency_type = 1
except Except2:
inconsistency_type = 2
except:
# Something else is wrong
raise
else:
inconsistency_type = 0
"""
Process each individual inconsistency down here instead of
inside the except blocks. Use 0 to mean no inconsistency.
"""
In this case, inconsistency_type is set in each except block, so that behaviour is complemented in the no-error case in else.
Of course, I'm describing this as a pattern that may turn up in your own code someday. In this specific case, you just set inconsistency_type to 0 before the try block anyway.
One of the use scenarios I can think of is unpredictable exceptions, which can be circumvented if you try again. For instance, when the operations in try block involves random numbers:
while True:
try:
r = random.random()
some_operation_that_fails_for_specific_r(r)
except Exception:
continue
else:
break
But if the exception can be predicted, you should always choose validation beforehand over an exception. However, not everything can be predicted, so this code pattern has its place.
I have found else useful for dealing with a possibly incorrect config file:
try:
value, unit = cfg['lock'].split()
except ValueError:
msg = 'lock monitoring config must consist of two words separated by white space'
self.log('warn', msg)
else:
# get on with lock monitoring if config is ok
An exception reading the lock config disables lock monitoring and ValueErrors log a helpful warning message.
Suppose your programming logic depends on whether a dictionary has an entry with a given key. You can test the result of dict.get(key) using if... else... construct, or you can do:
try:
val = dic[key]
except KeyError:
do_some_stuff()
else:
do_some_stuff_with_val(val)
One could use this construct for handling exceptions in a common way within the finally clause, while doing something else when there's no exception:
class TooManyRetries(RuntimeError):
pass
n_tries = 0
max_retries = 2
while True:
try:
n_tries += 1
if n_tries >= max_retries:
raise TooManyRetries
fail_prone_operation()
except Exception1 as ex:
# handle1
except Exception2 as ex:
# handle2
except Exception3 as ex:
# handle3
except TooManyRetries as ex:
raise
else: # No exception
n_tries = 0
finally:
common_restore_state()
continue
I may have missed it in the dozens of answers, but I prefer to minimize the LOC in a try: block, but I like how finally: can clean up a code segment. The else: block provides a clean way to accommodate that for file I/O, database work, etc., lots of examples above.
The else: block is confusing and (nearly) useless. It's also part of the for and while statements.
Actually, even on an if-statement, the else: can be abused in truly terrible ways creating bugs that are very hard to find.
Consider this.
if a < 10:
# condition stated explicitly
elif a > 10 and b < 10:
# condition confusing but at least explicit
else:
# Exactly what is true here?
# Can be hard to reason out what condition is true
Think twice about else:. It is generally a problem. Avoid it except in an if-statement and even then consider documenting the else- condition to make it explicit.

Categories