Python Data Persistence – Exceptions
Even an experienced programmer’s code does contain errors. If errors pertain to violation of language syntax, more often than not, they are detected by the interpreter (compiler in case of C++/Java) and code doesn’t execute till they are corrected.
There are times though when the code doesn’t show syntax-related errors but errors creep up after running it. What is more, sometimes code might execute without errors and some other times, its execution abruptly terminates. Clearly, some situation that arises in a running code is not tolerable to the interpreter. Such a runtime situation causing the error is called an exception.
Take a simple example of displaying the result of two numbers input by the user. The following snippet appears error-free as far as syntax error is concerned.
num1=int(input('enter a number..')) num2 = int(input(1 enter another number..')) result=num1/num2 print ('result: ' , result)
When executed, above code gives satisfactory output on most occasions, but when num2 happens to be 0, it breaks, (figure 5.3)
enter a number . . 12 enter another number . . 3 result: 4.0 enter a number . . 12 enter another number . . 0 Traceback (most recent call last) : File "E:\python37\tmp.py", line 3, in <module> result =num1/num2 ZeroDivisionError: division by zero
- Python Data Persistence – Installation
- Python Data Persistence – Package
- Java Program to Multiply Two Numbers Using Recursion
You can see that the program terminates as soon as it encounters the error without completing the rest of the statements. Such abnormal termination may prove to be harmful in some cases.
Imagine a situation involving a file object. If such runtime error occurs after the file is opened, the abrupt end of the program will not give a chance for the file object to close properly and it may result in corruption of data in the file. Hence exceptions need to be properly handled so that program ends safely.
If we look at the class structure of builtins in Python, there is an Exception class from which a number of built-in exceptions are defined. Depending upon the cause of exception in a running program, the object representing the corresponding exception class is created. In this section, we restrict ourselves to consider file operations-related exceptions.
Python’s exception handling mechanism is implemented by the use of two keywords – try and except. Both keywords are followed by a block of statements. The try: block contains a piece of code that is likely to encounter an exception. The except block follows the try: block containing statements meant to handle the exception. The above code snippet of the division of two numbers is rewritten to use the try-catch mechanism.
try: num1=int(input('enter a number..')) num2=int(input('enter another number..')) result=num1/num2 print (' result: ' , result) except: print ("error in division") print ("end of program")
Now there are two possibilities. As said earlier, the exception is a runtime situation largely depending upon reasons outside the program. In the code involving the division of two numbers, there is no exception if the denominator is non-zero. In such a case, try: block is executed completely, except block is bypassed and the program proceeds to subsequent statements.
If however, the denominator happens to be zero, a statement involving division produces an exception. Python interpreter abandons rest of statements in try: block and sends the program flow to except: block where exception handling statements are given. After except: block rest of unconditional statements keep on executing, (figure 5.4)
enter a number..15 enter another number..5 result: 3.0 end of program enter a number..15 enter another number..0 error in division end of program
Here, except block without any expression acts as a generic exception handler. To catch objects of a specific type of exception, the corresponding Exception class is mentioned in front of except keyword. In this case, ZeroDivisionError is raised, so it is mentioned in except statement. Also, you can use the ‘as’ keyword to receive the exception object in an argument and fetch more information about the exception.
try: num1=int(input('enter a number..')) num2=int(input('enter another number..')) result =num1/num2 print ('result: ', result) except ZeroDivisionError as e: print ("error message",e) print ("end of program")
File operations are always prone to raising exceptions. What if the file you are trying to open doesn’t exist at all? What if you opened a file in ‘r’ mode but trying to write data to it? These situations will raise runtime errors (exceptions) which must be handled using the try-except mechanism to avoid damage to data in files.
FileNotFoundError is a common exception encountered. It appears when an attempt to read a non-existing file. The following code handles the error.
E:\python37>python tmp.py enter a filename, .testfile.txt H e l l o P y t h o n end of program E:\python37>python tmp.py enter filename, .nofile.txt error message [Errno 2] No such file or directory: ' nofile . txt ' end of program
Another exception occurs frequently when you try to write data in a file opened with ‘r’ mode. Type of exception is UnsupportedOperation defined in the io module.
import io try: f=open ( ' testfile . txt1 , ' r ') f.write('Hello') print (data) except io.UnsupportedOperation as e: print ("error message",e) print ("end of program")
E:\python37 >python tmp.py error message not writable end of program
As we know, the write ( ) method of file objects needs a string argument. Hence the argument of any other type will result in typeError.
try: f=open (' testfile . txt' , ' w') f.write(1234) except TypeError as e: print ("error message",e) print ("end of program")
E:\python37>python tmp.py error message write() argument must be str, not int end of program
Conversely, for file in binary mode, the write () method needs bytes object as argument. If not, same TypeError is raised with different error message.
try: f =open ( ' testfile . txt' , ' wb' ) f.write('Hello1) except TypeError as e: print ("error message",e) print ("end of program")
E:\python37>python tmp.py error message a bytes-like object is required, not 'str' end of program
All file-related functions in the os module raise OSError in the case of invalid or inaccessible file names and paths, or other arguments that have the correct type but are not accepted by the operating system.
import os try: fd=os . open ( ' testfile . txt' , os . 0_RD0NLY | os . 0_CREAT) os.write(fd,'Hello'.encode()) except OSError as e: print ("error message",e) print ("end of program")
E:\python37>python tmp.py error message [Errno 9] Bad file descriptor end of program
In this chapter, we learned the basic file handling techniques. In the next chapter, we deal with advanced data serialization techniques and special-purpose file storage formats using Python’s built-in modules.