Handling Exceptions

CMPUT 175 Introduction to Foundations of Computing

Exceptions and Handling Errors

  • Post-test: View vignettes: Call Stack, Handling Exceptions, Exceptions Metaphor

Objectives

  • Learn to use Python Exceptions to handle unusual program conditions.

Outline

  • When things go wrong

  • Exceptions

  • Catching exceptions

  • Raising exceptions

  • Defining your own exception classes

  • Assertions

When Things Go Wrong

  • Input Validation: Always check input type, value, interval, etc. to avoid unexpected errors.

  • Handling errors is crucial when things do go wrong to manage situations appropriately.

Reacting to Errors

  • Possible scenarios:

    • User enters a string instead of an integer.

    • Popping from an empty stack.

    • Dividing by zero.

    • Reading from a non-existent file.

    • Network issues when accessing remote locations.

  • Determine whether to produce bad output, crash, or attempt recovery.

Handling Unusual Situations

  • Responsibility often falls on the programmer to deal with errors, leading to convoluted code.

  • Error indications are sometimes returned with unusual values (e.g., -1 or null) which can be hard to interpret.

  • Python provides mechanisms for handling exceptions, improving the coding experience.

What is an Exception?

  • An exception disrupts the normal flow of a program's instructions during execution.

  • It is a Python object that represents an error.

  • Exceptions raise when a situation cannot be coped with at run-time.

Raising Exceptions

  • A Python script raises an exception at the point where the error is detected.

  • Can also explicitly raise exceptions in the program.

Typical Exceptions

  • KeyError: Accessing a non-existent dictionary key.

  • ValueError: Searching a list for a non-existent value.

  • AttributeError: Calling a non-existent method.

  • NameError: Referencing a non-existent variable.

  • TypeError: Mixing datatypes without coercion.

Why Use Exceptions?

  • Advantages of using exceptions include:

    • Separation of error-handling code from regular code.

    • Deferring decisions on how to respond to exceptions.

    • Specifying different kinds of exceptions that can arise.

The Exception Handling Blocks

  • Enclose suspicious code in a try: block.

  • Follow it with an except: statement to handle exceptions.

  • Execution continues normally after the try...except block if no exceptions are raised.

  • Example:

    try:
        f = open('myfile.txt')
    except IOError:
        print("I/O error: File does not exist or cannot be read.")

Catching Exceptions

  • If not caught, the entire program crashes.

  • Multiple exceptions can be caught using a parenthesized tuple:

    except (RuntimeError, TypeError, NameError):
  • A generic except clause catches all other exceptions.

Multiple Except Clauses

  • A try statement can have multiple except clauses for different exceptions.

  • At most one handler executes.

  • Example:

    try:
        f = open('myfile.txt')
        s = f.readline()
        i = int(s.strip())
    except IOError:
        print("I/O error: File does not exist")
    except ValueError:
        print("Could not convert data to an integer.")

The Try Statement Flow

  1. Execute all code in the try clause.

  2. If no exception occurs, skip except.

  3. If an exception occurs, skip remaining try code and execute the matching except clause.

  4. If no matching except is found, the exception propagates up the call stack.

Exception Handling Process

  • Encapsulate code that may raise an exception in a try...except block.

  • If no exceptions arise, code executes normally.

  • If an exception arises, processing halts in the try block, looking for an appropriate except.

Example of Exception Output

  • Example with various outputs from different exception scenarios:

    try:
        alist = 5 * [0]
        last_item = alist[len(alist)]
    except IndexError:
        print('Invalid index')
  • This results in "Invalid index" due to an indexing issue.

Exception Propagation

  • Exceptions bubble up the call stack until caught or terminate the program if uncaught.

  • Example:

    def main():
        try:
            foo()
        except TypeError:
            print('Handled in main')

User-Defined Exceptions

  • Create exceptions by subclassing the standard built-in exceptions for more specific error handling.

  • Example of custom exception classes:

    class MyError(Exception):
        def __init__(self, value):
            self.value = value

Assertions

  • Assertions raise an AssertionError if the condition is not met, providing useful checks for input validity.

  • Implement assertions to validate function inputs and outputs.

  • Example:

    assert (Temperature >= 0), "Colder than absolute zero!"