[Python-ideas] Let try-except check the exception instance

Eric Snow ericsnowcurrently at gmail.com
Thu May 31 11:57:07 EDT 2018


On Wed, May 30, 2018 at 10:47 PM, Danilo J. S. Bellini
<danilo.bellini at gmail.com> wrote:
>>>> try:
> ...     # [...]
> ...     session.commit() # Here it raises!
> ...     # [...]
> ... except DatabaseError as exc:
> ...     msg = get_db_error_msg_from_exception(exc)
> ...     if msg == "beyond_limit":
> ...         # [...]
> ...     elif msg == "no_funds":
> ...         # [...]
> ...     else:
> ...         raise

Matching on error messages is a recipe for pain.  My experience with
Go seared that lesson into my brain. (The boilerplate involved there
to make custom error types leads to a lot of error message checking
instead.)

> That works, but I'd like to do something like:
>
>>>> try:
> ...     # [...]
> ... except BeyondLimit:
> ...     # [...]
> ... except NoFunds:
> ...     # [...]

Using subclasses like this is the way to go.  If you control the
originating code then make it happen. :)  If not then work to fix it
upstream.

If that fails then at least use a helper that converts the exception
after the fact.  It could look like following:

    # Subclassing DatabaseError means existing try-except block will
still work the same.
    class CustomDatabaseError(DatabaseError):
        ....

    class BeyondLimitError(CustomDatabaseError):
        ...

    class NoFundsError(CustomDatabaseError):
        ...

    def convert_exception(exc):
        if isinstance(exc, CustomDatabaseError):
            raise
        msg = get_db_error_msg_from_exception(exc)
        if msg == "beyond_limit":
            raise BeyondLimitError(...)
        elif msg == "no_funds":
            raise NoFundsError(...)
        else:
            raise

    @contextmanager
    def converted_exceptions():
        try:
            yield
        except CustomDatabaseError:
            raise
        except DatabaseError as exc:
            convert_exception(exc)

    def db_op(...):
        with converted_exceptions():
            # Some code that does DB ops, e.g. session.commit().
            ...

    try:
        db_op(...)
    except BeyondLimitError:
        ...
    except NoFundsError:
        ...
    except DatabaseError:  # fallback
        ...

The big wins there are with re-usability, maintainability, and
separation of concerns.  Notably, it consolidates your
message-handling code to one spot and keeps it focused.

-eric


More information about the Python-ideas mailing list