We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Orientation and the Gate step 10 of 13
Placement 3/6: exception boundary design
Placement diagnostic, 3 of 6. About seven minutes. If this one is opaque, T6 (Failure by Design) is where you start.
A parser that lets int() raise ValueError at its caller has leaked its
implementation. The caller now has to know that this config format is parsed
with int(), and the day you swap in Decimal you break every except
clause in the codebase. A boundary translates: it raises errors in the
vocabulary of its own domain, and it attaches the original as the cause so
the traceback still tells you the truth.
ConfigError is given. Write both halves:
def parse_line(line: str) -> tuple[str, int]:
Parse "key=value" into (key, int(value)), stripping surrounding
whitespace from both. Split on the first = only. Raise ConfigError(line)
when:
-
there is no
=at all, or the key is empty or whitespace — raise it without a cause, because nothing failed underneath you; this line simply is not config; -
the value is not an integer — raise it
fromtheValueErrorthatint()produced, because something genuinely did fail underneath you and the reader deserves to see it.
def solve(lines: list[str]) -> dict[str, list[str]]:
The boundary. Parse every line; a bad line must never abort the batch.
Return {"ok": [...], "errors": [...]} where:
-
okentries aref"{key}={value}"with the parsed key and int, so" spaced = 42 "becomes"spaced=42"and"n=+0"becomes"n=0". -
errorentries aref"{line}|ConfigError|{cause}"wherelineis the raw input line andcauseis the class name ofexc.__cause__, or"-"when there is no cause.
Why the tests can see __cause__
Because raise X from Y is not decoration. It sets X.__cause__ = Y, which
is what makes the traceback print
ValueError: invalid literal for int() with base 10: 'x'
The above exception was the direct cause of the following exception:
ConfigError: bad config line: 'a=x'
Omit the from and Python still records __context__ implicitly and prints
“During handling of the above exception, another exception occurred” — the
wording that tells a reader on a bad night that your error handler crashed,
rather than that your error handler was doing its job. The two are not
interchangeable, and __cause__ is the one you can assert on.
Production consequence
except Exception: pass around a parse loop is how a config file silently
loses half its keys. The shape here — a narrow domain exception, raised
from the cause, caught at exactly one boundary that collects rather than
aborts — is what lets a batch job report “14 of 400 rows rejected, here is
why” instead of dying on row 3.
Stuck?
Python reference solution
Sign in to attempt this problem and reveal the reference solution.