Exercise 3: Reproducing Both Bugs and Identifying the Exact Lines — Possible Solution ==================================================================== THE TEST ------------------------------ parse_length_naive('0') expand_shorthand_naive("10px 20px 30px") RESULT ------------------------------ parse_length_naive('0') -> raises ValueError expand_shorthand_naive("10px 20px 30px").left -> 10 (should be 20) BUG 1: THE EXACT LINE ------------------------------ def parse_length_naive(s): return float(s[:-2]) The single line `return float(s[:-2])` is responsible. For the string '0' (length 1), Python slicing `s[:-2]` means "everything except the last two characters" -- but there's only ONE character total, so the result is the empty string ''. `float('')` then raises ValueError: could not convert string to float: ''. WHY IT'S AN EASY MISTAKE TO MAKE ------------------------------ Every OTHER length value this engine deals with -- '16px', '10px', '2px' -- genuinely does end in a two-character 'px' suffix, and slicing it off with [:-2] is the obviously correct, simplest way to extract the number. The bug only exists because CSS has one specific, easy-to-forget exception: a literal zero never needs a unit at all. Nothing about writing float(s[:-2]) looks wrong in isolation -- it only breaks on an input shape (a unitless number) that's rare enough, and correct enough as real CSS, that it's easy to never think to test for it until a real rule (like Chapter 9's own 'margin: 16px 0') actually uses it. BUG 2: THE EXACT LINE ------------------------------ elif len(lengths) == 3: t, r, b = lengths l = t # the bug The line `l = t` is responsible. For "10px 20px 30px", lengths is [10, 20, 30], so t=10, r=20, b=30, and the naive version sets l = t = 10. The correct line, per real CSS's own 3-value shorthand definition, is `l = r` -- left is supposed to reuse the HORIZONTAL (second) value, matching right, not the top (first) value. WHY IT'S AN EASY MISTAKE TO MAKE ------------------------------ `t` is the first variable already sitting in scope right after the unpacking line, and reusing "the first thing you already have" is a completely natural instinct when writing a fallback -- especially since the 1-value case (`t = r = b = l = lengths[0]`) already established the pattern "reuse an earlier value for a side that wasn't given its own." The 3-value case looks superficially similar to that pattern, but the actual CSS rule it's supposed to implement is different: the missing side (left) is meant to mirror its OPPOSITE side (right), not fall back to the first value written. Without independently checking this specific case against the real CSS specification -- rather than just extending the same "reuse an earlier variable" pattern from the simpler 1-value case -- this exact mistake is genuinely easy to write and to have look correct on a casual read.