Chapter 14: Exceptions
The chapter introduces exceptions as Python’s way to handle unusual circumstances during program execution.
After this chapter you will be able to
- Purpose and range of exceptions. Explain that exceptions address unusual circumstances during program execution, most commonly errors, but can serve other purposes; Python supplies exceptions and lets users define their own.
- Exception propagation to handler. Explain how an exception lets a disk-write error reach the top-level save_to_file routine without error-checking code in each lower-level function.
- Costs of status returns. Trace how a disk-write failure would be reported through the word processor’s functions using success/failure status values, and explain why that approach becomes burdensome.
- Raising and catching exceptions. Distinguish raising an exception from catching one, and identify the code that handles a caught exception. Explain that functions can raise exceptions and that your own code can raise them explicitly.
- Type-specific exception handling. Explain why different failures call for different responses and how Python lets a handler catch only specified types of exceptions. Describe what happens when no suitable handler catches an exception.
- Exception objects and control flow. Trace what happens when Python raises an exception: normal execution stops, the current call chain is searched for a suitable handler, and the program aborts with an error message if none is found. Explain that the handler can access the exception object for more information.
- EAFP error handling approach. Distinguish Python’s EAFP approach, which often uses exceptions to deal with errors after they occur, from the LBYL approach of checking for possible errors beforehand. Explain the stated benefit of well-used exceptions for code readability.
- Exception types and inheritance. Explain that different exception types can reflect different causes, and that Python’s exception types are classes arranged in an inheritance hierarchy.
- Exception versus BaseException subclasses. Justify the recommendation to subclass Exception rather than BaseException for user-defined exceptions, using the Ctrl-C example.
- Exception raising and propagation. Explain that Python built-ins can raise exceptions when they detect a problem, and that your code can explicitly raise one with `raise`. Trace a raised exception to the nearest matching catcher, or describe what happens if none is found.
- Matching and handling exceptions. Trace how a try statement runs its body, searches except clauses in order when an exception occurs, and runs the first matching handler. Explain that handling an expected exception can keep the program from crashing.
- Else and finally execution. Distinguish when optional else and finally clauses execute, including what happens after finally if an exception remains unhandled.
- Custom exception creation and use. Explain that a custom exception is a class inheriting from Exception and can be raised, caught, and handled like other exceptions.
- Purpose of exception groups. Explain that an exception group bundles multiple exceptions so they can be handled together, particularly when concurrent programming produces exceptions at practically the same time.
- Handling grouped exceptions. Describe how except* clauses handle exceptions inside an ExceptionGroup: each clause handles matching exceptions, including exceptions that are subclasses of its specified type.
- Assertion failure and explanation. Explain when an assert statement raises AssertionError and what its optional argument is for.
- Disabling assertions for regular use. Describe how assertions can be disabled and why the section recommends leaving debugging assertions in the code.
- Exception hierarchy and handler order. Explain why an except LookupError clause placed before an except IndexError clause catches an IndexError, and how reversing the clauses changes which errors each handles.
- Centralized disk-writing error handling. Explain why the save_to_file function can handle writing errors without adding error-handling code to each subsidiary disk-writing function.
- General versus disk-full errors. Distinguish the IOError handled by the example from a confirmed disk-full condition, and describe the suggested way to tell them apart.
- Exception based formula evaluation. Explain how safe_apply lets spreadsheet formulas use standard Python arithmetic while returning None when a formula cannot be evaluated successfully.
- Error handling with exceptions. Explain why exceptions are a natural choice for handling many error conditions, especially when error handling is added after most of a program is complete.
- Abandoning untenable processing branches. Describe how exceptions are useful when a computational branch becomes untenable and a large amount of its processing must be discarded.
- Context-managed resource cleanup. Explain how a with block manages a file’s lifetime and why it removes the need to write routine cleanup code, even when an operation fails.
- AI Prompt for Adding Exceptions. Describe how to prompt an AI tool to add exceptions to an existing program: provide or point to the previous code and ask it to refactor that code.
What it builds on
- Exceptions (chapter 3) · Introduces exceptions as a Python control-flow feature.
- Closing files (chapter 13) · Closing files after use.
Section by section
Introduction
The chapter introduces exceptions as Python’s way to handle unusual circumstances during program execution.
- Purpose and range of exceptions: Explain that exceptions address unusual circumstances during program execution, most commonly errors, but can serve other purposes; Python supplies exceptions and lets users define their own.
Why it matters: This gives you the basic purpose of exceptions before the chapter explains how to use them.
Key terms: exceptions, errors
14.1 Introduction to exceptions
14.1.1 General philosophy of errors and exception handling
The section uses a disk-full word-processor example to show why exceptions are preferable to ignoring errors or passing failure status through every function call.
- Exception propagation to handler: Explain how an exception lets a disk-write error reach the top-level save_to_file routine without error-checking code in each lower-level function.
Why it matters: This is the reason exceptions can make error-handling code clearer without leaving errors unhandled. - Costs of status returns: Trace how a disk-write failure would be reported through the word processor’s functions using success/failure status values, and explain why that approach becomes burdensome.
Why it matters: It explains the problem that the exception mechanism is meant to solve. - Risk of ignoring errors: Describe why assuming every file write will succeed is an inadequate error-handling strategy for important programs.
Why it matters: The disk-space example shows why a program needs a way to respond when an operation fails.
Key terms: exception mechanism, propagates, save_to_file, success/failure status, status value, plumbing code, disk-space problem, mission-critical programs
14.1.2 A more formal definition of exceptions
This section defines raising an exception, catching it, and the code that handles it.
- Raising and catching exceptions: Distinguish raising an exception from catching one, and identify the code that handles a caught exception. Explain that functions can raise exceptions and that your own code can raise them explicitly.
Why it matters: These terms let you describe where an error originates and which code responds to it.
Key terms: raising, throwing, catching, exception handler, exception-handling code
14.1.3 Handling different types of exceptions
The section explains why Python uses different exception types and how handlers can respond selectively to them.
- Type-specific exception handling: Explain why different failures call for different responses and how Python lets a handler catch only specified types of exceptions. Describe what happens when no suitable handler catches an exception.
Why it matters: It lets a program respond to a disk-space failure differently from a numeric error.
Key terms: types of exceptions, raised, exception handler, catch
14.2 Exceptions in Python
Introduces how Python raises and handles exception objects, and why Python often handles errors after they occur rather than checking for every possibility beforehand.
- Exception objects and control flow: Trace what happens when Python raises an exception: normal execution stops, the current call chain is searched for a suitable handler, and the program aborts with an error message if none is found. Explain that the handler can access the exception object for more information.
Why it matters: This is the basic flow behind Python’s exception-handling code. - EAFP error handling approach: Distinguish Python’s EAFP approach, which often uses exceptions to deal with errors after they occur, from the LBYL approach of checking for possible errors beforehand. Explain the stated benefit of well-used exceptions for code readability.
Why it matters: The distinction explains why Python code may handle a failure instead of checking for it first.
Key terms: exception, exception object, raise statement, call chain, handler, easier to ask forgiveness than permission, EAFP, look before you leap, LBYL
14.2.1 Types of Python exceptions
This section explains how Python exception types form an inheritance hierarchy and why user-defined exceptions should normally inherit from Exception rather than BaseException.
- Exception types and inheritance: Explain that different exception types can reflect different causes, and that Python’s exception types are classes arranged in an inheritance hierarchy.
Why it matters: The hierarchy helps explain how code can handle related exception types. - Exception versus BaseException subclasses: Justify the recommendation to subclass Exception rather than BaseException for user-defined exceptions, using the Ctrl-C example.
Why it matters: The distinction lets ordinary exception handling leave Ctrl-C available to interrupt the code.
Key terms: exception types, exception hierarchy, inheritance, IndexError, LookupError, Exception, BaseException, KeyboardInterrupt, user-defined exceptions, Ctrl-C
14.2.2 Raising exceptions
This section explains how Python and user code raise exceptions, how raised exceptions travel to a catcher, and what their arguments can mean.
- Exception raising and propagation: Explain that Python built-ins can raise exceptions when they detect a problem, and that your code can explicitly raise one with `raise`. Trace a raised exception to the nearest matching catcher, or describe what happens if none is found.
Why it matters: It helps you distinguish where an exception originates from where it is ultimately handled or reported. - Exception constructor arguments: Describe how arguments are used when an exception is created, distinguishing the common message-string convention from what an exception class actually requires.
Why it matters: It prevents you from assuming that every exception constructor takes a human-readable message.
Key terms: raise statement, IndexError, exception catcher, stack, exception(args), string argument, exception type, class definition
14.2.3 Catching and handling exceptions
This section explains how a try statement catches exceptions, what happens when no handler matches, and when else and finally blocks run.
- Matching and handling exceptions: Trace how a try statement runs its body, searches except clauses in order when an exception occurs, and runs the first matching handler. Explain that handling an expected exception can keep the program from crashing.
Why it matters: It lets you respond to an expected failure, such as division by zero, instead of simply letting it crash the program. - Exception handler matching options: Describe how an except clause can bind the caught exception with as, omit the variable, or name a tuple of exception types.
Why it matters: These forms let a handler use the exception value when needed or handle several specified error types together. - Else and finally execution: Distinguish when optional else and finally clauses execute, including what happens after finally if an exception remains unhandled.
Why it matters: The distinction helps you put success-only code in else and cleanup code in finally. - Bare except clause risk: Explain what a final except clause with no exception types catches and why the section generally advises against it.
Why it matters: A broad catch can conceal the very error you need to find.
Key terms: try, except, exception handler, ZeroDivisionError, call chain, as, exception type, tuple of exceptions, else, finally
14.2.4 Defining new exceptions
This section shows how to define a custom exception, pass information with it, and choose an exception hierarchy for a program or library.
- Custom exception creation and use: Explain that a custom exception is a class inheriting from Exception and can be raised, caught, and handled like other exceptions.
Why it matters: You can give errors specific names while using Python’s ordinary exception handling. - Arguments carried by exceptions: Describe what happens to arguments supplied when raising a custom exception: a single uncaught argument appears at the end of the traceback, and a handler can access supplied arguments through the error’s args.
Why it matters: Exception arguments let error-handling code receive details about what went wrong. - Custom exception hierarchy choices: Distinguish the section’s suggested designs: subclass Exception directly for a small program with few unique errors, or use a library-specific exception class as the parent of that library’s exceptions.
Why it matters: An exception hierarchy can keep a library’s related errors organized without complicating a small program.
Key terms: Exception class, exception type, raise, catch, handle, argument, traceback, handler, args, tuple
14.2.5 Exception groups
This section explains why Python groups multiple exceptions and how except* handles the exceptions within a group.
- Purpose of exception groups: Explain that an exception group bundles multiple exceptions so they can be handled together, particularly when concurrent programming produces exceptions at practically the same time.
Why it matters: It explains why Python needs a way to handle several exceptions together. - Handling grouped exceptions: Describe how except* clauses handle exceptions inside an ExceptionGroup: each clause handles matching exceptions, including exceptions that are subclasses of its specified type.
Why it matters: It lets you recognize how separate errors within one group can each reach a matching handler.
Key terms: exception groups, concurrent programming, ExceptionGroup, except*, except, IOError, FileNotFoundError
14.2.6 Debugging programs with the assert statement
This section explains how assert raises an AssertionError during debugging and how optimization disables assertions without runtime cost.
- Assertion failure and explanation: Explain when an assert statement raises AssertionError and what its optional argument is for.
Why it matters: It lets you describe exactly what an assertion checks and what happens when the check fails. - Disabling assertions for regular use: Describe how assertions can be disabled and why the section recommends leaving debugging assertions in the code.
Why it matters: It explains why development checks can stay in the source without imposing a runtime cost when assertions are disabled.
Key terms: assert statement, AssertionError, __debug__, optional argument, -O, -OO, PYTHONOPTIMIZE, assert statements
14.2.7 The exception inheritance hierarchy
This section explains how exception inheritance and clause order determine which except clause catches an error.
- Exception hierarchy and handler order: Explain why an except LookupError clause placed before an except IndexError clause catches an IndexError, and how reversing the clauses changes which errors each handles.
Why it matters: Putting a broader exception handler first can prevent a more specific handler from ever running.
Key terms: IndexError, LookupError, subclass, except clause, inheritance
14.2.8 Example: A disk-writing program in Python
The disk-writing example shows how exception handling can stay outside the main writing sequence and how an I/O error can be distinguished from a disk-full condition.
- Centralized disk-writing error handling: Explain why the save_to_file function can handle writing errors without adding error-handling code to each subsidiary disk-writing function.
Why it matters: This keeps the main writing sequence and its error handling relatively uncluttered. - General versus disk-full errors: Distinguish the IOError handled by the example from a confirmed disk-full condition, and describe the suggested way to tell them apart.
Why it matters: Treating every I/O failure as a full disk could lead the program to handle the wrong problem.
Key terms: save_to_file, error-handling code, subsidiary disk-writing functions, IOError, disk-full problem, except body, call chain
14.2.9 Example: Exceptions in normal evaluation
This section shows how exceptions can support normal spreadsheet calculations by converting cell contents and returning None when a formula cannot be evaluated.
- Spreadsheet cell value conversion: Explain how a spreadsheet cell’s string is converted into a value: a numeric string becomes a float, a blank cell becomes 0, and another nonnumeric string becomes None.
Why it matters: These conversion rules determine what spreadsheet formulas receive from cells. - Exception based formula evaluation: Explain how safe_apply lets spreadsheet formulas use standard Python arithmetic while returning None when a formula cannot be evaluated successfully.
Why it matters: This keeps ordinary arithmetic available even when some cells have invalid values.
Key terms: float, ValueError, None, safe_apply, spreadsheet formulas, standard Python arithmetic operators
14.2.10 Where to use exceptions
This subsection identifies when exceptions are useful: handling errors, including errors addressed late in development, and abandoning processing on an untenable computational branch.
- Error handling with exceptions: Explain why exceptions are a natural choice for handling many error conditions, especially when error handling is added after most of a program is complete.
Why it matters: Error handling is often added late, and exceptions help keep that added code understandable. - Abandoning untenable processing branches: Describe how exceptions are useful when a computational branch becomes untenable and a large amount of its processing must be discarded.
Why it matters: An exception can be useful when continuing a line of computation would be pointless.
Key terms: exceptions, error handling, after-the-fact error-handling code, computational branch, branch-and-bound algorithms, parsing algorithms
14.3 Context managers using the with keyword
This section explains how context managers use with blocks to manage resources, especially closing files after use.
- Context-managed resource cleanup: Explain how a with block manages a file’s lifetime and why it removes the need to write routine cleanup code, even when an operation fails.
Why it matters: It lets you keep a file open for the work that needs it without writing routine closing code. - Multiline with statement formatting: Describe how parentheses can split a with statement across several lines after Python 3.10, and why the section recommends that form for readability.
Why it matters: The formatting option can make a with statement involving multiple files easier to follow.
Key terms: context managers, with keyword, file objects, routine cleanup, with statements, parentheses
14.4 Adding exceptions
This section asks the reader to identify plausible errors in a word-frequency module and refactor its functions to handle them appropriately.
14.4.1 Solving the problem with AI-generated code
This subsection suggests using the previous version of a program as the basis for an AI prompt to add exceptions.
- AI Prompt for Adding Exceptions: Describe how to prompt an AI tool to add exceptions to an existing program: provide or point to the previous code and ask it to refactor that code.
Why it matters: The existing code gives the AI tool a concrete starting point for the requested change.
Key terms: prompt, previous version of the code, exceptions
14.4.2 Solutions and discussion
14.5 Summary
Where this is used later
- Destructors and memory management (chapter 15) · The earlier discussion of context managers using the with keyword.
Concepts explained in this chapter
- exceptionAn exception is an abnormal runtime event represented in Python by an object that can interrupt normal execution and be handled by exception-handling constructs.
- try-catch statementA try-catch statement is a control-flow construct that executes code under exception handling and transfers control to a matching handler when an exception is raised.
- user-defined exceptionA custom exception is a programmer-defined exception class representing an application-specific abnormal condition.
- IndexErrorIndexError is a Python exception raised when a sequence or indexed access is requested outside its valid range.
- type errorTypeError is a Python built-in exception class raised when an operation or function receives a value of an inappropriate type.
- raise statementA raise statement explicitly signals an exception during program execution, optionally supplying an exception instance or class.
- except blockAn except clause is a branch of a Python try statement that handles exceptions matching a specified exception type.
- with statementA with statement is a Python control-flow construct that establishes a context manager-controlled block and performs its exit actions when the block ends.
- inheritanceInheritance is an object-oriented mechanism by which a class derives behavior and structure from another class, forming a hierarchy of related classes.
- NoneNone is Python's singleton object representing the absence of a value.
- floating-point arithmeticA floating-point number is a numeric value represented using a finite-precision floating-point format.
- spreadsheetA spreadsheet file is a file that stores worksheet data, including cells, values, formulas, and related tabular structure.
- PythonPython is a high-level general-purpose programming language with an interpreted execution model and dynamic features.
- refactoringRefactoring is the systematic restructuring of existing source code to improve its internal qualities without changing its externally observable behavior.
- conversational aiA chatbot is software that conducts conversational interactions with users through natural-language input and output.
- promptA prompt is an instruction or input supplied to an AI system to elicit a particular response or generated output.
- else clauseAn else clause is the optional part of a try statement that executes only when its try block completes without raising an exception.
- atomic typeAn atomic type is a type whose supported accesses and operations are indivisible with respect to concurrent execution and provide defined synchronization semantics.
- file objectA data file is a stored file whose contents provide data for retrieval, processing, or analysis.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
