Cohort 4Register

PART 2 · PYTHON FOUNDATIONS · CHAPTER 7

Errors, exceptions and context managers

FreeLesson 30 of 78

A pipeline that crashes on one bad row loses the other million. Catch narrowly, count what you skip, and always clean up.

TRY, EXCEPT, ELSE, FINALLYPYTHON 3.12
def parse_amount(text):
    try:
        value = float(text)
    except ValueError:
        print("  except: bad value", repr(text))
        return None
    else:
        print("  else: parsed", value)
        return value
    finally:
        print("  finally: always runs")

for t in ["1198.00", "abc"]:
    print(t, "->", parse_amount(t))

Output

  else: parsed 1198.0
  finally: always runs
1198.00 -> 1198.0
  except: bad value 'abc'
  finally: always runs
abc -> None
CUSTOM EXCEPTIONS AND CHAININGPYTHON 3.12
class DataQualityError(Exception):
    """raised when a batch fails a hard check"""

def check_batch(rows):
    bad = [r for r in rows if r["amount"] <= 0]
    if bad:
        raise DataQualityError(f"{len(bad)} rows <= 0")

try:
    check_batch([{"amount": 10}, {"amount": -50}])
except DataQualityError as e:
    print("blocked load:", e)

try:
    int("12a")
except ValueError as e:
    raise RuntimeError("could not parse order_id") from e

Output

blocked load: 1 rows <= 0
RuntimeError: could not parse order_id
A CONTEXT MANAGER ALWAYS CLEANS UPPYTHON 3.12
class Connection:
    def __enter__(self):
        print("open connection")
        return self
    def __exit__(self, exc_type, exc, tb):
        name = exc_type.__name__ if exc_type else None
        print("close connection, error:", name)
        return False                 # do not swallow errors

try:
    with Connection() as conn:
        print("loading rows")
        raise TimeoutError("warehouse timeout")
except TimeoutError:
    print("caught after cleanup")

Output

open connection
loading rows
close connection, error: TimeoutError
caught after cleanup

with calls __exit__ whether the block succeeds or fails. That is why with open(...) never leaks file handles. Page 38 shows the shorter @contextmanager version.

Practice before you move on

  1. Convert the amount column of orders_dirty.csv with float, catch ValueError, and print the values that fail.
  2. Write a context manager that prints start and end around a block, even when the block raises.