snazzybouche
snazzybouche

Reputation: 2424

How do I catch only a specific error in Python3?

Say I have a function that can produce a vast variety of errors.

I have a ValueError that I need to catch, a specific AttributeError, and then I also need to handle any other type of error.

try:
  func()
except AttributeError as e:
  if "specific error text" in str(e):
    print("The specific AttributeError occurred")
  else:
    raise
except ValueError:
  print("A value error occurred")
except Exception as e:
  print("Another error occurred: {}".format(str(e)))

Problem: If func() bubbles an AttributeError that's not the specific one I'm looking for, in this case, it'll be re-raised and not handled how I want it to be handled (via the general Exception handler).

How do I force non-specific errors to be handled further down in the chain, without duplicating code from the Exception section into the AttributeError section?

Upvotes: 1

Views: 1725

Answers (2)

Azat Ibrakov
Azat Ibrakov

Reputation: 11019

As an option you can process AttributeError and ValueError in one try-except block and all other Exceptions on the top level like

try:
    try:
        func()
    except AttributeError as e:
        if "specific error text" in str(e):
            print("The specific AttributeError occurred")
        else:
            raise
    except ValueError:
        print("A value error occurred")
except Exception as e:
    print("Another error occurred: {}".format(str(e)))

this may look a bit ugly though, so we can extract inner try-except block in a separate function like

def func_with_expected_exceptions_handling():
    try:
        func()
    except AttributeError as e:
        if "specific error text" in str(e):
            print("The specific AttributeError occurred")
        else:
            raise
    except ValueError:
        print("A value error occurred")

and after that

try:
    func_with_expected_exceptions_handling()
except Exception as e:
    print("Another error occurred: {}".format(str(e)))

this doesn't save us from an actual nested structure, but it may come in handy if this kind of func processing arises in other places.

BTW, I don't think checking for a specific error message in exception is a good idea, we need a little bit more context to see if it can be done easier.

EDIT

If I understood correctly your func looks like

def func(...):
    getattr(COMMANDS, cmd.command).command(irc_c, msg, cmd)

and you want to handle error from getattr call.

I can see next options here:

  • Wrap getattr call in try-except and process AttributeError in-place

    def func(...):
        try:
            commander = getattr(COMMANDS, cmd.command)
        except AttributeError:
            print('Command {} not found'.format(cmd.command))
        else:
            commander.command(irc_c, msg, cmd)
    
  • Wrap getattr call in try-except, re-raise a custom exception (or ValueError) and process it afterwards in OP try-except

    class CommandNotFound(Exception): pass
    
    
    def func(...):
        try:
            commander = getattr(COMMANDS, cmd.command)
        except AttributeError:
            raise CommandNotFound()  # or we can use `ValueError` instead
        else:
            commander.command(irc_c, msg, cmd)
    
  • Use default parameter of getattr function and make some kind of logging there like

    class DefaultCommand:
        def command(self, irc_c, msg, cmd):
            print("Command {} is not found".format(cmd.command))
    

    and after that used like

    getattr(COMMANDS, cmd.command, DefaultCommand()).command(irc_c, msg, cmd)
    

Upvotes: 2

Netwave
Netwave

Reputation: 42796

Basically you need to handle the specific error first. From more general to more specific, i.e Exception => AttributeError => YourError

>>> try:
...     raise MyCustomAttrErr("Hey, this failed!")
... except MyCustomAttrErr as e:
...     print(e)
... except AttributteError as e:
...     print("Attribute error raised")
... except Exception as e:
...     print("Base exception raised")
... 
Hey, this failed!

Python handled the except blocks in order from top to bottom and stops in the first block that captures it.

Upvotes: 1

Related Questions