189

What is the best way to get exceptions' messages from components of standard library in Python?

I noticed that in some cases you can get it via message field like this:

try:
  pass
except Exception as ex:
  print(ex.message)

but in some cases (for example, in case of socket errors) you have to do something like this:

try:
  pass
except socket.error as ex:
  print(ex)

I wondered is there any standard way to cover most of these situations?

FrozenHeart
  • 19,844
  • 33
  • 126
  • 242

5 Answers5

177

If you look at the documentation for the built-in errors, you'll see that most Exception classes assign their first argument as a message attribute. Not all of them do though.

Notably,EnvironmentError (with subclasses IOError and OSError) has a first argument of errno, second of strerror. There is no message... strerror is roughly analogous to what would normally be a message.

More generally, subclasses of Exception can do whatever they want. They may or may not have a message attribute. Future built-in Exceptions may not have a message attribute. Any Exception subclass imported from third-party libraries or user code may not have a message attribute.

I think the proper way of handling this is to identify the specific Exception subclasses you want to catch, and then catch only those instead of everything with an except Exception, then utilize whatever attributes that specific subclass defines however you want.

If you must print something, I think that printing the caught Exception itself is most likely to do what you want, whether it has a message attribute or not.

You could also check for the message attribute if you wanted, like this, but I wouldn't really suggest it as it just seems messy:

try:
    pass
except Exception as e:
    # Just print(e) is cleaner and more likely what you want,
    # but if you insist on printing message specifically whenever possible...
    if hasattr(e, 'message'):
        print(e.message)
    else:
        print(e)
ArtOfWarfare
  • 20,617
  • 19
  • 137
  • 193
  • Thanks for the answer. Is there any particular reason to use `str(ex)` instead of just `ex`? – FrozenHeart Oct 20 '15 at 14:57
  • 6
    @FrozenHeart - `print()` automatically calls `str()` for you. There's no reason to manually call it yourself, although it's harmless since calling `str()` on a string will just return the string itself. – ArtOfWarfare Oct 20 '15 at 15:44
  • 1
    @ArtOfWarfare I think there might be issues with `str()` if message is of type `unicode`. – kratenko Oct 20 '15 at 15:59
70

To improve on the answer provided by @artofwarfare, here is what I consider a neater way to check for the message attribute and print it or print the Exception object as a fallback.

try:
    pass 
except Exception as e:
    print getattr(e, 'message', repr(e))

The call to repr is optional, but I find it necessary in some use cases.


Update #1:

Following the comment by @MadPhysicist, here's a proof of why the call to repr might be necessary. Try running the following code in your interpreter:

try:
    raise Exception 
except Exception as e:
    print(getattr(e, 'message', repr(e)))
    print(getattr(e, 'message', str(e)))

The repr(e) line will print Exception() and the str(e) line will print an empty string.


Update #2:

Here is a demo with specifics for Python 2.7 and 3.5: https://gist.github.com/takwas/3b7a6edddef783f2abddffda1439f533

Alan Porter
  • 2,339
  • 1
  • 17
  • 12
  • 1
    `getattr` throws an exception if the object passed in doesn't have the requested attribute. If you wanted to do something like that, I think you should use the optional third argument for `getattr`, like this: `print getattr(e, 'message', e)`. Debatably better than what I did. Another option would be `print(e.message if hasattr(e, 'message') else e)`. – ArtOfWarfare Aug 06 '17 at 14:34
  • @ArtOfWarfare: Interestingly, I had just discovered that and was coming to update this answer. It works fine in Python 2. The issue you raise is with Python 3. – Olúwátóósìn Anímáṣahun Aug 06 '17 at 14:51
  • Right. Somehow, it seemed to work earlier. Thanks for pointing it out. – Olúwátóósìn Anímáṣahun Aug 06 '17 at 15:13
  • 2
    You would almost certainly want `str` instead of `repr`. – Mad Physicist Aug 11 '17 at 17:59
  • @MadPhysicist I have updated the answer with a proof of why you "might" need `repr`. – Olúwátóósìn Anímáṣahun Aug 12 '17 at 06:44
  • I don't have an interpreter nearby , could you show a sample output please ? – Mad Physicist Aug 12 '17 at 18:18
  • Your example is actually perfect for my case: http://ideone.com/jJUoyC. You want to see the message as being empty, not the constructor for your object. – Mad Physicist Aug 14 '17 at 17:54
  • Yes. You should try this: https://gist.github.com/takwas/3b7a6edddef783f2abddffda1439f533 – Olúwátóósìn Anímáṣahun Aug 14 '17 at 18:24
  • 2
    Compared to `str`, `repr` just adds the class name. Instead of using `repr`, I'd propose to use `print('{e.__class__.__name__}: {e}'.format(e=e))`. The print output is cleaner and adheres to the output of raise itself. – kadee Aug 01 '19 at 09:16
  • 1
    Based on the above, I found the most useful for me to be `'{e.__class__.__module__}.{e.__class__.__name__}: {e}'` – Shadi Dec 23 '19 at 14:15
  • 3
    Thanks!! `repr(e)` was for now the only solution that helped me print at least a minimal amount of info on an error I catch in some particular circumstance. `repr(e)` yields `KeyError(0,)` (which is what the error is), while `str(e)` or `e.message` yielded only `0` or nothing at all respectively. – FlorianH Apr 16 '20 at 10:39
9

I too had the same problem. Digging into this I found that the Exception class has an args attribute, which captures the arguments that were used to create the exception. If you narrow the exceptions that except will catch to a subset, you should be able to determine how they were constructed, and thus which argument contains the message.

try:
   # do something that may raise an AuthException
except AuthException as ex:
   if ex.args[0] == "Authentication Timeout.":
      # handle timeout
   else:
      # generic handling
user2501097
  • 1,011
  • 8
  • 4
7
from traceback import format_exc


try:
    fault = 10/0
except ZeroDivision:
    print(format_exc())

Another possibility is to use the format_exc() method from the traceback module.

idik
  • 872
  • 2
  • 10
  • 19
-3

I had the same problem. I think the best solution is to use log.exception, which will automatically print out stack trace and error message, such as:

import logging as log

try:
    pass
    log.info('Success')
except:
    log.exception('Failed')
Bill Z
  • 11
  • 2
  • 3
    What is `log`? I just tried `import log` on Python 2.7.13 and got a message that there is no module with that name. Is it something you added via `pip` or was it added in a newer version of python (I know, I know, I need to update to Python 3... I'll do that just as soon as CentOS stops shipping with Python 2 by default...) – ArtOfWarfare Feb 20 '19 at 16:55
  • 12
    He/She is probably using the logging module from python and then instantiating a logger as a log variable. `import logging`\n `log = logging.getLogger(__name__)` – Josepas Feb 28 '19 at 11:10