[pypy-commit] pypy default: merge array_interface which implements common usages of __array_interface__

mattip noreply at buildbot.pypy.org
Sun Nov 8 14:50:59 EST 2015


Author: mattip <matti.picus at gmail.com>
Branch: 
Changeset: r80599:8633f40661db
Date: 2015-11-08 21:40 +0200
http://bitbucket.org/pypy/pypy/changeset/8633f40661db/

Log:	merge array_interface which implements common usages of
	__array_interface__

diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -25,3 +25,7 @@
 preserves all int16 values, even across nan conversions. Also fix argmax, argmin
 for nan comparisons
 
+.. branch: array_interface
+
+Support common use-cases for __array_interface__, passes upstream tests
+
diff --git a/pypy/module/micronumpy/compile.py b/pypy/module/micronumpy/compile.py
--- a/pypy/module/micronumpy/compile.py
+++ b/pypy/module/micronumpy/compile.py
@@ -371,6 +371,8 @@
     @specialize.arg(2)
     def call_method(self, w_obj, s, *args):
         # XXX even the hacks have hacks
+        if s == 'size': # used in _array() but never called by tests
+            return IntObject(0)
         return getattr(w_obj, 'descr_' + s)(self, *args)
 
     @specialize.arg(1)
diff --git a/pypy/module/micronumpy/ctors.py b/pypy/module/micronumpy/ctors.py
--- a/pypy/module/micronumpy/ctors.py
+++ b/pypy/module/micronumpy/ctors.py
@@ -2,6 +2,7 @@
 from pypy.interpreter.gateway import unwrap_spec, WrappedDefault
 from rpython.rlib.buffer import SubBuffer
 from rpython.rlib.rstring import strip_spaces
+from rpython.rlib.rawstorage import RAW_STORAGE_PTR
 from rpython.rtyper.lltypesystem import lltype, rffi
 
 from pypy.module.micronumpy import descriptor, loop, support
@@ -45,7 +46,7 @@
     try:
         w_interface = space.getattr(w_object, space.wrap("__array_interface__"))
         if w_interface is None:
