<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Errors and Exceptions
While executing a python program we may encounter errors. There are 2 types of errors
Step1:  Exceptions
Step2:  Built-in Exceptions
Python creates an Exception object whenever a runtime error occurs. There are a number of built-in exceptions.
Step3:  Following are some of the built-in exceptions.
ZeroDivisionError - Raised when you try to divide a number by zero
FileNotFoundError - Raised when a file required does not exist
SyntaxError - Raised when proper syntax is not applied
NameError - Raised when a variable is not found in local or global scope
KeyError - Raised when a key is not found in a dictionary
Handling Exceptions
Python provides 'try/except' statements to handle the exceptions. The operation which can raise exception is placed inside the 'try' statement and code that handles exception is written in the 'except' clause.
Step4:  Catching Specific Exceptions
A try clause can have any number of except clause to capture specific exceptions and only one will be executed in case an exception occurs. We can use tuple of values to specify multiple exceptions in an exception clause
Step5:  The last except clause may omit the exception name(s), to serve as a wildcard. Use this with extreme caution, since it is easy to mask a real programming error in this way! It can also be used to print an error message and then re-raise the exception (allowing a caller to handle the exception as well)
Step6:  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
Step7:  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.
Exception Instances
The except clause may specify a variable after the exception name. The variable is bound to an exception instance with the arguments stored in instance.args. For convenience, the exception instance defines str() so the arguments can be printed directly without having to reference .args.
Step8:  Exception handlers don’t just handle exceptions if they occur immediately in the try clause, but also if they occur inside functions that are called (even indirectly) in the try clause. For example
Step9:  Raise Exceptions
The raise statement allows the programmer to force a specified exception to occur. For example
Step10:  If you need to determine whether an exception was raised but don’t intend to handle it, a simpler form of the raise statement allows you to re-raise the exception
Step11:  User Exceptions
Python has many built-in exceptions which forces your program to output an error when something in it goes wrong.
However, sometimes you may need to create custom exceptions that serves your purpose.
In Python, users can define such exceptions by creating a new class. This exception class has to be derived, either directly or indirectly, from Exception class. Most of the built-in exceptions are also derived form this class.
Step15:  Here, we have created a user-defined exception called CustomError which is derived from the Exception class. This new exception can be raised, like other exceptions, using the raise statement with an optional error message.
Point to Note
When we are developing a large Python program, it is a good practice to place all the user-defined exceptions that our program raises in a separate file. Many standard modules do this. They define their exceptions separately as exceptions.py or errors.py (generally but not always).
Most exceptions are defined with names that end in “Error,” similar to the naming of the standard exceptions.
Step19:  Here, we have defined a base class called Error.
The other two exceptions (ValueTooSmallError and ValueTooLargeError) that are actually raised by our program are derived from this class. This is the standard way to define user-defined exceptions in Python programming.
Many standard modules define their own exceptions to report errors that may occur in functions they define. A detailed example is given below
Step20:  Clean up Actions
The try statement in Python can have an optional finally clause. This clause is executed no matter what, and is generally used to release external resources.
A finally clause is always executed before leaving the try statement, whether an exception has occurred or not. When an exception has occurred in the try clause and has not been handled by an except clause (or it has occurred in an except or else clause), it is re-raised after the finally clause has been executed. 
The finally clause is also executed “on the way out” when any other clause of the try statement is left via a break, continue or return statement.
Step21:  Please note that the TypeError raised by dividing two strings is not handled by the except clause and therefore re-raised after the finally clause has been executed.
Pre Clean up Actions
Some objects define standard clean-up actions to be undertaken when the object is no longer needed.
Look at the following example, which tries to open a file and print its contents to the screen.
Step22:  The problem with this code is that it leaves the file open for an indeterminate amount of time after this part of the code has finished executing. This is not a best practice.
<ASSISTANT_TASK:>
Python Code:
print('Hello)
Explanation: Errors and Exceptions
While executing a python program we may encounter errors. There are 2 types of errors:
Syntax Errors - When you don't follow the proper structure of the python program (Like missing a quote during initialising a string).
Exceptions - Sometimes even when the syntax is correct, errors may occur when the program is run or executed. These run time errors are called Exceptions (like trying to divide by zero or file does not exist).
If Exceptions are not handled properly, the program will crash and come to a sudden & unexpected halt.
Syntax Errors
End of explanation
1 / 0
open('doesnotexistfile.txt')
Explanation: Exceptions
End of explanation
print(locals()['__builtins__'])
Explanation: Built-in Exceptions
Python creates an Exception object whenever a runtime error occurs. There are a number of built-in exceptions.
End of explanation
import sys
def divide(a,b):
    try:
        return a / b
    except: 
        print(sys.exc_info()[0])
        
divide (1,2)
divide (2,0) # This will be captured by the 'except' clause
# print custom error message
def divide(a,b):
    try:
        return a / b
    except: 
        print('Error occured',sys.exc_info()[0])
        
divide (1,2)
divide (2,0) # This will be captured by the 'except' clause
Explanation: Following are some of the built-in exceptions.
ZeroDivisionError - Raised when you try to divide a number by zero
FileNotFoundError - Raised when a file required does not exist
SyntaxError - Raised when proper syntax is not applied
NameError - Raised when a variable is not found in local or global scope
KeyError - Raised when a key is not found in a dictionary
Handling Exceptions
Python provides 'try/except' statements to handle the exceptions. The operation which can raise exception is placed inside the 'try' statement and code that handles exception is written in the 'except' clause.
End of explanation
def divide(a,b):
    try:
        return a / b
    except (ZeroDivisionError): 
        print('Number cannot be divided by zero or non-integer')
    except:
        print('Error Occured',sys.exc_info()[0])
        
divide (1,2)
divide (2,0) # This will be captured by the 'except - zero division error' clause
divide (2,'a') # This will be captured by the generic 'except' clause
def divide(a,b):
    try:
        return a / b
    except (ZeroDivisionError, TypeError): # use a tuple to capture multiple errors
        print('Number cannot be divided by zero or non-integer')
    except:
        print('Error Occured',sys.exc_info()[0])
        
divide (1,2)
divide (2,0) # This will be captured by the 'except - zero division error' clause
divide (2,'a') # This will be captured by the generic 'except' clause
Explanation: Catching Specific Exceptions
A try clause can have any number of except clause to capture specific exceptions and only one will be executed in case an exception occurs. We can use tuple of values to specify multiple exceptions in an exception clause
End of explanation
import sys
try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error: {0}".format(err))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise
Explanation: The last except clause may omit the exception name(s), to serve as a wildcard. Use this with extreme caution, since it is easy to mask a real programming error in this way! It can also be used to print an error message and then re-raise the exception (allowing a caller to handle the exception as well):
End of explanation
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()
Explanation: 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:
End of explanation
try:
    raise Exception('1002','Custom Exception Occured')
except Exception as inst:
    print(type(inst))
    print(inst)
    print(inst.args)
    errno, errdesc = inst.args
    print('Error Number:',errno)
    print('Error Description:',errdesc)
Explanation: 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.
Exception Instances
The except clause may specify a variable after the exception name. The variable is bound to an exception instance with the arguments stored in instance.args. For convenience, the exception instance defines str() so the arguments can be printed directly without having to reference .args.
End of explanation
def func_will_fail():
    return 1 / 0
try:
    func_will_fail()
except ZeroDivisionError as err:
    print('Handling Error - ',err)
Explanation: Exception handlers don’t just handle exceptions if they occur immediately in the try clause, but also if they occur inside functions that are called (even indirectly) in the try clause. For example:
End of explanation
raise NameError('Error Occured')
Explanation: Raise Exceptions
The raise statement allows the programmer to force a specified exception to occur. For example:
End of explanation
try:
    raise NameError('Error Captured')
except NameError:
    print('Captured Exception')
    raise
    
Explanation: If you need to determine whether an exception was raised but don’t intend to handle it, a simpler form of the raise statement allows you to re-raise the exception:
End of explanation
class CustomError(Exception):
    pass
raise CustomError()
raise CustomError('Unexpected Error Occured')
Explanation: User Exceptions
Python has many built-in exceptions which forces your program to output an error when something in it goes wrong.
However, sometimes you may need to create custom exceptions that serves your purpose.
In Python, users can define such exceptions by creating a new class. This exception class has to be derived, either directly or indirectly, from Exception class. Most of the built-in exceptions are also derived form this class.
End of explanation
# define Python user-defined exceptions
class Error(Exception):
   Base class for other exceptions
   pass
class ValueTooSmallError(Error):
   Raised when the input value is too small
   pass
class ValueTooLargeError(Error):
   Raised when the input value is too large
   pass
# our main program
# user guesses a number until he/she gets it right
# you need to guess this number
number = 10
while True:
   try:
       i_num = int(input("Enter a number: "))
       if i_num < number:
           raise ValueTooSmallError
       elif i_num > number:
           raise ValueTooLargeError
       break
   except ValueTooSmallError:
       print("This value is too small, try again!")
       print()
   except ValueTooLargeError:
       print("This value is too large, try again!")
       print()
print("Congratulations! You guessed it correctly.")
Explanation: Here, we have created a user-defined exception called CustomError which is derived from the Exception class. This new exception can be raised, like other exceptions, using the raise statement with an optional error message.
Point to Note
When we are developing a large Python program, it is a good practice to place all the user-defined exceptions that our program raises in a separate file. Many standard modules do this. They define their exceptions separately as exceptions.py or errors.py (generally but not always).
Most exceptions are defined with names that end in “Error,” similar to the naming of the standard exceptions.
End of explanation
class Error(Exception):
    Base class for exceptions in this module.
    pass
class InputError(Error):
    Exception raised for errors in the input.
    Attributes:
        expression -- input expression in which the error occurred
        message -- explanation of the error
    
    def __init__(self, expression, message):
        self.expression = expression
        self.message = message
class TransitionError(Error):
    Raised when an operation attempts a state transition that's not
    allowed.
    Attributes:
        previous -- state at beginning of transition
        next -- attempted new state
        message -- explanation of why the specific transition is not allowed
    
    def __init__(self, previous, next, message):
        self.previous = previous
        self.next = next
        self.message = message
Explanation: Here, we have defined a base class called Error.
The other two exceptions (ValueTooSmallError and ValueTooLargeError) that are actually raised by our program are derived from this class. This is the standard way to define user-defined exceptions in Python programming.
Many standard modules define their own exceptions to report errors that may occur in functions they define. A detailed example is given below:
End of explanation
try:
    raise KeyBoardInterrupt
finally:
    print('Bye')
def divide(a,b):
    try:
        result = a / b
    except ZeroDivisionError:
        print('Number cannot be divided by zero')
    else:
        print('Result',result)
    finally:
        print('Executed Finally Clause')
        
divide(2,1)
divide(2,0)
divide('1','2')
Explanation: Clean up Actions
The try statement in Python can have an optional finally clause. This clause is executed no matter what, and is generally used to release external resources.
A finally clause is always executed before leaving the try statement, whether an exception has occurred or not. When an exception has occurred in the try clause and has not been handled by an except clause (or it has occurred in an except or else clause), it is re-raised after the finally clause has been executed. 
The finally clause is also executed “on the way out” when any other clause of the try statement is left via a break, continue or return statement.
End of explanation
for line in open("myfile.txt"):
    print(line, end="")
Explanation: Please note that the TypeError raised by dividing two strings is not handled by the except clause and therefore re-raised after the finally clause has been executed.
Pre Clean up Actions
Some objects define standard clean-up actions to be undertaken when the object is no longer needed.
Look at the following example, which tries to open a file and print its contents to the screen.
End of explanation
with open("test.txt") as f:
    for line in f:
        print(line, end="")
Explanation: The problem with this code is that it leaves the file open for an indeterminate amount of time after this part of the code has finished executing. This is not a best practice.
End of explanation
<END_TASK>