we would like to use exceptions in our code. that is, we would like to expose exceptions as a public API. that is, users are expected to be able to catch these exceptions.
however, sometimes we wanna use standard exception types, like KeyError
and ValueError
just to name a couple. unfortunately, when writing complex code, we are bound to use standard functions or language features that can also raise these exceptions, like tuple unpacking:
>>> (x, y, z) = (1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected 3, got 2)
in theory these kinds of mistakes should never happen. unfortunately, if we have ValueError
as a public API, then anyone who hits such an edge-case will be met with an undebuggable misbehaviour.
we’re currently doing our best to wrap anything that might raise ValueError
and reraise it as a RuntimeError
instead, just in case there’s a bug in our code we didn’t catch. then, only the parts where we explicitly raise ValueError
will be caught by the caller. for example, let’s take this snippet from our code:
def get_property_value(self, prop):
"""Returns the value associated with the given property.
If duplicated, an earlier value should override a later value.
Args:
prop (DataProperty): The property.
Returns:
The value associated with the given property.
Raises:
PropertyError: If the property is not supported by this data
source.
LookupError: If the property is supported, but isn't available.
ValueError: If the property doesn't have exactly one value.
"""
iterator = self.get_property_values(prop)
try:
# note: unpacking
ret, = iterator
except LookupError as exc:
# don't accidentally swallow bugs in the iterator
raise RuntimeError from exc
return ret
@abc.abstractmethod
def get_property_values(self, prop):
"""Returns the values associated with the given property as an iterable.
If duplicated, earlier values should override later values.
Args:
prop (DataProperty): The property.
Returns:
The values associated with the given property.
Raises:
PropertyError: If the property is not supported by this data
source.
LookupError: If the property is supported, but isn't available.
"""
raise PropertyError
here, we opted to let ValueError
be raised by the unpacking and that propagates and all is well. but, we accidentally left the get_property_values
call unprotected, so it could also raise its own, unrelated ValueError
.
this is really frustrating, and we wish we had a better way of dealing with this issue. so, does anyone know if there is a better way?