A drawback to handling errors via exceptions
Recently I discovered an interesting and long standing bug in DWiki. DWiki is essentially a mature program, so this one was uncovered through the common mechanism of someone using invalid input, in this case a specific sort of invalid URL. DWiki creates time-based views of this blog through synthetic parts of the URLs that end in things like, for example, '.../2014/10/' for entries from October 2014. Someone came along and requested a URL that looked like '.../2014/99/', and DWiki promptly hit an uncaught Python exception (well, technically it was caught and logged by my general error code).
(A mature program usually doesn't have bugs handling valid input, even uncommon valid input. But the many forms of invalid input are often much less well tested.)
To be specific, it promptly coughed up:
calendar.IllegalMonthError: bad month number 99; must be 1-12
Down in the depths of the code that handled a per-month view I was
calendar.monthrange() to determine how many days a given month
has, which was throwing an exception because '99' is of course not a
valid month of the year. The exception escaped because I wasn't doing
anything in my code to either catch it or not let invalid months get
that far in the code.
The standard advantage of handling errors via exceptions definitely applied here. Even though I had totally overlooked this error possibility, the error did not get quietly ignored and go on to corrupt further program state; instead I got smacked over the nose with the existence of this bug so I could find it and fix it. But it also exposes a drawback of handling errors with exceptions, which is that it makes it easier to overlook the possibility of errors because that possibility isn't explicit.
The calendar module doesn't document
what exceptions it raises, either in general or especially in the
monthrange() in specific (where it would be easy
to spot while reading about the function). Because an exception is
effectively an implicit extra return 'value' from functions, it's
easy to overlook the possibility that you'll actually get an exception;
in Python, there's nothing there to rub your nose in it and make you
think about it. And so I never even thought about what happened if
monthrange() was handed invalid input, in part because of the
usual silent assumption that the
code would only be called with valid input because of course DWiki
doesn't generate date range URLs with bad months in them.
Explicit error returns may require a bunch of inconvenient work to handle them individually instead of letting you aggregate exception handling together, but the mere presence of an explicit error return in a method's or function's signature serves as a reminder that yes, the function can fail and so you need to handle it. Exceptions for errors are more convenient and more safe for at least casual programming, but they do mean you need to ask yourself what-if questions on a regular basis (here, 'what if the month is out of range?').
(It turns out I've run into this general issue before, although that time the documentation had a prominent notice that I just ignored. The general issue of error handling with exceptions versus explicit returns is on my mind these days because I've been doing a bunch of coding in Go, which has explicit error returns.)