Author: Maciej Fijalkowski <fij...@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 _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit