From 2650a534f3d3c86d6c8d84b10ab9547895c5c1a8 Mon Sep 17 00:00:00 2001
From: Douglas RAILLARD <douglas.raillard@arm.com>
Date: Thu, 7 Nov 2019 13:39:19 +0000
Subject: [PATCH] exception: Fix DevlibError unpickling

Unpickling of BaseException is done by feeding self.args to the exception type.
This self.args attribute is initialized in two places: in
BaseException.__new__ (before __init__ is called) and in BaseException.__init__
as well.

The following code ends up with self.args == ('hello',), instead of (1, 2):

    class MyExcep(BaseException):
          def __init__(self, foo, bar):
              print('before super().__init__()', self.args)
              super().__init__('hello')
              print('after super().__init__()', self.args)

    MyExcep(1, 2)
    # Prints:
    # before super().__init__() (1, 2)
    # after super().__init__() ('hello',)

When unplickling such instance, ('hello',) will be fed to MyExcep.__init__(),
which will fail with a TypeError since it requires 2 positional arguments.

In order to fix that, super().__init__() needs to be handwritten instead of
getting the one from BaseException:

    class MyBase(BaseException):
        def __init__(self, msg):
            self.msg = msg

    class MyExcep(MyBase):
          def __init__(self, foo, bar):
              print('before super().__init__()', self.args)
              super().__init__('hello')
              print('after super().__init__()', self.args)

    MyExcep(1, 2)
    # Prints:
    # before super().__init__() (1, 2)
    # after super().__init__() (1, 2)

This will correctly initialize self.args == (1, 2), allowing unpickling to work.
---
 devlib/exception.py | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/devlib/exception.py b/devlib/exception.py
index 2f5018c..0042cb1 100644
--- a/devlib/exception.py
+++ b/devlib/exception.py
@@ -15,11 +15,17 @@
 
 class DevlibError(Exception):
     """Base class for all Devlib exceptions."""
+
+    def __init__(self, *args):
+        message = args[0] if args else None
+        self._message = message
+
     @property
     def message(self):
-        if self.args:
-            return self.args[0]
-        return str(self)
+        if self._message is not None:
+            return self._message
+        else:
+            return str(self)
 
 
 class DevlibStableError(DevlibError):