Hedronite · Cert-Prep Synthesis Lesson · Python Institute · PCAP (python_day #0, PCAP-emphasis) · Track Python Day 2 · Fri 2026-07-24
PCAP · Exceptions try / except / else / finally Built-in Hierarchy raise · propagation

PCAP — Exceptions and Error Handling — what runs, in what order, and why

The exam does not test whether you can write a try block. It tests whether you know exactly what runs when one does or does not raise.

Lesson Class: Cert-Prep (PCAP — Python Institute)
Track / Day: Python (Deep Python) — round-robin day 2, python_day #0 → PCAP-emphasis
Objective: Exceptions and Error Handling (try/except/else/finally · hierarchy · raise)
Word Count: ~2,320
Grounding: Learning Python (Lutz) — Ch 33 Unified try/except/finally · Ch 34 Built-in Exception Classes
Paired Ops: Resilient Python Automation — Retry, Degrade, or Fail Loud
Paired Dev: Python's Exception Model in Depth
Discipline: ROD v3 · aether-accent meta-card · q-card practice questions

A finally does not stop an exception. It runs its cleanup, and then the exception continues on its way. Half the exam's traps live in that one sentence.

§ IFrame

The PCAP exam does not test whether you can write a try block. It tests whether you know exactly what runs, in what order, when one does or does not raise. That is a narrower and harder thing. Most working programmers use exceptions daily and could not say with certainty whether the else clause runs when the try raises, or whether finally runs when the except itself raises, or which handler wins when two could match. The exam asks precisely those questions, because they are where real bugs live, and the answers are all determined by fixed rules with no ambiguity once you know them.

This is the PCAP-emphasis face of today's trio. The Ops lesson decided which of three responses each failed step deserves. The Dev lesson went into chaining, hierarchies, and exception groups. This lesson drills the statement itself — the four clauses of try, the built-in exception tree, and what raise does — because those mechanics are the ground both other lessons stood on, and they are exactly the PCAP objective. Learn the rules cold here and the exam question becomes a lookup.

§ IIDomain Foundations — The Four Clauses and Their Order

A try statement has up to four kinds of clause, and each answers a different question. The try block holds the code that might raise. An except block handles a raised exception. The else block runs only if the try finished with no exception. The finally block runs on the way out no matter what happened. Lutz sets out the exact ordering rule in the chapter that grounds this lesson: the parts must appear as try, then zero or more except, then an optional else, then an optional finally, and an else may appear only if at least one except is present.

The order in which they run under different outcomes is the whole subject, so hold it precisely. If the try block raises, Python stops the try immediately and looks for a matching except; the else does not run because the try did not finish clean. If the try block does not raise, every except is skipped and the else runs. Either way, whether an exception occurred or not, whether it was handled or not, the finally runs last. Lutz states the net effect plainly: the finally clause always runs on the way out of the try, and if an exception is still active when the finally finishes, it continues to propagate after the finally block runs.

That last fact is the one the exam loves. A finally does not stop an exception. It runs its cleanup and then the exception continues on its way, up to the next handler or to the top-level default handler that prints the traceback and ends the program. The finally is for cleanup that must happen regardless — closing a file, releasing a lock — not for handling. Handling is the except block's job.

§ IIIObjective Flavor — The else Clause and Why It Exists

The else clause confuses people because it looks redundant. Why not just put its code at the end of the try block? The answer is scope of protection, and PCAP tests it directly.

Code in the try block is protected by the except handlers. Code in the else block is not. Suppose you open a file in the try, and you want to process it only if the open succeeded, but you do not want a bug in your processing to be caught by the except IOError that was meant only for the open. Put the open in the try and the processing in the else. Now an IOError from the open is caught, and a KeyError from your processing propagates normally instead of being swallowed by a handler that was never meant for it. The else runs only when the try succeeded, and it runs outside the reach of the handlers, which is exactly what you want for the code that should run after a successful risky operation but should not be shielded by that operation's error handling.

This is the same discipline the Ops lesson named when it said to catch the narrowest exception you can. An except that is too broad, or a try block that wraps too much, swallows bugs that should have propagated. The else clause is the language's built-in tool for keeping the protected region small: only the risky line is in the try; everything that depends on its success but has its own failure modes goes in the else.

§ IVObjective Flavor — The Built-in Hierarchy and Matching

Exceptions in Python are classes, and they are matched by class. This single fact drives half the exam's exception questions. An except clause naming a class catches an instance of that class or of any subclass of it. So except Exception catches almost everything, because nearly every built-in exception inherits from Exception; except ArithmeticError catches ZeroDivisionError and OverflowError because both derive from it; and except LookupError catches both KeyError and IndexError for the same reason.

The tree the exam expects you to know has a fixed top. BaseException is the root of everything. Below it sit Exception and a few special cases that deliberately do not inherit from ExceptionKeyboardInterrupt and SystemExit among them — precisely so that a broad except Exception does not accidentally swallow a user's Ctrl-C or a deliberate sys.exit. Lutz's built-in-exceptions reference, which grounds this section, lays out the standard classes and their inheritance; the PCAP wants you to know the common families: LookupError over KeyError and IndexError, ArithmeticError over ZeroDivisionError, and Exception over the ordinary ones you meet daily.

Two matching rules follow, and both appear on the exam. First, order matters: Python tries the except clauses top to bottom and runs the first one that matches, so a broad handler placed above a specific one shadows it. If you write except Exception before except ValueError, the ValueError handler is dead code, because Exception matched first. Put specific handlers above general ones. Second, one except can name a tuple of classes — except (KeyError, IndexError) — to give several types the same handler without repeating the block.

§ VObjective Flavor — Raising and Propagating

The raise statement is how you signal an error yourself, and it has three forms the exam distinguishes. raise SomeError("message") creates and raises a new instance. raise SomeError with a class and no parentheses raises a fresh instance of that class, because Python instantiates it for you. And a bare raise, with nothing after it, re-raises the exception currently being handled — it is legal only inside an except block, and it is how you catch an exception, do something with it such as logging, and then let it continue propagating unchanged.

Propagation is the behavior of an unhandled exception, and it is mechanical. When an exception is raised and the current function has no matching handler, the function stops and the exception moves to the caller. If the caller has no handler, it moves to that caller's caller, climbing the call stack until either a matching except is found or the stack runs out. If it runs out, the default handler prints the traceback — the full climb it just made, innermost call last — and terminates the program. The exam presents nested-function code and asks where an exception is caught or what the program prints; you answer by walking the stack outward from the raise to the first matching handler, which is exactly the mechanic the Dev lesson's chaining preserves across a translation.

Raising your own exceptions is where the custom hierarchy from the Dev lesson meets the PCAP objective. When you raise ValueError("bad config"), a caller can catch it with except ValueError. When you define your own exception class by inheriting from Exception and raise that, a caller catches it by your class name or by any superclass in its chain. The rules are the same for built-in and user-defined exceptions, because to Python they are the same kind of thing: classes, raised as instances, matched by inheritance.

§ VIConnection to Today's Ops and Dev Lessons

The three lessons are one topic at three heights. This PCAP lesson holds the statement mechanics: what runs when, how the tree matches, what raise does. The Ops lesson took those mechanics and made policy from them — retry, degrade, or fail loud — deciding which response each step's failure deserves. The Dev lesson took them deeper into the language — chaining so a translated exception keeps its cause, a custom hierarchy so callers catch by category, exception groups so a concurrent step reports every failure. If you can walk the try / except / else / finally order in your sleep and match the built-in tree without hesitating, both other lessons read as natural extensions rather than new material.

§ VIIPractice Questions

Answer each before reading the resolution.

Question 1 · Clause order

In a try statement with a try block, one except, an else, and a finally, the try block runs and does not raise. In what order do the remaining clauses run, and which is skipped?

Question 2 · finally and propagation

A try block raises KeyError. There is a finally clause but no except that matches KeyError. Does the finally run, and what happens to the KeyError afterward?

Question 3 · Handler order

A try has except Exception first and except ValueError second. The try raises ValueError. Which handler runs, and what is wrong with this structure?

Question 4 · Matching by inheritance

Will except LookupError catch a KeyError? Will it catch a ZeroDivisionError? Explain both using the class tree.

Question 5 · Forms of raise

What is the difference between raise ValueError, raise ValueError("bad"), and a bare raise with nothing after it, and where is the bare form legal?

§ VIIIQuestion Resolutions

Answer 1The else runs, then the finally runs. Every except is skipped, because the try block finished with no exception, and the except clauses only run when there is an exception to handle. The else runs precisely because the try succeeded, and the finally runs last on the way out as it always does.
Answer 2The finally runs. A finally always runs on the way out of the try, regardless of whether an exception occurred or was handled. Because no except matched the KeyError, the exception is still active when the finally finishes, so after the finally block runs the KeyError continues to propagate to the enclosing scope, and if nothing there handles it, to the top-level default handler.
Answer 3The except Exception handler runs, because Python tries handlers top to bottom and Exception is a superclass of ValueError, so it matches first. The except ValueError below it is dead code — it can never run, because anything it would catch is already caught by the broader handler above. Specific handlers must be placed above general ones.
Answer 4Yes to the first, no to the second. KeyError inherits from LookupError, so a handler for the superclass catches the subclass. ZeroDivisionError inherits from ArithmeticError, not from LookupError, so except LookupError does not catch it. Matching is by the class tree: a handler catches its class and any subclass, and nothing else.
Answer 5raise ValueError names the class with no parentheses; Python instantiates it for you and raises a fresh instance. raise ValueError("bad") creates the instance yourself with a message argument and raises it. A bare raise re-raises the exception currently being handled, unchanged, and is legal only inside an except block — it is how you handle an exception partially, such as logging it, and then let it continue propagating.

§ IXClosing

Exceptions on the PCAP reduce to three fixed rule-sets: the clause order of try / except / else / finally, matching by the class tree with specific handlers above general, and the three forms of raise with the mechanical stack-climb of propagation. None of it is judgment. All of it is rules, and once the rules are automatic the exam questions are lookups.

Before the next Python day, take a short function with a try/except/else/finally and trace on paper what prints under three cases: the try succeeds, the try raises a caught exception, the try raises an uncaught one. If your trace matches the interpreter every time, this objective is yours.

Paired lessons → Ops 01-Earth-DevOps/Synthesis-Lessons/2026-07-24-resilient-python-automation-... · Dev Polyglot-Dev/Python/2026-07-24-pythons-exception-model-in-depth-...

🫡 ⚖️ 📜
Leo.Syri — Praetor Consulate, Imperium Luminaura
Filed 2026-07-24 Friday Fajr · Cert-Prep lesson · PCAP-emphasis (python_day #0) · Python deep-mastery track (day 2)
Backward-Synergy-Reach → today's Ops resilience + Dev exception model · CKA (07-23) · Terraform Associate 003 (07-22)
Grounded in Learning Python (Lutz) Ch 33–34 + current PCAP blueprint · HTML shipped in-cycle per HARD DISCIPLINE · aether-accent