-            return None
+            return None, False
         version_w = space.finditem(w_interface, space.wrap("version"))
         if version_w is None:
             raise oefmt(space.w_ValueError, "__array_interface__ found without"
@@ -67,19 +68,46 @@
             raise oefmt(space.w_ValueError,
                     "__array_interface__ missing one or more required keys: shape, typestr"
                     )
-        raise oefmt(space.w_NotImplementedError,
-                    "creating array from __array_interface__ not supported yet")
-        '''
-        data_w = space.listview()
+        if w_descr is not None:
+            raise oefmt(space.w_NotImplementedError,
+                    "__array_interface__ descr not supported yet")
+        if w_strides is None or space.is_w(w_strides, space.w_None):
+            strides = None
+        else:
+            strides = [space.int_w(i) for i in space.listview(w_strides)]
         shape = [space.int_w(i) for i in space.listview(w_shape)]
         dtype = descriptor.decode_w_dtype(space, w_dtype)
-        rw = space.is_true(data_w[1])
-        '''
-        #print 'create view from shape',shape,'dtype',dtype,'descr',w_descr,'data',data_w[0],'rw',rw
-        return None
+        if dtype is None:
+            raise oefmt(space.w_ValueError,
+                    "__array_interface__ could not decode dtype %R", w_dtype
+                    )
+        if w_data is not None and (space.isinstance_w(w_data, space.w_tuple) or space.isinstance_w(w_data, space.w_list)):
+            data_w = space.listview(w_data)
+            data = rffi.cast(RAW_STORAGE_PTR, space.int_w(data_w[0]))
+            read_only = True # XXX why not space.is_true(data_w[1])
+            offset = 0
+            return W_NDimArray.from_shape_and_storage(space, shape, data, 
+                                    dtype, strides=strides, start=offset), read_only
+        if w_data is None:
+            data = w_object
+        else:
+            data = w_data
+        w_offset = space.finditem(w_interface, space.wrap('offset'))
+        if w_offset is None:
+            offset = 0
+        else:
+            offset = space.int_w(w_offset)
+        #print 'create view from shape',shape,'dtype',dtype,'data',data
+        if strides is not None:
+            raise oefmt(space.w_NotImplementedError,
+                   "__array_interface__ strides not fully supported yet") 
+        arr = frombuffer(space, data, dtype, support.product(shape), offset)
+        new_impl = arr.implementation.reshape(arr, shape)
+        return W_NDimArray(new_impl), False
+        
     except OperationError as e:
         if e.match(space, space.w_AttributeError):
-            return None
+            return None, False
         raise
 
 
@@ -103,19 +131,20 @@
     if space.isinstance_w(w_object, space.w_type):
         raise oefmt(space.w_ValueError, "cannot create ndarray from type instance")
     # for anything that isn't already an array, try __array__ method first
+    dtype = descriptor.decode_w_dtype(space, w_dtype)
     if not isinstance(w_object, W_NDimArray):
         w_array = try_array_method(space, w_object, w_dtype)
         if w_array is not None:
             # continue with w_array, but do further operations in place
             w_object = w_array
             copy = False
+            dtype = w_object.get_dtype()
     if not isinstance(w_object, W_NDimArray):
-        w_array = try_interface_method(space, w_object)
+        w_array, _copy = try_interface_method(space, w_object)
         if w_array is not None:
             w_object = w_array
-            copy = False
-    dtype = descriptor.decode_w_dtype(space, w_dtype)
-
+            copy = _copy
+            dtype = w_object.get_dtype()
 
     if isinstance(w_object, W_NDimArray):
         npy_order = order_converter(space, w_order, NPY.ANYORDER)
diff --git a/pypy/module/micronumpy/test/test_ndarray.py b/pypy/module/micronumpy/test/test_ndarray.py
--- a/pypy/module/micronumpy/test/test_ndarray.py
+++ b/pypy/module/micronumpy/test/test_ndarray.py
@@ -3070,7 +3070,7 @@
         assert (b == zeros(10)).all()
 
     def test_array_interface(self):
-        from numpy import array
+        from numpy import array, ones
         a = array(2.5)
         i = a.__array_interface__
         assert isinstance(i['data'][0], int)
@@ -3093,7 +3093,7 @@
 
         class Dummy(object):
             def __init__(self, aif=None):
-                if aif:
+                if aif is not None:
                     self.__array_interface__ = aif
 
         a = array(Dummy())
@@ -3102,6 +3102,31 @@
         raises(ValueError, array, Dummy({'version': 0}))
         raises(ValueError, array, Dummy({'version': 'abc'}))
         raises(ValueError, array, Dummy({'version': 3}))
+        raises(TypeError, array, Dummy({'version': 3, 'typestr': 'f8', 'shape': ('a', 3)}))
+
+        a = array([1, 2, 3])
+        b = array(Dummy(a.__array_interface__))
+        b[1] = 200
+        assert a[1] == 2 # upstream compatibility, is this a bug?
+        interface_a = a.__array_interface__
+        interface_b = b.__array_interface__
+        # only the data[0] value should differ
+        assert interface_a['data'][0] != interface_b['data'][0]
+        assert interface_b['data'][1] == interface_a['data'][1]
+        interface_b.pop('data')
+        interface_a.pop('data')
+        assert interface_a == interface_b
+
+        b = array(Dummy({'version':3, 'shape': (50,), 'typestr': 'u1',
+                         'data': 'a'*100}))
+        assert b.dtype == 'uint8'
+        assert b.shape == (50,)
+
+        a = ones((1,), dtype='float16')
+        b = Dummy(a.__array_interface__)
+        c = array(b)
+        assert c.dtype == 'float16'
+        assert (a == c).all()
 
     def test_array_indexing_one_elem(self):
         from numpy import array, arange


More information about the pypy-commit mailing list