3

Is there an idiomatic way to catch an exception when multiple methods may throw?:

try:
    someMethod()  # raises OSError
    someOtherMethod()  # raises OSError
except OSError:
    handle()

The exception may or may not be caused by the same event internal to the helper functions, so in the current layout it seems impossible to discern which method threw the exception. To resolve, there are two cases I could implement.

The first case is wrapping each method in its own try/except:

try:
    someMethod()
except OSError:
    handle()

try:
    someOtherMethod()
except OSError:
    handle()

While this address the issue of identifying which helper function threw the exception, it presents a new problem. If methods require successful of preceding methods, separate try/except blocks lead to forced early returning as subsequent blocks should not be executed:

try:
    someMethod()
except OSError:
    handle()
    return

try:
    someOtherMethod()
except OSError:
    handle()

This seems like a code-smell and is therefore undesired. The second case is creating custom exceptions based on the content of the internally generated exception:

class FailedReadIOError(Exception):
    def __init__(self, msg):
        super().__init__(msg)

class FailedFolderError(Exception):
    def __init__(self, msg):
        super().__init__(msg)

def someMethod():

    try:
        # stuff
    except OSError as err:
        raise FailedReadIOError('thrown in someMethod') from err

    try:
        # stuff
    except OSError as err:
        raise FailedFolderError('thrown in someMethod') from err

def someOtherMethod():

    try:
        # stuff
    except OSError as err:
        raise FailedReadIOError('thrown in someOtherMethod') from err


try:
    someMethod()
    someOtherMethod()
except FailedReadIOError:
    handle_this()
except FailedFolderError:
    handle_that()

This adequately addresses the concerns but requires additional (minimal) code to be written. Is there a pattern that achieves the same result without the need to create custom exceptions when multiple methods throw the same exception?

1 Answer 1

6

This depends entirely how you will handle the errors.

  • If you can't handle the errors meaningfully, don't.
  • If your functions raise errors that can be meaningfully handled, then the errors should be meaningful.

Often, no error handling is the most appropriate approach. In many contexts, just letting errors propagate can be totally acceptable.

Sometimes, a broad error category like OSError is sufficient to understand what has gone wrong and how your code can recover. Depending on context, inspecting the error's errno could yield further information. Such a strategy might be suitable for a function that wraps a lower-level function. But this has the implication that the other code needs to understand the OSError appropriately.

But in general, there is no good way around defining custom exception types that communicate the problem in a manner that is suitable in the context of your business logic. If your code needs to treat a failed read differently from a missing folder, your code should have a clear representation of these concepts. Any program has some kind of data model. We can use classes to structure this model, and to give names to certain concepts. Exceptions are a part of this model just like other classes.


While the above is nice theory, the most practical approach might be to maintain extra state variables that let us understand where the exception originated from:

checkpoint = 'start'
try:
    someMethod()  # raises OSError
    checkpoint = 'inner'
    someOtherMethod()  # raises OSError
except OSError as ex:
    if checkpoint == 'start':
      handle_this(ex)
    else:
      handle_that(ex)

A similar idea would be to inspect the exceptions stack trace to determine which function caused it…

While such techniques are not generally a good idea because such state or such dependency on internal functions can make the code massively more complex to maintain, it can be appropriate on a small scale, e.g. scripts or in code that does not depend on other modules.

1
  • 1
    Hadn't considered this, but I think the custom exception type for detailed context inside a broad exception type (such as OSError) is the cleanest manner to keep code concise and readable; yes it requires extra code to define the classes and handle exceptions internal to the helper methods, but the tradeoff is meaningful. In my case, I need to handle the errors so that they can be properly logged and notify the user with information as to why the application could not launch.
    – pstatix
    Feb 24, 2021 at 15:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.