Repository: joblib/joblib

Issue:
Regression: joblib.hash fails on 0d array
```python
import numpy as np
import joblib

arr = np.array(0)
joblib.hash(arr)
```

Works with joblib 0.9.4 and fails on master with the following error:
```
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-4-c182cb63f185> in <module>()
----> 1 joblib.hash(arr)

/home/le243287/dev/alt-scikit-learn/sklearn/externals/joblib/hashing.py in hash(obj, hash_name, coerce_mmap)
    256     else:
    257         hasher = Hasher(hash_name=hash_name)
--> 258     return hasher.hash(obj)

/home/le243287/dev/alt-scikit-learn/sklearn/externals/joblib/hashing.py in hash(self, obj, return_digest)
     66     def hash(self, obj, return_digest=True):
     67         try:
---> 68             self.dump(obj)
     69         except pickle.PicklingError as e:
     70             e.args += ('PicklingError while hashing %r: %r' % (obj, e),)

/volatile/le243287/miniconda3/lib/python3.5/pickle.py in dump(self, obj)
    406         if self.proto >= 4:
    407             self.framer.start_framing()
--> 408         self.save(obj)
    409         self.write(STOP)
    410         self.framer.end_framing()

/home/le243287/dev/alt-scikit-learn/sklearn/externals/joblib/hashing.py in save(self, obj)
    205             # taking the memoryview.
    206             self._hash.update(
--> 207                 self._getbuffer(obj_c_contiguous.view(self.np.uint8)))
    208 
    209             # We store the class, to be able to distinguish between

ValueError: new type not compatible with array.
```

Full disclosure I discovered this when running the nilearn tests using joblib master.

Discussion:
aabadie: @lesteve, I addressed your previous comments and squashed the commits.
lesteve: Thanks a lot, merging !

Patch:
diff --git a/joblib/hashing.py b/joblib/hashing.py
index a6e5337..ced817b 100644
--- a/joblib/hashing.py
+++ b/joblib/hashing.py
@@ -189,7 +189,11 @@ class NumpyHasher(Hasher):
         if isinstance(obj, self.np.ndarray) and not obj.dtype.hasobject:
             # Compute a hash of the object
             # The update function of the hash requires a c_contiguous buffer.
-            if obj.flags.c_contiguous:
+            if obj.shape == ():
+                # 0d arrays need to be flattened because viewing them as bytes
+                # raises a ValueError exception.
+                obj_c_contiguous = obj.flatten()
+            elif obj.flags.c_contiguous:
                 obj_c_contiguous = obj
             elif obj.flags.f_contiguous:
                 obj_c_contiguous = obj.T


Test patch:
diff --git a/joblib/test/test_hashing.py b/joblib/test/test_hashing.py
index b197167..d36e4d8 100644
--- a/joblib/test/test_hashing.py
+++ b/joblib/test/test_hashing.py
@@ -402,6 +402,16 @@ def test_hashes_are_different_between_c_and_fortran_contiguous_arrays():
     assert_not_equal(hash(arr_c), hash(arr_f))
 
 
+@with_numpy
+def test_0d_array():
+    hash(np.array(0))
+
+
+@with_numpy
+def test_0d_and_1d_array_hashing_is_different():
+    assert_not_equal(hash(np.array(0)), hash(np.array([0])))
+
+
 @with_numpy
 def test_hashes_stay_the_same_with_numpy_objects():
     # We want to make sure that hashes don't change with joblib