runtime
87 / 250
lesson 87 Exceptions & Validation · intermediate

Read the error and handle each kind

`except ValueError as err` keeps the error so you can report its message. Several `except` clauses let each kind of failure get its own response.

You'll be able to
  • Use except ValueError as err: and str(err) to report what went wrong
  • Write several except clauses, or one with a tuple of types, and know which runs
  • Turn failures into a validation report: which rows failed, and why
The idea

Skipping a bad value is sometimes enough. A data team usually wants more: which row, and what was wrong with it. Write except ValueError as err: and the error object is stored in err. str(err) (or printing err) gives its message, the same text you'd see at the bottom of a traceback.

Run each example to see what it prints. Running examples doesn't count toward progress.

example-1.py · Report the message
for text in ["6.95", "1,234.50", "N/A"]:
    try:
        print(float(text))
    except ValueError as err:
        print("rejected:", err)

One line can fail in more than one way. Reading row["Quantity"] raises KeyError when the column is missing; converting it raises ValueError when the text is wrong. Add one except clause per kind. Python checks them top to bottom and runs the first that matches. When two kinds need the same response, list them in one clause as a tuple: except (KeyError, ValueError) as err:.

An error you didn't list still stops the program, as it should.

example-2.py · An error nobody listed
row = {"Quantity": "12", "Price": None}
try:
    total = int(row["Quantity"]) * float(row["Price"])
except KeyError as err:
    print("missing column", err)
except ValueError as err:
    print("bad number:", err)
example-3.py · A validation report for invoice lines
def check_lines(rows):
    problems = []
    for number, row in enumerate(rows, start=2):
        try:
            quantity = int(row["Quantity"])
            price = float(row["Price"])
        except KeyError as err:
            problems.append(f"line {number}: missing column {err}")
        except ValueError as err:
            problems.append(f"line {number}: {err}")
    return problems

rows = [
    {"StockCode": "85048", "Quantity": "12", "Price": "6.95"},
    {"StockCode": "22350", "Quantity": "twelve", "Price": "2.55"},
    {"StockCode": "22087", "Price": "2.95"},
]
for problem in check_lines(rows):
    print(problem)

When to use it: whenever a failure should be reported, logged or counted rather than just skipped. Mistakes to avoid: putting a broad clause such as except Exception: first, so the specific clauses below it never run; reporting a fixed text like "error" that throws away the message; and catching an error type that this code can't actually raise, which only misleads the reader.

Review
  • except SomeError as err: keeps the error; str(err) is its message (a KeyError's shows the key with quotes).
  • Several clauses: the first match runs, so put specific types before general ones.
  • A tuple of types shares one handler; unlisted errors still stop the program.
  • Next: else for code that should run only on success, and finally for code that must always run.
Practise this

Loading your progress…

To master this lesson, solve both challenges without hints or the answer, and pass the concept check.

Read the error and handle each kind — Exceptions & Validation · Runtime