on the importance of exceptions

Meredith Montgomery mmontgomery at levado.to
Mon Sep 5 22:15:15 EDT 2022


Meredith Montgomery <mmontgomery at levado.to> writes:

> I'm trying to show people that exceptions are a very nice thing to have
> when it comes to detecting when something went awry somewhere.  I'd like
> a real-world case, though.  

Here's my contribution.  I want to handle all errors in main() and the
real job is done in does_a_job(), which, in turns, needs to delegate
tasks to those other procedures that fail sometimes.  

It's does_a_job() that /must/ distinguish the error codes because errors
come in as False from both opens_file() and reads_file().  So the checks
must be done both in does_a_job() and in main().  (We also notice that
does_a_job() has a return-type that's a union (sometimes an integer,
sometimes a string), which makes distinguishing error code and success a
bit harder.)

--8<---------------cut here---------------start------------->8---
from random import randint

def main():
  r, data = does_a_job()
  if r < 0:
    if r == -1:
      print("Fatal. Could not open file.")
      return None
    if r == -2:
      print("Fatal. Could not read file")
      return None
  print(f"Great! We got the data: ``{r}''.")

def does_a_job():
  okay = opens_file()
  if not okay:
    return -1
  okay, data = reads_file()
  if not okay:
    return -2
  closes_file()
  return data

def open_file(): # Opens okay with probability 80%
  return randint(1,10) <= 8

def read_file(): # Reads okay with probability 50%
  return randint(1,10) <= 5, "data I am"

def closes_file():  # Works with probability 1
  return True
--8<---------------cut here---------------end--------------->8---

If we could give the program a final destination at does_a_job(), the
job would be easier.  But all we want to do in does_a_job() is to
propagate the error conditions upwards to main() to really decide what
to do.  Exceptions lets us do that with a cleaner version.

--8<---------------cut here---------------start------------->8---
from random import randint

def main():
  try:
    data = does_a_job()
  except FileNotFoundError:
    print("Fatal. Could not open file.")
  except MemoryError:
    print("Fatal. Could not read file")
  else:
    print(f"Great! We got the data: ``{data}''.")

def does_a_job():
  open_file()
  data = reads_file()
  close_file()
  return data

def open_file(): # Opens okay with probability 80%
  if randint(1,10) <= 8:
    return True
  raise FileNotFoundError("Sorry: the file system is /so/ busy right now.")

def reads_file(): # Reads okay with probability 50%
  if randint(1,10) <= 5:
    return "data I am"
  raise MemoryError("Sorry: not enough memory for /all/ this data.")

def close_file():  # Works with probability 1
  return True
--8<---------------cut here---------------end--------------->8---


More information about the Python-list mailing list