The usual way of handling error cases is using exceptions. A function raises an exception if some requirement condition is not met or something unexpected happens.
Here are some examples of exceptions. You've may have seen many of these already.
If you try to access a variable that is not defined, Python raises a NameError.
no_such_variable
Trying to convert invalid number as int raises a ValueError.
int("bad-number")
Trying to open a non existing file in read mode raises a FileNotFoundError.
open("no-file.txt")
When an exception is raised, it Python exits the program after printing the traceback.
%%file sq2.py
def square(x):
return x*x
def sum_of_squares(x, y):
return square(x) + square(y)
def main():
result = sum_of_squares(3, "4")
print(result)
if __name__ == "__main__":
main()
!python sq2.py
Pay attention to the the traceback above. It prints the stack of the functions active when the exception occurred.
Q: What happens if there are two statements that are causing exceptions. Will both of them be executed?
def f():
print("BEGIN f")
x = 1 + "2"
print("middle line")
y = "1" + 2
print("END f")
return x+y
f()
Exceptions can be handled by using the try-except statements.
For example, the following function reads a file if available otherwise returns empty string.
import sys
def readfile(filename):
"""Returns the contents of the given file.
If the file is not found, empty string is returned.
"""
try:
return open(filename).read()
except FileNotFoundError:
print("WARNING: file {} is not found".format(filename), file=sys.stderr)
return ""
readfile("nofile.txt")
readfile("three.txt")
The except statement takes the class of the exception to be handle. It is even possible to have multiple except blocks for handling multiple errors.
def readint(filename):
try:
text = open(filename).read()
return int(text)
except FileNotFoundError as e:
print("ERROR: File not Found: " + filename, file=sys.stderr)
except ValueError as e:
print("ERROR: Invalid integer: ", repr(text), file=sys.stderr)
return 0
%%file 5.txt
5
readint("5.txt")
readint("nofile.txt")
readint("three.txt")
Exceptions can be raised using raise statement.
class ValidationError(Exception):
pass
def register(username, password):
validate_username(username)
validate_password(password)
# do_registration()
def validate_username(username):
if len(username) < 3:
raise ValidationError("The username must have at least 3 characters")
def validate_password(password):
if len(password) < 8:
raise ValidationError("The password must have at least 8 characters")
register("anand", "pw")
Problem: Write a function safeint to convert given string to an integer. The function should accept two arguments, the string to be converted and a default value. If the given string is not a valid integer, the default value should be returned.
>>> safeint('3', 0)
3
>>> safeint('N/A', 0)
0
# your code here
safeint('3', 0)
safeint('N/A', 0)
Problem: Write a program sumfile.py to compute sum of all the numbers in a file. It is expected that there'll be one number per line. If there are any invalid numbers in the file, they should be ignored after printing a warning.
$ python sumfile.py num.txt
WARNING: Bad Number 'N/A'
WARNING: Bad Number 'xx'
15
%%file sumfile.py
import sys
def safeint(strvalue, default):
try:
return int(strvalue)
except ValueError:
print("WARNING: Invalid number", repr(strvalue))
return default
def sumfile(filename):
numbers = [safeint(line, 0) for line in open(filename)]
return sum(numbers)
def main():
filename = sys.argv[1]
result = sumfile(filename)
print(result)
if __name__ == "__main__":
main()
%%file num.txt
1
2
3
N/A
4
5
xx
%%file good.txt
1
2
3
4
5
!python sumfile.py good.txt
!python sumfile.py num.txt