Patterns¶
Optional error handling¶
In some cases it may be desirable to keep exception handling in a helper
that returns None
on error. In the past, the project used an
optional error
argument that defaulted to True
that indicated
that exceptions should be raised. Callers could pass error=False
to
instruct the function to return None
instead.
With Python’s
typing
annotations, these routines must be annotated as returning an
Optional
value. While the
@overload
decorator allows us to associate return types with specific argument types
and counts, there is no way to associate a return type with specific
argument values, like error=False
.
A function annotated as returning an Optional
value affects the implied
types of the variables used to assign the result. Every caller of such
a routine would need to check the result against None
in order to
drop the Optional
annotation from the type. Even when we know the
function cannot return None
when passed error=True
.
The way we handle this is to have separate functions for each case
so that callers which will never have a None
value returned
do not need to check it.
Here are a few examples:
Function raises its own exceptions¶
from typing import Optional
import gdb
def new_routine(val: gdb.Value) -> str:
if some_condition:
raise RuntimeError("something bad happened")
return val.string()
def new_routine_safe(val: gdb.Value) -> Optional[str]:
try:
return new_routine(val)
except RuntimeError:
return None
Function calls functions that raise optional exceptions¶
from typing import Optional
import gdb
def some_existing_routine(val: gdb.Value, error: bool = True) -> Optional[str]:
if some_condition:
if error:
raise RuntimeError("something bad happened")
return None
return val.string()
def new_routine(val: gdb.Value) -> str:
print("do something")
ret = some_existing_routine(val)
# This is required to drop the Optional annotation
if ret is None:
raise RuntimeError("some_existing_routine can't return None")
return ret
def new_routine_safe(val: gdb.Value) -> Optional[str]:
return some_existing_routine(val, False)