← Home

None Is Not an Error

by Jeff Thoensen

I have a small CLI tool that looks up an MLB player's season stats by name. The lookup used to fail without ever raising anything: if the MLB API had no player matching a name, search_player_id printed "Player not found." and returned None, and it was on whoever called it to check for that and stop. If that check gets missed anywhere in the chain, None gets passed into the next function that expected a real player ID.

Testing that old version meant capturing whatever the script printed and asserting on the message, then separately checking that the return value was None. Replacing the print-and-return-None pattern with a real exception, PlayerLookupError, meant one assertion covers it: pytest.raises(PlayerLookupError) either happens or it doesn't, with nothing else to check.

The function that decides between pitching and hitting stats used to return (None, None) when a player had a season with no usable stats, the same shape the caller was already checking for a missing player entirely, so both very different problems ended up as the same "something is None, give up" check. Raising PlayerLookupError with an actual message, "No usable stats available for 2025" versus "No player found matching...", means you don't have to guess which one happened.

Running the script with a name as an argument, instead of only through the interactive prompt, meant tests could call the lookup function directly with a name and a mocked response, instead of needing to fake stdin to drive the interactive version. The rewrite, tests included, is on GitHub.