Skip to content

Latest commit

 

History

History
86 lines (61 loc) · 2.76 KB

File metadata and controls

86 lines (61 loc) · 2.76 KB

Patterns

Optional error handling

In some cases it may be desirable to keep exception handling in a helper that returns :obj:`None` on error. In the past, the project used an optional error argument that defaulted to :obj:`True` that indicated that exceptions should be raised. Callers could pass error=False to instruct the function to return :obj:`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 :obj:`None` in order to drop the Optional annotation from the type. Even when we know the function cannot return :obj:`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 :obj:`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)