[pypy-commit] pypy default: merge

fijal noreply at buildbot.pypy.org
Wed Jun 3 09:17:22 CEST 2015


Author: Maciej Fijalkowski <fijall at gmail.com>
Branch: 
Changeset: r77802:05a0a598cdd6
Date: 2015-06-03 09:17 +0200
http://bitbucket.org/pypy/pypy/changeset/05a0a598cdd6/

Log:	merge

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
@@ -5,4 +5,5 @@
 .. this is a revision shortly after release-2.6.0
 .. startrev: 91904d5c5188
 
-
+.. branch: use_min_scalar
+Correctly resolve the output dtype of ufunc(array, scalar) calls.
diff --git a/pypy/module/_cffi_backend/ffi_obj.py b/pypy/module/_cffi_backend/ffi_obj.py
--- a/pypy/module/_cffi_backend/ffi_obj.py
+++ b/pypy/module/_cffi_backend/ffi_obj.py
@@ -95,6 +95,23 @@
             else:
                 raise KeyError    # don't handle this error case here
 
+    def _ffi_bad_type(self, input_text):
+        info = self.ctxobj.info
+        errmsg = rffi.charp2str(info.c_error_message)
+        if len(input_text) > 500:
+            raise oefmt(self.w_FFIError, "%s", errmsg)
+        printable_text = ['?'] * len(input_text)
+        for i in range(len(input_text)):
+            if ' ' <= input_text[i] < '\x7f':
+                printable_text[i] = input_text[i]
+            elif input_text[i] == '\t' or input_text[i] == '\n':
+                printable_text[i] = ' '
+        num_spaces = rffi.getintfield(info, 'c_error_location')
+        raise oefmt(self.w_FFIError, "%s\n%s\n%s^",
+                    rffi.charp2str(info.c_error_message),
+                    ''.join(printable_text),
+                    " " * num_spaces)
+
     @jit.dont_look_inside
     def parse_string_to_type(self, string, consider_fn_as_fnptr):
         # This cannot be made @elidable because it calls general space
@@ -108,11 +125,7 @@
             info = self.ctxobj.info
             index = parse_c_type.parse_c_type(info, string)
             if index < 0:
-                num_spaces = rffi.getintfield(info, 'c_error_location')
-                raise oefmt(self.w_FFIError, "%s\n%s\n%s^",
-                            rffi.charp2str(info.c_error_message),
-                            string,
-                            " " * num_spaces)
+                raise self._ffi_bad_type(string)
             x = realize_c_type.realize_c_type_or_func(
                 self, self.ctxobj.info.c_output, index)
             assert x is not None
diff --git a/pypy/module/_cffi_backend/test/test_ffi_obj.py b/pypy/module/_cffi_backend/test/test_ffi_obj.py
--- a/pypy/module/_cffi_backend/test/test_ffi_obj.py
+++ b/pypy/module/_cffi_backend/test/test_ffi_obj.py
@@ -181,6 +181,12 @@
         assert str(e.value) == ("undefined struct/union name\n"
                                 "struct never_heard_of_s\n"
                                 "       ^")
+        e = raises(ffi.error, ffi.cast, "\t\n\x01\x1f~\x7f\x80\xff", 0)
+        assert str(e.value) == ("identifier expected\n"
+                                "  ??~???\n"
+                                "  ^")
+        e = raises(ffi.error, ffi.cast, "X" * 600, 0)
+        assert str(e.value) == ("undefined type name")
 
     def test_ffi_buffer(self):
         import _cffi_backend as _cffi1_backend
diff --git a/pypy/module/micronumpy/test/test_dtypes.py b/pypy/module/micronumpy/test/test_dtypes.py
--- a/pypy/module/micronumpy/test/test_dtypes.py
+++ b/pypy/module/micronumpy/test/test_dtypes.py
@@ -1129,6 +1129,20 @@
         exc = raises(ValueError, "dtype([('a', '<i8'), ('a', '<f8')])")
         assert exc.value[0] == 'two fields with the same name'
 
