Handling errors

Part of the free LogicWiz Python course, Module 7: Errors and debugging. The examples and practice run in the app.

Meher typed two when Canteen Counter asked for the number of samosas. The whole app crashed in the middle of the lunch rush, with twenty people in the queue. One try would have saved it.

You'll learn to

  • Catch an error with `try`/`except` and keep the program running
  • Know exactly when `else` and `finally` run, and why except order matters
  • Raise your own errors with `raise`, and check your own assumptions with `assert`
  • Keep asking until the user types valid input

Learn

try and except

Put the risky lines inside try:. If one of them raises an error, Python jumps straight to a matching except block, skipping the rest of try. If nothing goes wrong, the except block is skipped.

try:
    risky lines
except ValueError:
    what to do instead

Name the error you expect (ValueError, KeyError...). After the whole try statement, the program carries on normally, so one bad entry no longer kills the app.

Surviving "two"

qty_text = input()
try:
    qty = int(qty_text)
    print("Samosas:", qty)
except ValueError:
    print("Please type a number, like 2")
print("Next customer")

The input is two, so int() fails and Python jumps to except ("Samosas:" never prints). Change the input to 3 and run again.

else and finally

def divide(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        print("Can't divide by zero")
    else:
        print("Result:", result)
    finally:
        print("Done with", a, b)

divide(10, 2)
divide(5, 0)

Step through it. The first call goes try → else → finally. The second goes try → except → finally. finally ran both times.

Which block runs when?

BlockRuns when
tryAlways starts. Stops at the first line that raises.
except XOnly if an error of type X (or a child of X) happened in try
elseOnly if try finished with no error
finallyAlways, error or not, even after a return (use it to close or clean up)

Several excepts: the first match wins

for text in ["4", "zero", "0"]:
    try:
        n = int(text)
        print(100 // n)
    except ValueError as e:
        print("Bad number:", e)
    except ZeroDivisionError:
        print("Zero plates?")

Python checks the except blocks top to bottom and runs only the first one that matches. as e gives you the error object; printing it shows its message.

Board output questions love try/except/else/finally. Trace them line by line: try stops at the error, only one except runs (the first match), else runs only with no error, finally always runs. And since Exception is the parent of nearly every error, except Exception: placed first catches everything, so the specific blocks below it never run.

raise and assert

Sometimes Python is happy but you know a value is wrong: a quantity of -2 is a valid int, but not a valid order. Then raise the error yourself:

raise ValueError("Quantity must be at least 1")

The caller can catch it like any other error, and the message travels with it.

assert condition, "message" is a quick check on your own code: if the condition is False it raises AssertionError. Use it for "this should never happen" checks and tests, not for checking what users type, because asserts can be switched off (python -O).

Raising your own error

def set_qty(qty):
    if qty < 1:
        raise ValueError("Quantity must be at least 1")
    return qty

try:
    set_qty(-2)
except ValueError as e:
    print("Error:", e)

price = 15
assert price > 0, "price must be positive"
print("Price ok")

Change price to -15 and run: the assert stops the program with AssertionError: price must be positive.

Ask until the input is valid

while True:
    text = input()
    try:
        qty = int(text)
        if qty < 1:
            raise ValueError("too small")
        break
    except ValueError:
        print("Try again:", text)
print("Qty =", qty)

The inputs are abc, -3, 4. Both bad kinds (not a number, and too small) end in the same except, and only a good value reaches break. This loop is the heart of every app that never crashes.

In one line

try the risky lines, except a named error, else runs only on success, finally always runs; the first matching except wins, so put specific ones first; raise your own errors.