2

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
id01
  • 1,353
  • 1
  • 13
  • 19
  • 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). – jedwards Nov 10 '17 at 05:17
  • I'm trying to catch an exception in the except clause, not in the try. – id01 Nov 10 '17 at 05: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. – cs95 Nov 10 '17 at 05:19
  • Sorry for the misunderstanding, in the case you describe I agree with coldspeed. – jedwards Nov 10 '17 at 05: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. ;) – PM 2Ring Nov 10 '17 at 05:21
  • @cᴏʟᴅsᴘᴇᴇᴅ thanks. I've noticed that this type of Python code is a bit more messy than it should be, but looks like I'll just have to live with it. – id01 Nov 10 '17 at 05:21
  • Recursive calls to the function with the try except in it? Like if you catch a value error, try to fix it in the exception, then call the same function again? – Jack Homan Nov 10 '17 at 05:28
  • @JackHoman Sure, that would work, but in practice it's rare where you couldn't do the same thing with a simple `while` loop rather than recursion. Another option is to simply let the exceptions bubble up to the calling code, where you have a chain of (un-nested) `except` clauses, assuming that the calling code is able to handle those exceptions. – PM 2Ring Nov 10 '17 at 05:34
  • For the first *two*; `object is not in the list` and `is not already in the database` - can you discern which is which from the accompanying error message? – wwii Nov 10 '17 at 05:41

2 Answers2

5

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
Matthias Fripp
  • 16,256
  • 5
  • 25
  • 40
-1

yes. You could make use of exception hierarchy in python. should be careful in the order of the exceptions. The except on which the error is captured will executed and rest all are ignored.

Do,

inspect.getclasstree(inspect.getmro(Exception))

 BaseException
... Exception
...... StandardError
......... TypeError
......... ImportError
............ ZipImportError
......... EnvironmentError
............ IOError
............... ItimerError
............ OSError
......... EOFError
......... RuntimeError
............ NotImplementedError
......... NameError
............ UnboundLocalError
......... AttributeError
......... SyntaxError
............ IndentationError
............... TabError
......... LookupError
............ IndexError
............ KeyError
............ CodecRegistryError
......... ValueError
............ UnicodeError
............... UnicodeEncodeError
............... UnicodeDecodeError
............... UnicodeTranslateError
......... AssertionError
......... ArithmeticError
............ FloatingPointError
............ OverflowError
............ ZeroDivisionError
......... SystemError
............ CodecRegistryError
......... ReferenceError
......... MemoryError
......... BufferError
Prakash Palnati
  • 2,871
  • 20
  • 32
  • 2
    How should OP make use of it? Posting the exception tree doesn't make it very obvious. – cs95 Nov 10 '17 at 05:13
  • I'm not trying to catch different exceptions in the first function, but instead, in each successive block, I'm trying to catch the exception in the previous block (the handler). I do not see any better way to do this than having nested try-except blocks, but it gets very messy that way. I'm asking for a better way. – id01 Nov 10 '17 at 05:13
  • 1
    Did you plagiarise this answer from https://stackoverflow.com/a/18296681/4909087? – cs95 Nov 10 '17 at 05:14
  • there is nothing to be plagarised for an obvious answer and I copied the tree from a notepad saved way back in 2014. – Prakash Palnati Nov 10 '17 at 05:15