+    def test_array_from_record(self):
+        import numpy as np
+        a = np.array(('???', -999, -12345678.9), 
+                     dtype=[('c', '|S3'), ('a', '<i8'), ('b', '<f8')])
+        # Change the order of the keys
+        b = np.array(a, dtype=[('a', '<i8'), ('b', '<f8'), ('c', '|S3')])
+        assert b.base is None
+        assert b.dtype.fields['a'][1] == 0
+        assert b['a'] == -999
+        a = np.array(('N/A', 1e+20, 1e+20, 999999),
+                     dtype=[('name', '|S4'), ('x', '<f8'), 
+                            ('y', '<f8'), ('block', '<i8', (2, 3))])
+        assert (a['block'] == 999999).all()
+
     def test_create_from_dict(self):
         import numpy as np
         import sys
diff --git a/pypy/module/micronumpy/test/test_subtype.py b/pypy/module/micronumpy/test/test_subtype.py
--- a/pypy/module/micronumpy/test/test_subtype.py
+++ b/pypy/module/micronumpy/test/test_subtype.py
@@ -656,6 +656,7 @@
                 self.output += 'In __array_wrap__:'
                 self.output += '   self is %s' % repr(self)
                 self.output += '   arr is %r\n' % (out_arr,)
+                self.output += '   context is %r\n' % (context,)
                 # then just call the parent
                 ret = np.ndarray.__array_wrap__(self, out_arr, context)
                 print 'wrap',self.output
diff --git a/pypy/module/micronumpy/test/test_ufuncs.py b/pypy/module/micronumpy/test/test_ufuncs.py
--- a/pypy/module/micronumpy/test/test_ufuncs.py
+++ b/pypy/module/micronumpy/test/test_ufuncs.py
@@ -1349,3 +1349,6 @@
         assert np.add(np.float16(0), np.longdouble(0)).dtype == np.longdouble
         assert np.add(np.float16(0), np.complex64(0)).dtype == np.complex64
         assert np.add(np.float16(0), np.complex128(0)).dtype == np.complex128
+        assert np.add(np.zeros(5, dtype=np.int8), 257).dtype == np.int16
+        assert np.subtract(np.zeros(5, dtype=np.int8), 257).dtype == np.int16
+        assert np.divide(np.zeros(5, dtype=np.int8), 257).dtype == np.int16
diff --git a/pypy/module/micronumpy/types.py b/pypy/module/micronumpy/types.py
--- a/pypy/module/micronumpy/types.py
+++ b/pypy/module/micronumpy/types.py
@@ -20,7 +20,7 @@
      cast_gcref_to_instance
 from rpython.rtyper.lltypesystem import lltype, rffi, llmemory
 from rpython.tool.sourcetools import func_with_new_name
-from pypy.module.micronumpy import boxes
+from pypy.module.micronumpy import boxes, support
 from pypy.module.micronumpy.concrete import SliceArray, VoidBoxStorage, V_OBJECTSTORE
 from pypy.module.micronumpy.strides import calc_strides
 from . import constants as NPY
@@ -2265,10 +2265,12 @@
     def _coerce(self, space, arr, ofs, dtype, w_items, shape):
         # TODO: Make sure the shape and the array match
         from pypy.module.micronumpy.descriptor import W_Dtype
-        if w_items is not None:
+        if w_items is None:
+            items_w = [None] * shape[0]
+        elif support.issequence_w(space, w_items):
             items_w = space.fixedview(w_items)
         else:
-            items_w = [None] * shape[0]
+            items_w = [w_items] * shape[0]
         subdtype = dtype.subdtype
         assert isinstance(subdtype, W_Dtype)
         itemtype = subdtype.itemtype
@@ -2363,8 +2365,16 @@
     def coerce(self, space, dtype, w_item):
         from pypy.module.micronumpy.base import W_NDimArray
         if isinstance(w_item, boxes.W_VoidBox):
