Repository: ipython/ipython

Issue:
BUG: cannot create an OBJECT array from memory buffer
I found a problem with the IPython.parallel (v1.1.0) cluster that is similar but not identical to [this stackoverflow question](http://stackoverflow.com/questions/17013457/ipython-parallel-valueerror-cannot-create-an-object-array-from-memory-buffer).
My guess is that IPython has an optimized way to return numpy arrays from the cluster, but it fails on numpy arrays with `dtype=object`.
This runs as expected:

```
from IPython.parallel import Client
c = Client()
lv = c.load_balanced_view()
import numpy
c[:].execute('import numpy')

def foo(i):
    a = numpy.zeros(3); a.fill(1)
    return a
def bar(i):
    a = numpy.zeros(3, dtype=object); a.fill(1)
    return a
def bar_wrapped(i):
    a = numpy.zeros(3, dtype=object); a.fill(1)
    return {'a': a }

print(foo(1))
print(bar(1))
print(bar_wrapped(1))
```

Any of these lines work

```
lv.map_sync(foo, range(5))
lv.apply_sync(foo, 1)
lv.map_sync(bar_wrapped, range(5))
```

On the other hand, any of these lines give the error:

```
lv.map_sync(bar, range(5))
lv.apply_sync(bar, 1)
lv.apply_sync(bar_wrapped, 1)
```

```
ValueError: cannot create an OBJECT array from memory buffer
```

Furthermore, after attempting map on bar, any other attempts to map/apply any of the functions will fail with the same error. Note that this doesn't happen for apply on bar or apply on bar_wrapped
Restarting the notebook (no need to restart the cluster), will fix the problem (i.e. you can map on foo again)

My system information: (This happened on both Windows/Linux)

```
{'codename': 'An Afternoon Hack',
 'commit_hash': '7c2ea3a',
 'commit_source': 'installation',
 'default_encoding': 'cp437',
 'ipython_path': 'C:\\Python33\\lib\\site-packages\\IPython',
 'ipython_version': '1.1.0',
 'os_name': 'nt',
 'platform': 'Windows-7-6.1.7601-SP1',
 'sys_executable': 'C:\\Python33\\python.exe',
 'sys_platform': 'win32',
 'sys_version': '3.3.2 (v3.3.2:d047928ae3f6, May 16 2013, 00:03:43) [MSC v.1600 32 bit (Intel)]'}
```



Discussion:
Thanks, I'll look into it.


Resolving patch:
diff --git a/IPython/utils/pickleutil.py b/IPython/utils/pickleutil.py
--- a/IPython/utils/pickleutil.py
+++ b/IPython/utils/pickleutil.py
@@ -192,7 +192,15 @@ def __init__(self, obj):
         from numpy import ascontiguousarray
         self.shape = obj.shape
         self.dtype = obj.dtype.descr if obj.dtype.fields else obj.dtype.str
+        self.pickled = False
         if sum(obj.shape) == 0:
+            self.pickled = True
+        elif obj.dtype == 'O':
+            # can't handle object dtype with buffer approach
+            self.pickled = True
+        elif obj.dtype.fields and any(dt == 'O' for dt,sz in obj.dtype.fields.values()):
+            self.pickled = True
+        if self.pickled:
             # just pickle it
             self.buffers = [pickle.dumps(obj, -1)]
         else:
@@ -203,7 +211,7 @@ def __init__(self, obj):
     def get_object(self, g=None):
         from numpy import frombuffer
         data = self.buffers[0]
-        if sum(self.shape) == 0:
+        if self.pickled:
             # no shape, we just pickled it
             return pickle.loads(data)
         else:


Test patch:
diff --git a/IPython/parallel/tests/test_view.py b/IPython/parallel/tests/test_view.py
--- a/IPython/parallel/tests/test_view.py
+++ b/IPython/parallel/tests/test_view.py
@@ -286,6 +286,21 @@ def test_apply_numpy(self):
             C = view.apply_sync(lambda x:x, B)
             assert_array_equal(B,C)
     
+    @skip_without('numpy')
+    def test_apply_numpy_object_dtype(self):
+        """view.apply(f, ndarray) with dtype=object"""
+        import numpy
+        from numpy.testing.utils import assert_array_equal
+        view = self.client[-1]
+        
+        A = numpy.array([dict(a=5)])
+        B = view.apply_sync(lambda x:x, A)
+        assert_array_equal(A,B)
+        
+        A = numpy.array([(0, dict(b=10))], dtype=[('i', int), ('o', object)])
+        B = view.apply_sync(lambda x:x, A)
+        assert_array_equal(A,B)
+    
     @skip_without('numpy')
     def test_push_pull_recarray(self):
         """push/pull recarrays"""