<jupyter_start><jupyter_text>Handling ExceptionsThis section shows how to handle exception to recover from runtime errors. 1 The Best Error Handling StrategyBecause an exception represents a runtime error, the best error handling strategy is to avoid exceptions. If you design a program carefully, you can avoid many exceptions thus there are no need to handle them.For example, to avoid `ZeroDivisionError`, check the divisor first before a division computation. If the divisor is zero, display an error message and ask the user to correct the problem.To avoid `FileNotFoundError`, check the existence of a file before read from it. Following is a code example.<jupyter_code>import os

READ_MODE = 'r'

filename = input('Please type the filename: ')

if os.path.isfile(filename):
    with open(filename, READ_MODE) as file:
        print(f'Process {filename}')
        # process the file content here
else:
    print(f'{filename} is not a valid file, please check that you input the correct filename.')<jupyter_output><empty_output><jupyter_text>You should avoid runtime errors if possible. Sometimes it is hard to check a runtime error or to display user-friendly error message, you can use the mechanisms introduced in the following sections.  2 Basic `try` StatementYou can use the `try` statement to handle exceptions. The basic `try` statement has a `try` clause and an `except` clause. Following is an example:<jupyter_code>FILENAME = 'test.txt'

try:
    with open(FILENAME) as file: 
        pass # process file data here
except OSError as error:
    print(f'Unable to open file {FILENAME}. Error message: {error}')

print('After the hanlding code, program keeps running')<jupyter_output><empty_output><jupyter_text>Below the  `try:` clause, you can write statements in a code block that is protected by the caluse. If there is no exception in the try-clause code block, the except-cluase is skipped. If there is an exception raised, Python will check if the exception matches with the exception type specified in the `except ExceptionType as variable_name:` clause. If there is a match, the code block in the except-cluase will be executed. Otherwise, the exception is uncaught and Python will stop the execution and print an error message. From a user's perspective, the program crashes when there is an uncaught exception. A file cannot be opened for many reasons: not found, no permission, time out errors etc. The `OSError` can be used to catch these errors and display a user friendly message. If you don't need the error message, you can ignore the `as variable_name` in the except clause. The code will be the following:<jupyter_code>FILENAME = 'test.txt'

try:
    with open(FILENAME) as file: 
        pass # process file data here
except OSError:
    print(f'Unable to open file {FILENAME}')<jupyter_output><empty_output><jupyter_text>3 Multiple `except` ClauseIf the code block in a try-clause has many operations, it could raise many different exceptions. You can use multiple except-clause to catch different exceptions.<jupyter_code>try:
    # all statements in this block is protected
    int("abc")
except OSError as error:
    print(f'Unable to open file {FILENAME}. Error message: {error}')
except ValueError as error:
    print(f'Value error message: {error}')<jupyter_output><empty_output><jupyter_text>Agian, the `as variable_name` is optional if you don't want to access the error message.  4 Catch All ExceptionsIf you want to catch all exception, you can use `except:` without an exception type. You can use it alone or as the last of a sequence of except-clauses.<jupyter_code>import sys

try:
    # all statements in this block is protected
    print('test exceptions')
    1 / 0 # raise an exception
except OSError as error:
    print(f'Unable to open file {FILENAME}. Error message: {error}')
except ValueError as error:
    print(f'Value error message: {error}')
except:
    error = sys.exc_info()[0] # to get error info
    print(f'Unexpected error: {error}')<jupyter_output><empty_output><jupyter_text>The following code doesn't get the erro info and only has a catch-all except-cluase.<jupyter_code>try:
    # all statements in this block is protected
    print('test exceptions')
    1 / 0 # raise an exception
except:
    print(f'Unexpected exception, blame its developer.')<jupyter_output><empty_output><jupyter_text>5 The `else` and `finally` ClauseThe try statement can have two optional clauses. An optional `else` cluase and an optional `finally` clause. Python executes the `else` clause code block if there is no exception raised. The `finally` clause is always executed regardless there is an exception or not. It is often used to run clean up code.<jupyter_code>try:
    print('normal code, no exception.')
    1 / 0
except:
    print('skipped if no exception.')
else:
    print('executed when there is no exception.')
finally:
    print('always execute the finally code block.')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.07053607416364369, 'fraction_numerical': 0.0024183796856106408, 'mean_word_length': 4.639772727272727, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '280489', 'license': 'Apache-2.0', 'n_tokens_mistral': 1362, 'n_tokens_neox': 1325, 'n_words': 658, 'path': '7-exceptions/handling-exception.ipynb', 'repo_name': 'ying-teaching/python', 'size': '4962'}