-            return w_item
-        if w_item is not None:
+            if dtype == w_item.dtype:
+                return w_item
+            else:
+                # match up the field names
+                items_w = [None] * len(dtype.fields)
+                for i in range(len(dtype.fields)):
+                    name = dtype.names[i]
+                    if name in w_item.dtype.names:
+                        items_w[i] = w_item.descr_getitem(space, space.wrap(name))
+        elif w_item is not None:
             if space.isinstance_w(w_item, space.w_tuple):
                 if len(dtype.fields) != space.len_w(w_item):
                     raise OperationError(space.w_ValueError, space.wrap(
diff --git a/pypy/module/micronumpy/ufuncs.py b/pypy/module/micronumpy/ufuncs.py
--- a/pypy/module/micronumpy/ufuncs.py
+++ b/pypy/module/micronumpy/ufuncs.py
@@ -21,7 +21,8 @@
 from pypy.module.micronumpy.support import (_parse_signature, product,
         get_storage_as_int, is_rhs_priority_higher)
 from .casting import (
-    can_cast_type, can_cast_to, find_result_type, promote_types)
+    can_cast_type, can_cast_array, can_cast_to,
+    find_result_type, promote_types)
 from .boxes import W_GenericBox, W_ObjectBox
 
 def done_if_true(dtype, val):
@@ -474,7 +475,8 @@
         if out is None:
             if w_res.is_scalar():
                 return w_res.get_scalar_value()
-            w_res = space.call_method(w_obj, '__array_wrap__', w_res)
+            ctxt = space.newtuple([self, space.newtuple([w_obj]), space.wrap(0)])
+            w_res = space.call_method(w_obj, '__array_wrap__', w_res, ctxt)
         return w_res
 
     def call_scalar(self, space, w_arg, in_dtype):
@@ -494,17 +496,12 @@
         return dt_in, dt_out, self.func
 
     def _calc_dtype(self, space, arg_dtype, out=None, casting='unsafe'):
-        use_min_scalar = False
         if arg_dtype.is_object():
             return arg_dtype, arg_dtype
         in_casting = safe_casting_mode(casting)
         for dt_in, dt_out in self.dtypes:
-            if use_min_scalar:
-                if not can_cast_array(space, w_arg, dt_in, in_casting):
-                    continue
-            else:
-                if not can_cast_type(space, arg_dtype, dt_in, in_casting):
-                    continue
+            if not can_cast_type(space, arg_dtype, dt_in, in_casting):
+                continue
             if out is not None:
                 res_dtype = out.get_dtype()
                 if not can_cast_type(space, dt_out, res_dtype, casting):
@@ -604,21 +601,18 @@
                             w_rdtype.get_name(), w_ldtype.get_name(),
                             self.name)
 
-        if self.are_common_types(w_ldtype, w_rdtype):
-            if not w_lhs.is_scalar() and w_rhs.is_scalar():
-                w_rdtype = w_ldtype
-            elif w_lhs.is_scalar() and not w_rhs.is_scalar():
-                w_ldtype = w_rdtype
-        calc_dtype, dt_out, func = self.find_specialization(space, w_ldtype, w_rdtype, out, casting)
         if (isinstance(w_lhs, W_GenericBox) and
                 isinstance(w_rhs, W_GenericBox) and out is None):
-            return self.call_scalar(space, w_lhs, w_rhs, calc_dtype)
+            return self.call_scalar(space, w_lhs, w_rhs, casting)
         if isinstance(w_lhs, W_GenericBox):
             w_lhs = W_NDimArray.from_scalar(space, w_lhs)
         assert isinstance(w_lhs, W_NDimArray)
         if isinstance(w_rhs, W_GenericBox):
             w_rhs = W_NDimArray.from_scalar(space, w_rhs)
         assert isinstance(w_rhs, W_NDimArray)
+        calc_dtype, dt_out, func = self.find_specialization(
+            space, w_ldtype, w_rdtype, out, casting, w_lhs, w_rhs)
+
         new_shape = shape_agreement(space, w_lhs.get_shape(), w_rhs)
         new_shape = shape_agreement(space, new_shape, out, broadcast_down=False)
         w_highpriority, out_subtype = array_priority(space, w_lhs, w_rhs)
@@ -632,10 +626,14 @@
         if out is None:
             if w_res.is_scalar():
                 return w_res.get_scalar_value()
