Skip to content

← Everyday Standard Library Tools step 13 of 55

Easy Primitives

Find the First Error with a Generator

Your service writes a log with thousands of lines, and you want the first error. A function that builds a list of every error line reads the whole log, even when the first error is on line 2. A generator reads only as far as you ask.

log = ["INFO server started", "ERROR disk full", "INFO retrying"]
first_error(log)  # "ERROR disk full", after reading 2 lines

What to write

Fill in two functions:

  • error_lines(lines) is a generator function. Loop over lines and yield each line that starts with "ERROR". Use line.startswith("ERROR"), so "error: disk" and " ERROR disk" do not count.
  • first_error(lines) returns the first error line, or None if there is none. Get it from error_lines(lines), and stop reading as soon as you have it.

Both functions take any iterable of strings, not only a list.

What the driver reports

Keep CountingLines and solve as they are. CountingLines hands out the log one line at a time and counts how many lines were read. solve(lines=...) returns a dictionary:

  • read_before_next: how many lines error_lines read before anyone asked it for a value. A generator has read nothing yet, so this is 0. A function that builds a list has already read every line.
  • all_errors: every error line, in log order.
  • second_pass: what a second list() over the same generator gives. It is [], because a generator is empty after one pass.
  • first_error: what first_error returned.
  • lines_read: how many lines first_error read. For the log above it is 2.

Edge cases

  • An empty log gives no errors, None, and a lines_read of 0.
  • With no error line, first_error reads every line and returns None.