3

In a Python program, one generally catches an exception using a try-except block:

try:
    # Do stuff
except ValueError:
    # Handle exception

The best way I know of to catch an exception in an exception handler is a nested try-except block. However, with many nested try-catch blocks, this may get a bit messy:

try:
    # Some assignment that throws an exception if the object is not in the list
    try:
        # Some assignment function that throws an exception if the the object is not already in the database
        # Error Handling
    except ValueError:
        try:
            # Some function that throws an exception if the object does not have an undesired property
            # Error Handling
        except AttributeError:
            try:
                # Some function that throws an exception if an error happens
            except Exception:
                # Exception handling
except ValueError:
    # Exception handling

Is there a neater way to do this? Something like:

try:
   # Some assignment that throws an exception if the object is not in the list
   try:
       # Some assignment function that throws an exception if the object is not already in the database
   except ValueError:
       # Some function that throws an exception if the object does not have an undesired property
   exceptexcept AttributeError:
       # Some function that throws an exception if an error happens
   exceptexcept Exception:
       # Exception handling 
except ValueError:
   # Exception handling
9
  • In your last block, what is your intent with exceptexcept? If these were simply except, then your code would work, likely as you expect. (With the caveat that the outer ValueError would only catch exceptions that occurred outside the inner try block (as those would be caught by, at least, the last except Exception block). Commented Nov 10, 2017 at 5:17
  • I'm trying to catch an exception in the except clause, not in the try. Commented Nov 10, 2017 at 5:18
  • 2
    Assuming you are trying to catch errors in the error handling code, then there is nothing to be done. You'll have to work with this nested hierarchy. Commented Nov 10, 2017 at 5:19
  • Sorry for the misunderstanding, in the case you describe I agree with coldspeed. Commented Nov 10, 2017 at 5:20
  • 1
    Like they said, you're stuck with the nesting. So the moral of the story is: try to avoid using code in except blocks that can raise exceptions. ;) Commented Nov 10, 2017 at 5:21

1 Answer 1

6

This sounds like a loop where you want to keep trying until you succeed or run out of options. So you could implement it that way, e.g., something like this

# Each pair contains a function to call and an exception that can be caught.
# If that exception is raised, the next function will be tried.
action_list = [
    (get_from_list, ValueError),  # throws ValueError if item can't be retrieved
    (get_from_database, ValueError),  # throws ValueError if item can't be retrieved
    (get_from_object, AttributeError),  # throws AttributeError if item lacks desired property
]

result = None
for action, ex in action_list:
    try:
        result = action(key)
        break
    except ex:
        continue

You could tidy this up a bit by having all your helper functions raise a custom exception like "NotFound", which you then use as a signal to check the next level, like this:

# actions to try; all raise NotFound if unsuccessful
action_list = [
    get_from_list, get_from_database, get_from_object
]

result = None
for action in action_list:
    try:
        result = action(key)
        break
    except NotFound:
        continue

Or you could put all the steps in a function that returns as soon as it succeeds. This way your assignments could be done in regular code rather than using helper functions:

def get_value(key):

    try:
        return some_list[int(key)]
    except ValueError:
        pass

    try:
        return get_from_database(key)
    except ValueError:
        pass

    try:
        return getattr(some_object, key)
    except AttributeError:
        pass

    return None

If you don't want another function you could abuse a for loop:

result = None
for _ in range(1):

    try:
        result = some_list[int(key)]
        break
    except ValueError:
        pass

    try:
        result = get_from_database(key)
        break
    except ValueError:
        pass

    try:
        result = getattr(some_object, key)
        break
    except AttributeError:
        pass

Or you could use a single outer try/except with a custom Found exception as a "structured goto":

result = None
try:
    try:
        result = some_list[int(key)]
        raise Found
    except ValueError:
        pass
    try:
        result = get_from_database(key)
        raise Found
    except ValueError:
        pass
    try:
        result = getattr(some_object, key)
        raise Found
    except AttributeError:
        pass
except Found:
    pass  # all good

Or there's this tried-but-true construct:

result = None
if result is None:
    try:
        result = some_list[int(key)]
    except ValueError:
        pass
if result is None:
    try:
        result = get_from_database(key)
    except ValueError:
        pass
if result is None:
    try:
        result = getattr(some_object, key)
    except AttributeError:
        pass
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.