Home > Enterprise >  Nesting try-except blocks leads to overly indented code
Nesting try-except blocks leads to overly indented code

Time:10-26

I have a rather philosophical problem writing Python code. Normally it is considered good style to use as many try-except-else blocks as necessary to handle all expectable cases. But I find that this is approach sometimes leads to unnecessarily many levels of indentation that makes it difficult to keep the line count below 80 and suggest nonexistent hierarchical structure in the code. I feel I'm missing some sort of "GOTO" statement or similar jump to cope with this. Therefore I'm wondering if I'm missing some coding style, or Python statement, or pattern, to improve my Python code.

I'll put a more concrete example. Let's think I'm defining a function, and I'm within a loop, where various things could go wrong. Each thing is sufficient to make the rest of the code within the loop skippable, which I normally skip with continue in a way similar to:

def mu_func()
    failed = False
    for i in range(100):
        try:
            pass
            # we try something that might raise an exception
        except:
            print("something went wrong by reason #1")
            failed = True
            continue
        try:
            pass
            # we try something that might raise an exception
        except:
            print("something went wrong by reason #2")
            failed = True
            continue
        try:
            pass
            # we try something that might raise an exception
        except:
            print("something went wrong by reason #3")
            failed = True
    # some cleanup code at the end of the function
    # that depends on the errors raised
    return failed

The important thing to note here is that since we are within a loop, the continue statement saves me from having to use an else statement to skip the rest of the code if any exception is raised. Therefore the indentation level does not keep growing. Also note that I cannot just exit from the exception handling, because I need to do some cleanup right before returning from the function.

Now note what happens when we are not within a loop:

def mu_func()
    failed = False
    try:
        pass
        # we try something that might raise an exception
    except:
        print("something went wrong by reason #1")
        failed = True
    else:
        try:
            pass
            # we try something that might raise an exception
        except:
            print("something went wrong by reason #2")
            failed = True
        else:
            try:
                pass
                # we try something that might raise an exception
            except:
                print("something went wrong by reason #3")
               failed = True
    # some cleanup code at the end of the function
    # that depends on the errors raised
    return failed

¿See the difference? If want to skip the rest of the code after any exception has been raised, I have to put inside an else statement everything below, which trivially increases a level of indent from that point on. This not only leads to annoying code where it is difficult to to keep the line count below 80 characters. The worst aspect is that these nested try-except-else blocks suggest some sort of hierarchy in the code, which is artificial. All exceptions are equally important. This can be easily appreciated in the first example where the order of the try-except blocks could be nicely exchanged without touching the indent and without affecting the functionality.

Is there a Python statement, or pattern, that allows me to skip the rest of the code once an exception is handled, that precludes me from having to add more and more indent levels? Am I misusing the else statement? I'm tempted to put everything within a trivial 1-iteration loop so I have access to the continue statement to perform the jump to the end of block statement that I'm missing.

CodePudding user response:

One approach might be to wrap the for loop with a try-except block:

def mu_func():
    try:
        for i in range(100):
            # we try something that might raise an exception
            action_1()
            print("action #1 succeeds")

            action_2()
            print("action #2 succeeds")

            action_3()
            print("action #3 succeeds")

    except:
        print("something went wrong!")
        failed = True

    else:
        failed = False

    return failed

If you have other logic in your for loop, I'd suggest wrapping only the logic that might raise an exception:

def mu_func():
    for i in range(100):
        try:
            # we try something that might raise an exception
            action_1()
            print("action #1 succeeds")

            action_2()
            print("action #2 succeeds")

            action_3()
            print("action #3 succeeds")

        except:
            print("something went wrong!")
            failed = True
            break

        # here we might do other stuff in the loop
        ...
    
    else:
        failed = False

    return failed

CodePudding user response:

What about using:

def mu_func()
    failed = False
    try:
        pass
        # we try something that might raise an exception
    except ExceptionClass1():
        print("something went wrong by reason #1")
        failed = True
    except ExceptionClass2():
        print("something went wrong by reason #2")
        failed = True
    except ExceptionClass3():
        print("something went wrong by reason #3")
        failed = True
    except ExceptionClass4():
        print("something went wrong by reason #4")
        failed = True
    return failed
  • Related