17

When I try to serialize an exception using json.dump, I get errors like

TypeError: IOError('socket error', error(61, 'Connection refused')) is not JSON serializable

and

TypeError: error(61, 'Connection refused') is not JSON serializable

The __dict__ field of exceptions is {} (this is why How to make a class JSON serializable does not help me: the answers there assume that __dict__ contains all the necessary information, they also assume that I have control over the class to be serialized).

Is there something more intelligent that saving str(exn)?

I would prefer a human-readable text representation (not pickle).

PS. Here is what I came up with:

def exception_as_dict(ex):
    return dict(type=ex.__class__.__name__,
                errno=ex.errno, message=ex.message,
                strerror=exception_as_dict(ex.strerror)
                if isinstance(ex.strerror,Exception) else ex.strerror)

json.dumps(exception_as_dict(err),indent=2)
{
  "errno": "socket error", 
  "type": "IOError", 
  "strerror": {
    "errno": 61, 
    "type": "error", 
    "strerror": "Connection refused"
  }
}
sds
  • 58,617
  • 29
  • 161
  • 278
  • You could capture the exception object and pickle it? You can pass the pickled data as a binary payload in a JSON object. – cs95 Jul 21 '17 at 14:50
  • @cᴏʟᴅsᴘᴇᴇᴅ: I would prefer a human-readable text representation. Your comment suggests that the answer is "no". Thanks. – sds Jul 21 '17 at 14:54
  • You didn't make that clear. It seemed like you wanted some way of communicating execptions remotely. Anyway, you'll have to look for some 3rd party library, or write your own custom parser to deconstruct or construct the object to/from JSON. – cs95 Jul 21 '17 at 15:05
  • 1
    Possible duplicate of [How to make a class JSON serializable](https://stackoverflow.com/questions/3768895/how-to-make-a-class-json-serializable) – Leon Jul 21 '17 at 15:19
  • 4
    @Leon: not really a dupe: `__dict__` is useless in `Exception`. – sds Jul 21 '17 at 15:29
  • Just pass the exception through `str()` see the original post - https://stackoverflow.com/questions/4460669/python-getting-the-error-message-of-an-exception?lq=1 – niceman Dec 26 '17 at 00:45

2 Answers2

15

You can use exc_info with traceback as below:

import traceback
import sys
try:
    raise KeyError('aaa!!!')
except Exception as e:
    exc_info = sys.exc_info()
    print(''.join(traceback.format_exception(*exc_info)))
David Weinberg
  • 1,033
  • 1
  • 13
  • 29
10

Exceptions can not be pickled (by default), you have two options:

  1. Use Python's built in format_exc() and serialize the formatted string.

  2. Use tblib

With the latter, you can pass wrapped exceptions and also reraise them later.

import tblib.pickling_support
tblib.pickling_support.install()
import pickle, sys 

def inner_0():
    raise Exception('fail')

def inner_1():
    inner_0()

def inner_2():
    inner_1()

try:
    inner_2()
except:
    s1 = pickle.dumps(sys.exc_info())
knipknap
  • 5,934
  • 7
  • 39
  • 43