Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a custom exception class with multiple init args pickleable

Why does my custom Exception class below not serialize/unserialize correctly using the pickle module?

import pickle  class MyException(Exception):     def __init__(self, arg1, arg2):         self.arg1 = arg1         self.arg2 = arg2          super(MyException, self).__init__(arg1)  e = MyException("foo", "bar")  str = pickle.dumps(e) obj = pickle.loads(str) 

This code throws the following error:

Traceback (most recent call last): File "test.py", line 13, in <module>    obj = pickle.loads(str) File "/usr/lib/python2.7/pickle.py", line 1382, in loads    return Unpickler(file).load() File "/usr/lib/python2.7/pickle.py", line 858, in load    dispatch[key](self) File "/usr/lib/python2.7/pickle.py", line 1133, in load_reduce    value = func(*args) TypeError: __init__() takes exactly 3 arguments (2 given) 

I'm sure this problem stems from a lack of knowledge on my part of how to make a class pickle-friendly. Interestingly, this problem doesn't occur when my class doesn't extend Exception.

Thanks for any help. Kyle

EDIT: Fixing my call to super per shx2 EDIT: Cleaning up title/content

like image 833
kylejmcintyre Avatar asked Apr 26 '13 20:04

kylejmcintyre


People also ask

How do you throw a custom exception in Python?

In Python, users can define custom exceptions by creating a new class. This exception class has to be derived, either directly or indirectly, from the built-in Exception class. Most of the built-in exceptions are also derived from this class.

What keyword can you use to define a new exception class in Python?

In any case, you only need __init__ or __str__ if you do something different from what Exception itself does.

How do I get an exception message in Python?

The most common method to catch and print the exception message in Python is by using except and try statement. You can also save its error message using this method. Another method is to use logger.

What are exceptions Python?

An exception is an event, which occurs during the execution of a program that disrupts the normal flow of the program's instructions. In general, when a Python script encounters a situation that it cannot cope with, it raises an exception. An exception is a Python object that represents an error.


2 Answers

The current answers break down if you're using both arguments to construct an error message to pass to the parent Exception class. I believe the best way is to simply override the __reduce__ method in your exception. The __reduce__ method should return a two item tuple. The first item in the tuple is your class. The second item is a tuple containing the arguments to pass to your class's __init__ method.

import pickle  class MyException(Exception):     def __init__(self, arg1, arg2):         self.arg1 = arg1         self.arg2 = arg2          super(MyException, self).__init__('arg1: {}, arg2: {}'.format(arg1, arg2))      def __reduce__(self):         return (MyException, (self.arg1, self.arg2))   original = MyException('foo', 'bar') print repr(original) print original.arg1 print original.arg2  reconstituted = pickle.loads(pickle.dumps(original)) print repr(reconstituted) print reconstituted.arg1 print reconstituted.arg2 

More info about __reduce__ here.

like image 185
Sean Avatar answered Sep 28 '22 17:09

Sean


Make arg2 optional:

class MyException(Exception):     def __init__(self, arg1, arg2=None):         self.arg1 = arg1         self.arg2 = arg2         super(MyException, self).__init__(arg1) 

The base Exception class defines a .__reduce__() method to make the extension (C-based) type picklable and that method only expects one argument (which is .args); see the BaseException_reduce() function in the C source.

The easiest work-around is making extra arguments optional. The __reduce__ method also includes any additional object attributes beyond .args and .message and your instances are recreated properly:

>>> e = MyException('foo', 'bar') >>> e.__reduce__() (<class '__main__.MyException'>, ('foo',), {'arg1': 'foo', 'arg2': 'bar'}) >>> pickle.loads(pickle.dumps(e)) MyException('foo',) >>> e2 = pickle.loads(pickle.dumps(e)) >>> e2.arg1 'foo' >>> e2.arg2 'bar' 
like image 38
Martijn Pieters Avatar answered Sep 28 '22 18:09

Martijn Pieters