Skip to content

Improve exception-fluency of SDK API #19

@erikrose

Description

@erikrose

Currently, errors are raised (exclusively?) as Err exceptions (parallel to Rust's Err variant of its Result type), and the caller must pick them apart manually using isinstance() or similar:

try:
    result = await_request(pending_request)
except Err as exc:
    if isinstance(exc.value, Error_OptionalNone):
        # There were no more requests within the timeout.
        break
    else:
        # Something truly went wrong.
        raise
else:
    # Do the no-error case.

This should be shortenable to:

try:
    result = await_request(pending_request)
except NoRequestError as exc:
    break
else:
    # Do the no-error case.

(This may not be the most motivating example, since await_request() should perhaps return None when there is no request. However, it serves to illustrate the form.)

Result is currently defined as Union[Ok[T], Err[E]]. It seems to me that we can achieve an idiomatic and lossless mapping onto Python's result/exception duality by having Result-returning functions raise E xor return T.

Note that currently we raise the entire Err(E), not merely E, necessitating further unpacking. I don't think it brings any benefit, since E is the only thing in the Err:

@dataclass(frozen=True)
class Err(Generic[E], Exception):
    value: E

A notable speed bump is that the current error classes are root classes, not subclasses of BaseException, as is required for catching them.

@dataclass
class Error_OptionalNone:
    pass

However, it appears the binding generator is aware those are errors…

Error = Union[Error_GenericError, Error_InvalidArgument, Error_BadHandle, Error_BufferLen, Error_Unsupported, Error_HttpInvalid, Error_HttpUser, Error_HttpIncomplete, Error_OptionalNone, Error_HttpHeadTooLarge, Error_HttpInvalidStatus, Error_LimitExceeded]

…so perhaps the fix is straightforward.

For completeness and in case fixing it closer to the source proves intractable: decorating Result-returning functions with one that catches any Err, unpacks it, and re-raises its E would domesticate them sufficiently. (The BaseException speed bump would still need to be solved first.)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions