Repository: joblib/joblib

Issue:
Error propagating exceptions on-non subclassable types
I was using joblib to run 8 optimizations using nlopt in parallel, and got this error:

```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-138-0fe3676d9e18> in <module>()
     31     for i in range(iterations):
     32         pool = Parallel(n_jobs=max_jobs, verbose=5)
---> 33         out = pool(delayed(nlopt_optimization)(job_id) for job_id in range(max_jobs))
     34         solutions.extend(out)
     35 

/home/federico/anaconda/lib/python2.7/site-packages/joblib/parallel.pyc in __call__(self, iterable)
    658                 # consumption.
    659                 self._iterating = False
--> 660             self.retrieve()
    661             # Make sure that we get a last message telling us we are done
    662             elapsed_time = time.time() - self._start_time

/home/federico/anaconda/lib/python2.7/site-packages/joblib/parallel.pyc in retrieve(self)
    540                             )
    541                         # Convert this to a JoblibException
--> 542                         exception_type = _mk_exception(exception.etype)[0]
    543                         raise exception_type(report)
    544                     raise exception

/home/federico/anaconda/lib/python2.7/site-packages/joblib/my_exceptions.pyc in _mk_exception(exception, name)
     67         this_exception = type(this_name, (exception, JoblibException),
     68                     dict(__repr__=JoblibException.__repr__,
---> 69                         __str__=JoblibException.__str__),
     70                     )
     71         _exception_mapping[this_name] = this_exception

TypeError: type 'nlopt.ForcedStop' is not an acceptable base type
```

My understanding is because a bunch of nlopt types are actually all generated using SWIG, and cannot be naturally inherited.  Should there be a check before joblib tries creating a nice custom exception that the type can indeed be inherited from?

Discussion:
ogrisel: LGTM, can you please add a changelog entry and merge?

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index e08fc89..66531c1 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -4,6 +4,11 @@ Latest changes
 Release 0.9.4
 -------------
 
+Loïc Estève
+
+    FIX for raising non inheritable exceptions in a Parallel call. See
+    https://github.com/joblib/joblib/issues/269 for more details.
+
 Alexandre Abadie
 
     FIX joblib.hash error with mixed types sets and dicts containing mixed
diff --git a/joblib/my_exceptions.py b/joblib/my_exceptions.py
index 500fcd7..9d26cb5 100644
--- a/joblib/my_exceptions.py
+++ b/joblib/my_exceptions.py
@@ -64,14 +64,20 @@ def _mk_exception(exception, name=None):
         this_exception = _exception_mapping[this_name]
     else:
         if exception is Exception:
-            # We cannot create a subclass: we are already a trivial
-            # subclass
+            # JoblibException is already a subclass of Exception. No
+            # need to use multiple inheritance
             return JoblibException, this_name
-        elif issubclass(exception, JoblibException):
-            return JoblibException, JoblibException.__name__
-        this_exception = type(
-            this_name, (JoblibException, exception), {})
-        _exception_mapping[this_name] = this_exception
+        try:
+            this_exception = type(
+                this_name, (JoblibException, exception), {})
+            _exception_mapping[this_name] = this_exception
+        except TypeError:
+            # This happens if "Cannot create a consistent method
+            # resolution order", e.g. because 'exception' is a
+            # subclass of JoblibException or 'exception' is not an
+            # acceptable base class
+            this_exception = JoblibException
+
     return this_exception, this_name
 
 


Test patch:
diff --git a/joblib/test/test_my_exceptions.py b/joblib/test/test_my_exceptions.py
index 7c396ca..b283434 100644
--- a/joblib/test/test_my_exceptions.py
+++ b/joblib/test/test_my_exceptions.py
@@ -42,6 +42,16 @@ def test_inheritance_special_cases():
         assert_true(my_exceptions._mk_exception(exception)[0] is
                     my_exceptions.JoblibException)
 
+    # Non-inheritable exception classes should be mapped to
+    # JoblibException by _mk_exception. That can happen with classes
+    # generated with SWIG. See
+    # https://github.com/joblib/joblib/issues/269 for a concrete
+    # example.
+    non_inheritable_classes = [type(lambda: None), bool]
+    for exception in non_inheritable_classes:
+        assert_true(my_exceptions._mk_exception(exception)[0] is
+                    my_exceptions.JoblibException)
+
 
 def test__mk_exception():
     # Check that _mk_exception works on a bunch of different exceptions