-            w_res = space.call_method(w_highpriority, '__array_wrap__', w_res)
+            ctxt = space.newtuple([self, space.newtuple([w_lhs, w_rhs]), space.wrap(0)])
+            w_res = space.call_method(w_highpriority, '__array_wrap__', w_res, ctxt)
         return w_res
 
-    def call_scalar(self, space, w_lhs, w_rhs, in_dtype):
+    def call_scalar(self, space, w_lhs, w_rhs, casting):
+        in_dtype, out_dtype, func = self.find_specialization(
+            space, w_lhs.get_dtype(space), w_rhs.get_dtype(space),
+            out=None, casting=casting)
         w_val = self.func(in_dtype,
                           w_lhs.convert_to(space, in_dtype),
                           w_rhs.convert_to(space, in_dtype))
@@ -643,7 +641,8 @@
             return w_val.w_obj
         return w_val
 
-    def _find_specialization(self, space, l_dtype, r_dtype, out, casting):
+    def _find_specialization(self, space, l_dtype, r_dtype, out, casting,
+                             w_arg1, w_arg2):
         if (not self.allow_bool and (l_dtype.is_bool() or
                                          r_dtype.is_bool()) or
                 not self.allow_complex and (l_dtype.is_complex() or
@@ -655,15 +654,23 @@
             dtype = find_result_type(space, [], [l_dtype, r_dtype])
             bool_dtype = get_dtype_cache(space).w_booldtype
             return dtype, bool_dtype, self.func
-        dt_in, dt_out = self._calc_dtype(space, l_dtype, r_dtype, out, casting)
+        dt_in, dt_out = self._calc_dtype(
+            space, l_dtype, r_dtype, out, casting, w_arg1, w_arg2)
         return dt_in, dt_out, self.func
 
-    def find_specialization(self, space, l_dtype, r_dtype, out, casting):
+    def find_specialization(self, space, l_dtype, r_dtype, out, casting,
+                            w_arg1=None, w_arg2=None):
         if self.simple_binary:
             if out is None and not (l_dtype.is_object() or r_dtype.is_object()):
-                dtype = promote_types(space, l_dtype, r_dtype)
+                if w_arg1 is not None and w_arg2 is not None:
+                    w_arg1 = convert_to_array(space, w_arg1)
+                    w_arg2 = convert_to_array(space, w_arg2)
+                    dtype = find_result_type(space, [w_arg1, w_arg2], [])
+                else:
+                    dtype = promote_types(space, l_dtype, r_dtype)
                 return dtype, dtype, self.func
-        return self._find_specialization(space, l_dtype, r_dtype, out, casting)
+        return self._find_specialization(
+            space, l_dtype, r_dtype, out, casting, w_arg1, w_arg2)
 
     def find_binop_type(self, space, dtype):
         """Find a valid dtype signature of the form xx->x"""
@@ -684,15 +691,21 @@
             "requested type has type code '%s'" % (self.name, dtype.char))
 
 
-    def _calc_dtype(self, space, l_dtype, r_dtype, out=None, casting='unsafe'):
-        use_min_scalar = False
+    def _calc_dtype(self, space, l_dtype, r_dtype, out, casting,
+                    w_arg1, w_arg2):
         if l_dtype.is_object() or r_dtype.is_object():
             dtype = get_dtype_cache(space).w_objectdtype
             return dtype, dtype
+        use_min_scalar = (w_arg1 is not None and w_arg2 is not None and
+                          ((w_arg1.is_scalar() and not w_arg2.is_scalar()) or
+                           (not w_arg1.is_scalar() and w_arg2.is_scalar())))
         in_casting = safe_casting_mode(casting)
         for dt_in, dt_out in self.dtypes:
             if use_min_scalar:
-                if not can_cast_array(space, w_arg, dt_in, in_casting):
+                w_arg1 = convert_to_array(space, w_arg1)
+                w_arg2 = convert_to_array(space, w_arg2)
+                if not (can_cast_array(space, w_arg1, dt_in, in_casting) and
+                        can_cast_array(space, w_arg2, dt_in, in_casting)):
                     continue
             else:
                 if not (can_cast_type(space, l_dtype, dt_in, in_casting) and


More information about the pypy-commit mailing list