Author: Richard Plangger <planri...@gmail.com> Branch: vecopt-merge Changeset: r80229:04ab6b2dc6ff Date: 2015-10-15 09:33 +0200 http://bitbucket.org/pypy/pypy/changeset/04ab6b2dc6ff/
Log: merged default diff too long, truncating to 2000 out of 2228 lines 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 @@ -75,7 +75,13 @@ remain valid as long as the target exists (unlike the previous version, where handles become invalid *before* the __del__ is called). +.. branch: ufunc-casting + +allow automatic casting in ufuncs (and frompypyfunc) to cast the +arguments to the allowed function type declarations, fixes various +failures in linalg cffi functions + .. branch: vecopt .. branch: vecopt-merge -A new optimization pass to use SIMD instructions for trace loop that allow this +A new optimization pass to use emit vectorized loops diff --git a/pypy/module/cpyext/__init__.py b/pypy/module/cpyext/__init__.py --- a/pypy/module/cpyext/__init__.py +++ b/pypy/module/cpyext/__init__.py @@ -66,6 +66,7 @@ import pypy.module.cpyext.codecs import pypy.module.cpyext.pyfile import pypy.module.cpyext.pystrtod +import pypy.module.cpyext.pytraceback # now that all rffi_platform.Struct types are registered, configure them api.configure_types() diff --git a/pypy/module/cpyext/include/Python.h b/pypy/module/cpyext/include/Python.h --- a/pypy/module/cpyext/include/Python.h +++ b/pypy/module/cpyext/include/Python.h @@ -126,6 +126,7 @@ #include "fileobject.h" #include "pysignals.h" #include "pythread.h" +#include "traceback.h" /* Missing definitions */ #include "missing.h" diff --git a/pypy/module/cpyext/include/frameobject.h b/pypy/module/cpyext/include/frameobject.h --- a/pypy/module/cpyext/include/frameobject.h +++ b/pypy/module/cpyext/include/frameobject.h @@ -4,7 +4,7 @@ extern "C" { #endif -typedef struct { +typedef struct _frame { PyObject_HEAD PyCodeObject *f_code; PyObject *f_globals; diff --git a/pypy/module/cpyext/include/traceback.h b/pypy/module/cpyext/include/traceback.h --- a/pypy/module/cpyext/include/traceback.h +++ b/pypy/module/cpyext/include/traceback.h @@ -4,7 +4,15 @@ extern "C" { #endif -typedef PyObject PyTracebackObject; +struct _frame; + +typedef struct _traceback { + PyObject_HEAD + struct _traceback *tb_next; + struct _frame *tb_frame; + int tb_lasti; + int tb_lineno; +} PyTracebackObject; #ifdef __cplusplus } diff --git a/pypy/module/cpyext/pytraceback.py b/pypy/module/cpyext/pytraceback.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/pytraceback.py @@ -0,0 +1,50 @@ +from rpython.rtyper.lltypesystem import rffi, lltype +from pypy.module.cpyext.api import ( + PyObjectFields, generic_cpy_call, CONST_STRING, CANNOT_FAIL, Py_ssize_t, + cpython_api, bootstrap_function, cpython_struct, build_type_checkers) +from pypy.module.cpyext.pyobject import ( + PyObject, make_ref, from_ref, Py_DecRef, make_typedescr, borrow_from) +from pypy.module.cpyext.frameobject import PyFrameObject +from rpython.rlib.unroll import unrolling_iterable +from pypy.interpreter.error import OperationError +from pypy.interpreter.pytraceback import PyTraceback +from pypy.interpreter import pycode + + +PyTracebackObjectStruct = lltype.ForwardReference() +PyTracebackObject = lltype.Ptr(PyTracebackObjectStruct) +PyTracebackObjectFields = PyObjectFields + ( + ("tb_next", PyTracebackObject), + ("tb_frame", PyFrameObject), + ("tb_lasti", rffi.INT), + ("tb_lineno", rffi.INT), +) +cpython_struct("PyTracebackObject", PyTracebackObjectFields, PyTracebackObjectStruct) + +@bootstrap_function +def init_traceback(space): + make_typedescr(PyTraceback.typedef, + basestruct=PyTracebackObject.TO, + attach=traceback_attach, + dealloc=traceback_dealloc) + + +def traceback_attach(space, py_obj, w_obj): + py_traceback = rffi.cast(PyTracebackObject, py_obj) + traceback = space.interp_w(PyTraceback, w_obj) + if traceback.next is None: + w_next_traceback = None + else: + w_next_traceback = space.wrap(traceback.next) + py_traceback.c_tb_next = rffi.cast(PyTracebackObject, make_ref(space, w_next_traceback)) + py_traceback.c_tb_frame = rffi.cast(PyFrameObject, make_ref(space, space.wrap(traceback.frame))) + rffi.setintfield(py_traceback, 'c_tb_lasti', traceback.lasti) + rffi.setintfield(py_traceback, 'c_tb_lineno',traceback.get_lineno()) + +@cpython_api([PyObject], lltype.Void, external=False) +def traceback_dealloc(space, py_obj): + py_traceback = rffi.cast(PyTracebackObject, py_obj) + Py_DecRef(space, rffi.cast(PyObject, py_traceback.c_tb_next)) + Py_DecRef(space, rffi.cast(PyObject, py_traceback.c_tb_frame)) + from pypy.module.cpyext.object import PyObject_dealloc + PyObject_dealloc(space, py_obj) diff --git a/pypy/module/cpyext/slotdefs.py b/pypy/module/cpyext/slotdefs.py --- a/pypy/module/cpyext/slotdefs.py +++ b/pypy/module/cpyext/slotdefs.py @@ -61,6 +61,30 @@ args_w = space.fixedview(w_args) return generic_cpy_call(space, func_binary, w_self, args_w[0]) +def wrap_binaryfunc_l(space, w_self, w_args, func): + func_binary = rffi.cast(binaryfunc, func) + check_num_args(space, w_args, 1) + args_w = space.fixedview(w_args) + + if not space.is_true(space.issubtype(space.type(args_w[0]), + space.type(w_self))): + raise OperationError(space.w_NotImplementedError, space.wrap( + "NotImplemented")) + + return generic_cpy_call(space, func_binary, w_self, args_w[0]) + +def wrap_binaryfunc_r(space, w_self, w_args, func): + func_binary = rffi.cast(binaryfunc, func) + check_num_args(space, w_args, 1) + args_w = space.fixedview(w_args) + + if not space.is_true(space.issubtype(space.type(args_w[0]), + space.type(w_self))): + raise OperationError(space.w_NotImplementedError, space.wrap( + "NotImplemented")) + + return generic_cpy_call(space, func_binary, args_w[0], w_self) + def wrap_inquirypred(space, w_self, w_args, func): func_inquiry = rffi.cast(inquiry, func) check_num_args(space, w_args, 0) diff --git a/pypy/module/cpyext/test/test_traceback.py b/pypy/module/cpyext/test/test_traceback.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_traceback.py @@ -0,0 +1,40 @@ +from rpython.rtyper.lltypesystem import lltype, rffi +from pypy.module.cpyext.test.test_api import BaseApiTest +from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref +from pypy.module.cpyext.pytraceback import PyTracebackObject +from pypy.interpreter.pytraceback import PyTraceback +from pypy.interpreter.pyframe import PyFrame + +class TestPyTracebackObject(BaseApiTest): + def test_traceback(self, space, api): + w_traceback = space.appexec([], """(): + import sys + try: + 1/0 + except: + return sys.exc_info()[2] + """) + py_obj = make_ref(space, w_traceback) + py_traceback = rffi.cast(PyTracebackObject, py_obj) + assert (from_ref(space, rffi.cast(PyObject, py_traceback.c_ob_type)) is + space.gettypeobject(PyTraceback.typedef)) + + traceback = space.interp_w(PyTraceback, w_traceback) + assert traceback.lasti == py_traceback.c_tb_lasti + assert traceback.get_lineno() == py_traceback.c_tb_lineno + assert space.eq_w(space.getattr(w_traceback, space.wrap("tb_lasti")), + space.wrap(py_traceback.c_tb_lasti)) + assert space.is_w(space.getattr(w_traceback, space.wrap("tb_frame")), + from_ref(space, rffi.cast(PyObject, + py_traceback.c_tb_frame))) + + while not space.is_w(w_traceback, space.w_None): + assert space.is_w( + w_traceback, + from_ref(space, rffi.cast(PyObject, py_traceback))) + w_traceback = space.getattr(w_traceback, space.wrap("tb_next")) + py_traceback = py_traceback.c_tb_next + + assert lltype.normalizeptr(py_traceback) is None + + api.Py_DecRef(py_obj) diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py --- a/pypy/module/cpyext/test/test_typeobject.py +++ b/pypy/module/cpyext/test/test_typeobject.py @@ -589,6 +589,48 @@ assert bool(module.newInt(-1)) raises(ValueError, bool, module.newInt(-42)) + def test_binaryfunc(self): + module = self.import_extension('foo', [ + ("new_obj", "METH_NOARGS", + """ + FooObject *fooObj; + + Foo_Type.tp_as_number = &foo_as_number; + foo_as_number.nb_add = foo_nb_add_call; + if (PyType_Ready(&Foo_Type) < 0) return NULL; + fooObj = PyObject_New(FooObject, &Foo_Type); + if (!fooObj) { + return NULL; + } + + return (PyObject *)fooObj; + """)], + """ + typedef struct + { + PyObject_HEAD + } FooObject; + + static PyObject * + foo_nb_add_call(PyObject *self, PyObject *other) + { + return PyInt_FromLong(42); + } + + PyTypeObject Foo_Type = { + PyObject_HEAD_INIT(0) + /*ob_size*/ 0, + /*tp_name*/ "Foo", + /*tp_basicsize*/ sizeof(FooObject), + }; + static PyNumberMethods foo_as_number; + """) + a = module.new_obj() + b = module.new_obj() + c = 3 + assert (a + b) == 42 + raises(NotImplementedError, "b + c") + def test_tp_new_in_subclass_of_type(self): skip("BROKEN") module = self.import_module(name='foo3') diff --git a/pypy/module/micronumpy/concrete.py b/pypy/module/micronumpy/concrete.py --- a/pypy/module/micronumpy/concrete.py +++ b/pypy/module/micronumpy/concrete.py @@ -70,7 +70,10 @@ @jit.unroll_safe def setslice(self, space, arr): - if len(arr.get_shape()) > len(self.get_shape()): + if arr.get_size() == 1: + # we can always set self[:] = scalar + pass + elif len(arr.get_shape()) > len(self.get_shape()): # record arrays get one extra dimension if not self.dtype.is_record() or \ len(arr.get_shape()) > len(self.get_shape()) + 1: diff --git a/pypy/module/micronumpy/loop.py b/pypy/module/micronumpy/loop.py --- a/pypy/module/micronumpy/loop.py +++ b/pypy/module/micronumpy/loop.py @@ -161,10 +161,10 @@ call_many_to_one_driver = jit.JitDriver( name='numpy_call_many_to_one', - greens=['shapelen', 'nin', 'func', 'res_dtype'], + greens=['shapelen', 'nin', 'func', 'in_dtypes', 'res_dtype'], reds='auto') -def call_many_to_one(space, shape, func, res_dtype, in_args, out): +def call_many_to_one(space, shape, func, in_dtypes, res_dtype, in_args, out): # out must hav been built. func needs no calc_type, is usually an # external ufunc nin = len(in_args) @@ -182,9 +182,9 @@ vals = [None] * nin while not out_iter.done(out_state): call_many_to_one_driver.jit_merge_point(shapelen=shapelen, func=func, - res_dtype=res_dtype, nin=nin) + in_dtypes=in_dtypes, res_dtype=res_dtype, nin=nin) for i in range(nin): - vals[i] = in_iters[i].getitem(in_states[i]) + vals[i] = in_dtypes[i].coerce(space, in_iters[i].getitem(in_states[i])) w_arglist = space.newlist(vals) w_out_val = space.call_args(func, Arguments.frompacked(space, w_arglist)) out_iter.setitem(out_state, res_dtype.coerce(space, w_out_val)) @@ -195,10 +195,10 @@ call_many_to_many_driver = jit.JitDriver( name='numpy_call_many_to_many', - greens=['shapelen', 'nin', 'nout', 'func', 'res_dtype'], + greens=['shapelen', 'nin', 'nout', 'func', 'in_dtypes', 'out_dtypes'], reds='auto') -def call_many_to_many(space, shape, func, res_dtype, in_args, out_args): +def call_many_to_many(space, shape, func, in_dtypes, out_dtypes, in_args, out_args): # out must hav been built. func needs no calc_type, is usually an # external ufunc nin = len(in_args) @@ -221,24 +221,29 @@ out_states[i] = out_state shapelen = len(shape) vals = [None] * nin - while not out_iters[0].done(out_states[0]): + test_iter, test_state = in_iters[-1], in_states[-1] + if nout > 0: + test_iter, test_state = out_iters[0], out_states[0] + while not test_iter.done(test_state): call_many_to_many_driver.jit_merge_point(shapelen=shapelen, func=func, - res_dtype=res_dtype, nin=nin, nout=nout) + in_dtypes=in_dtypes, out_dtypes=out_dtypes, + nin=nin, nout=nout) for i in range(nin): - vals[i] = in_iters[i].getitem(in_states[i]) + vals[i] = in_dtypes[i].coerce(space, in_iters[i].getitem(in_states[i])) w_arglist = space.newlist(vals) w_outvals = space.call_args(func, Arguments.frompacked(space, w_arglist)) # w_outvals should be a tuple, but func can return a single value as well if space.isinstance_w(w_outvals, space.w_tuple): batch = space.listview(w_outvals) for i in range(len(batch)): - out_iters[i].setitem(out_states[i], res_dtype.coerce(space, batch[i])) + out_iters[i].setitem(out_states[i], out_dtypes[i].coerce(space, batch[i])) out_states[i] = out_iters[i].next(out_states[i]) - else: - out_iters[0].setitem(out_states[0], res_dtype.coerce(space, w_outvals)) + elif nout > 0: + out_iters[0].setitem(out_states[0], out_dtypes[0].coerce(space, w_outvals)) out_states[0] = out_iters[0].next(out_states[0]) for i in range(nin): in_states[i] = in_iters[i].next(in_states[i]) + test_state = test_iter.next(test_state) return space.newtuple([convert_to_array(space, o) for o in out_args]) setslice_driver = jit.JitDriver(name='numpy_setslice', 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 @@ -159,8 +159,7 @@ af2 = ufunc(af) assert all(af2 == af * 2) ac = arange(10, dtype=complex) - skip('casting not implemented yet') - ac1 = ufunc(ac) + raises(TypeError, ufunc, ac) def test_frompyfunc_2d_sig(self): import sys @@ -199,6 +198,10 @@ ai2 = ufunc(aiV) assert (ai2 == aiV * 2).all() + ai = arange(0).reshape(0, 1, 1) + ao = ufunc(ai) + assert ao.shape == (0, 1, 1) + def test_frompyfunc_needs_nditer(self): import sys from numpy import frompyfunc, dtype, arange @@ -268,6 +271,76 @@ assert out0.shape == in0.shape assert (out0 == in0 * 2).all() + def test_frompyfunc_casting(self): + import sys + import numpy as np + if '__pypy__' not in sys.builtin_module_names: + skip('PyPy only frompyfunc extension') + + def times2_int(in0, out0): + assert in0.dtype == int + assert out0.dtype == int + # hack to assing to a 0-dim array + out0.real = in0 * 2 + + def times2_complex(in0, out0): + assert in0.dtype == complex + assert out0.dtype == complex + out0.real = in0.real * 2 + out0.imag = in0.imag + + def times2_complex0(in0): + assert in0.dtype == complex + return in0 * 2 + + def times2_int0(in0): + assert in0.dtype == int + return in0 * 2 + + times2stacked = np.frompyfunc([times2_int, times2_complex], 1, 1, + dtypes=[np.dtype(int), np.dtype(int), + np.dtype(complex), np.dtype(complex)], + stack_inputs=True, signature='()->()', + ) + times2 = np.frompyfunc([times2_int0, times2_complex0], 1, 1, + dtypes=[np.dtype(int), np.dtype(int), + np.dtype(complex), np.dtype(complex)], + stack_inputs=False, + ) + for d in [np.dtype(float), np.dtype('uint8'), np.dtype('complex64')]: + in0 = np.arange(4, dtype=d) + out0 = times2stacked(in0) + assert out0.shape == in0.shape + assert out0.dtype in (int, complex) + assert (out0 == in0 * 2).all() + + out0 = times2(in0) + assert out0.shape == in0.shape + assert out0.dtype in (int, complex) + assert (out0 == in0 * 2).all() + + def test_frompyfunc_scalar(self): + import sys + import numpy as np + if '__pypy__' not in sys.builtin_module_names: + skip('PyPy only frompyfunc extension') + + def summer(in0): + out = np.empty(1, in0.dtype) + out[0] = in0.sum() + return out + + pysummer = np.frompyfunc([summer, summer], 1, 1, + dtypes=[np.dtype(int), np.dtype(int), + np.dtype(complex), np.dtype(complex)], + stack_inputs=False, signature='(m,m)->()', + ) + for d in [np.dtype(float), np.dtype('uint8'), np.dtype('complex64')]: + in0 = np.arange(4, dtype=d).reshape(1, 2, 2) + out0 = pysummer(in0) + assert out0 == in0.sum() + assert out0.dtype in (int, complex) + def test_ufunc_kwargs(self): from numpy import ufunc, frompyfunc, arange, dtype def adder(a, b): @@ -1393,7 +1466,7 @@ def test_add_doc(self): import sys if '__pypy__' not in sys.builtin_module_names: - skip('') + skip('cpython sets docstrings differently') try: from numpy import set_docstring except ImportError: 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 @@ -709,6 +709,32 @@ raise oefmt(space.w_TypeError, "ufunc '%s' not supported for the input types", self.name) +def _match_dtypes(space, indtypes, targetdtypes, i_target, casting): + allok = True + for i in range(len(indtypes)): + origin = indtypes[i] + target = targetdtypes[i + i_target] + if origin is None: + continue + if target is None: + continue + if not can_cast_type(space, origin, target, casting): + allok = False + break + return allok + +def _raise_err_msg(self, space, dtypes0, dtypes1): + dtypesstr = '' + for d in dtypes0: + if d is None: + dtypesstr += 'None,' + else: + dtypesstr += '%s%s%s,' % (d.byteorder, d.kind, d.elsize) + _dtypesstr = ','.join(['%s%s%s' % (d.byteorder, d.kind, d.elsize) \ + for d in dtypes1]) + raise oefmt(space.w_TypeError, + "input dtype [%s] did not match any known dtypes [%s] ", + dtypesstr,_dtypesstr) class W_UfuncGeneric(W_Ufunc): @@ -799,29 +825,36 @@ outargs0 = outargs[0] assert isinstance(inargs0, W_NDimArray) assert isinstance(outargs0, W_NDimArray) + nin = self.nin + assert nin >= 0 res_dtype = outargs0.get_dtype() new_shape = inargs0.get_shape() # XXX use _find_array_wrap and wrap outargs using __array_wrap__ + if self.stack_inputs: + loop.call_many_to_many(space, new_shape, func, + dtypes, [], inargs + outargs, []) + if len(outargs) < 2: + return outargs[0] + return space.newtuple(outargs) if len(outargs) < 2: return loop.call_many_to_one(space, new_shape, func, - res_dtype, inargs, outargs[0]) + dtypes[:nin], dtypes[-1], inargs, outargs[0]) return loop.call_many_to_many(space, new_shape, func, - res_dtype, inargs, outargs) + dtypes[:nin], dtypes[nin:], inargs, outargs) + w_casting = space.w_None + w_op_dtypes = space.w_None for tf in need_to_cast: if tf: - raise oefmt(space.w_NotImplementedError, "casting not supported yet") + w_casting = space.wrap('safe') + w_op_dtypes = space.newtuple([space.wrap(d) for d in dtypes]) + w_flags = space.w_None # NOT 'external_loop', we do coalescing by core_num_dims - w_op_flags = space.newtuple([space.wrap(r) for r in ['readonly'] * len(inargs)] + \ - [space.wrap(r) for r in ['readwrite'] * len(outargs)]) - w_op_dtypes = space.w_None - w_casting = space.w_None + w_ro = space.newtuple([space.wrap('readonly'), space.wrap('copy')]) + w_rw = space.newtuple([space.wrap('readwrite'), space.wrap('updateifcopy')]) + + w_op_flags = space.newtuple([w_ro] * len(inargs) + [w_rw] * len(outargs)) w_op_axes = space.w_None - #print '\nsignature', sig - #print [(d, getattr(self,d)) for d in dir(self) if 'core' in d or 'broad' in d] - #print [(d, locals()[d]) for d in locals() if 'core' in d or 'broad' in d] - #print 'shapes',[d.get_shape() for d in inargs + outargs] - #print 'steps',[d.implementation.strides for d in inargs + outargs] if isinstance(func, W_GenericUFuncCaller): # Use GeneralizeUfunc interface with signature # Unlike numpy, we will not broadcast dims before @@ -934,19 +967,32 @@ # linear_search_type_resolver in numpy ufunc_type_resolutions.c # type_tup can be '', a tuple of dtypes, or a string # of the form d,t -> D where the letters are dtype specs - nop = len(inargs) + len(outargs) + + # XXX why does the next line not pass translation? + # dtypes = [i.get_dtype() for i in inargs] dtypes = [] + for i in inargs: + if isinstance(i, W_NDimArray): + dtypes.append(i.get_dtype()) + else: + dtypes.append(None) + for i in outargs: + if isinstance(i, W_NDimArray): + dtypes.append(i.get_dtype()) + else: + dtypes.append(None) if isinstance(type_tup, str) and len(type_tup) > 0: try: if len(type_tup) == 1: - dtypes = [get_dtype_cache(space).dtypes_by_name[type_tup]] * self.nargs + s_dtypes = [get_dtype_cache(space).dtypes_by_name[type_tup]] * self.nargs elif len(type_tup) == self.nargs + 2: + s_dtypes = [] for i in range(self.nin): - dtypes.append(get_dtype_cache(space).dtypes_by_name[type_tup[i]]) + s_dtypes.append(get_dtype_cache(space).dtypes_by_name[type_tup[i]]) #skip the '->' in the signature for i in range(self.nout): j = i + self.nin + 2 - dtypes.append(get_dtype_cache(space).dtypes_by_name[type_tup[j]]) + s_dtypes.append(get_dtype_cache(space).dtypes_by_name[type_tup[j]]) else: raise oefmt(space.w_TypeError, "a type-string for %s " \ "requires 1 typecode or %d typecode(s) before and %d" \ @@ -955,42 +1001,29 @@ except KeyError: raise oefmt(space.w_ValueError, "unknown typecode in" \ " call to %s with type-string '%s'", self.name, type_tup) - else: - # XXX why does the next line not pass translation? - # dtypes = [i.get_dtype() for i in inargs] - for i in inargs: - if isinstance(i, W_NDimArray): - dtypes.append(i.get_dtype()) - else: - dtypes.append(None) - for i in outargs: - if isinstance(i, W_NDimArray): - dtypes.append(i.get_dtype()) - else: - dtypes.append(None) + # Make sure args can be cast to dtypes + if not _match_dtypes(space, dtypes, s_dtypes, 0, "safe"): + _raise_err_msg(self, space, dtypes, s_dtypes) + dtypes = s_dtypes #Find the first matchup of dtypes with _dtypes for i in range(0, len(_dtypes), self.nargs): - allok = True - for j in range(self.nargs): - if dtypes[j] is not None and dtypes[j] != _dtypes[i+j]: - allok = False + allok = _match_dtypes(space, dtypes, _dtypes, i, "no") if allok: break else: - if len(self.funcs) > 1: - - dtypesstr = '' - for d in dtypes: - if d is None: - dtypesstr += 'None,' - else: - dtypesstr += '%s%s%s,' % (d.byteorder, d.kind, d.elsize) - _dtypesstr = ','.join(['%s%s%s' % (d.byteorder, d.kind, d.elsize) \ - for d in _dtypes]) - raise oefmt(space.w_TypeError, - "input dtype [%s] did not match any known dtypes [%s] ", - dtypesstr,_dtypesstr) - i = 0 + # No exact matches, can we cast? + for i in range(0, len(_dtypes), self.nargs): + allok = _match_dtypes(space, dtypes, _dtypes, i, "safe") + if allok: + end = i + self.nargs + assert i >= 0 + assert end >=0 + dtypes = _dtypes[i:end] + break + else: + if len(self.funcs) > 1: + _raise_err_msg(self, space, dtypes, _dtypes) + i = 0 # Fill in empty dtypes for j in range(self.nargs): if dtypes[j] is None: @@ -1086,7 +1119,7 @@ for j in range(offset, len(iter_shape)): x = iter_shape[j + offset] y = dims_to_broadcast[j] - if (x > y and x % y) or y %x: + if y != 0 and x != 0 and ((x > y and x % y) or y %x): raise oefmt(space.w_ValueError, "%s: %s operand %d has a " "mismatch in its broadcast dimension %d " "(size %d is different from %d)", @@ -1123,7 +1156,7 @@ # the current op (signalling it can handle ndarray's). # TODO parse and handle subok - # TODO handle flags, op_flags + # TODO handle more flags, op_flags #print 'iter_shape',iter_shape,'arg_shapes',arg_shapes,'matched_dims',matched_dims return iter_shape, arg_shapes, matched_dims diff --git a/rpython/annotator/bookkeeper.py b/rpython/annotator/bookkeeper.py --- a/rpython/annotator/bookkeeper.py +++ b/rpython/annotator/bookkeeper.py @@ -8,12 +8,12 @@ from contextlib import contextmanager from rpython.flowspace.model import Constant -from rpython.annotator.model import (SomeOrderedDict, - SomeString, SomeChar, SomeFloat, unionof, SomeInstance, SomeDict, - SomeBuiltin, SomePBC, SomeInteger, TLS, SomeUnicodeCodePoint, +from rpython.annotator.model import ( + SomeOrderedDict, SomeString, SomeChar, SomeFloat, unionof, SomeInstance, + SomeDict, SomeBuiltin, SomePBC, SomeInteger, TLS, SomeUnicodeCodePoint, s_None, s_ImpossibleValue, SomeBool, SomeTuple, SomeImpossibleValue, SomeUnicodeString, SomeList, HarmlesslyBlocked, - SomeWeakRef, SomeByteArray, SomeConstantType, SomeProperty) + SomeWeakRef, SomeByteArray, SomeConstantType, SomeProperty, AnnotatorError) from rpython.annotator.classdef import InstanceSource, ClassDef from rpython.annotator.listdef import ListDef, ListItem from rpython.annotator.dictdef import DictDef @@ -225,7 +225,8 @@ x = int(x) result = SomeInteger(nonneg = x>=0) else: - raise Exception("seeing a prebuilt long (value %s)" % hex(x)) + # XXX: better error reporting? + raise ValueError("seeing a prebuilt long (value %s)" % hex(x)) elif issubclass(tp, str): # py.lib uses annotated str subclasses no_nul = not '\x00' in x if len(x) == 1: diff --git a/rpython/annotator/classdef.py b/rpython/annotator/classdef.py --- a/rpython/annotator/classdef.py +++ b/rpython/annotator/classdef.py @@ -112,14 +112,10 @@ for desc in s_newvalue.descriptions: if desc.selfclassdef is None: if homedef.classdesc.settled: - raise Exception("demoting method %s " - "to settled class %s not " - "allowed" % - (self.name, homedef) - ) - #self.bookkeeper.warning("demoting method %s " - # "to base class %s" % - # (self.name, homedef)) + raise AnnotatorError( + "demoting method %s to settled class " + "%s not allowed" % (self.name, homedef) + ) break # check for attributes forbidden by slots or _attrs_ diff --git a/rpython/annotator/description.py b/rpython/annotator/description.py --- a/rpython/annotator/description.py +++ b/rpython/annotator/description.py @@ -801,8 +801,9 @@ s_init = basedesc.s_read_attribute('__init__') parent_has_init = isinstance(s_init, SomePBC) if has_init and not parent_has_init: - raise Exception("some subclasses among %r declare __init__()," - " but not the common parent class" % (descs,)) + raise AnnotatorError( + "some subclasses among %r declare __init__()," + " but not the common parent class" % (descs,)) # make a PBC of MethodDescs, one for the __init__ of each class initdescs = [] for desc, classdef in zip(descs, classdefs): diff --git a/rpython/annotator/test/test_annrpython.py b/rpython/annotator/test/test_annrpython.py --- a/rpython/annotator/test/test_annrpython.py +++ b/rpython/annotator/test/test_annrpython.py @@ -4,10 +4,12 @@ from rpython.conftest import option from rpython.annotator import model as annmodel +from rpython.annotator.model import AnnotatorError, UnionError from rpython.annotator.annrpython import RPythonAnnotator as _RPythonAnnotator +from rpython.annotator.classdef import NoSuchAttrError from rpython.translator.translator import graphof as tgraphof from rpython.annotator.policy import AnnotatorPolicy -from rpython.annotator.signature import Sig +from rpython.annotator.signature import Sig, SignatureError from rpython.annotator.listdef import ListDef, ListChangeUnallowed from rpython.annotator.dictdef import DictDef from rpython.flowspace.model import * @@ -213,7 +215,7 @@ def f(): return X().meth() a = self.RPythonAnnotator() - py.test.raises(annmodel.AnnotatorError, a.build_types, f, []) + py.test.raises(AnnotatorError, a.build_types, f, []) def test_methodcall1(self): a = self.RPythonAnnotator() @@ -360,7 +362,7 @@ def f(l): return g(*l) a = self.RPythonAnnotator() - with py.test.raises(annmodel.AnnotatorError): + with py.test.raises(AnnotatorError): a.build_types(f, [[int]]) def test_star_unpack_and_keywords(self): @@ -769,7 +771,8 @@ def f(): return x a = self.RPythonAnnotator(policy=AnnotatorPolicy()) - py.test.raises(Exception, a.build_types, f, []) + with py.test.raises(Exception): + a.build_types(f, []) def test_exception_deduction_with_raise1(self): a = self.RPythonAnnotator() @@ -959,14 +962,16 @@ def f(): return large_constant a = self.RPythonAnnotator() - py.test.raises(Exception, a.build_types, f, []) + with py.test.raises(ValueError): + a.build_types(f, []) # if you want to get a r_uint, you have to be explicit about it def test_add_different_ints(self): def f(a, b): return a + b a = self.RPythonAnnotator() - py.test.raises(Exception, a.build_types, f, [r_uint, int]) + with py.test.raises(UnionError): + a.build_types(f, [r_uint, int]) def test_merge_different_ints(self): def f(a, b): @@ -976,7 +981,8 @@ c = b return c a = self.RPythonAnnotator() - py.test.raises(Exception, a.build_types, f, [r_uint, int]) + with py.test.raises(UnionError): + a.build_types(f, [r_uint, int]) def test_merge_ruint_zero(self): def f(a): @@ -2612,14 +2618,14 @@ def f(): return A() a = self.RPythonAnnotator() - py.test.raises(annmodel.AnnotatorError, a.build_types, f, []) + py.test.raises(AnnotatorError, a.build_types, f, []) # class B(object): pass x = B() def g(): return isinstance(x, A) - py.test.raises(annmodel.AnnotatorError, a.build_types, g, []) + py.test.raises(AnnotatorError, a.build_types, g, []) def test_import_from_mixin(self): class M(object): @@ -2694,7 +2700,8 @@ return a.x # should explode here a = self.RPythonAnnotator() - e = py.test.raises(Exception, a.build_types, f, [int]) + with py.test.raises(NoSuchAttrError) as excinfo: + a.build_types(f, [int]) # this should explode on reading the attribute 'a.x', but it can # sometimes explode on 'self.x = x', which does not make much sense. # But it looks hard to fix in general: we don't know yet during 'a.x' @@ -2928,7 +2935,8 @@ s = a.build_types(fun, [s_nonneg, s_nonneg]) assert isinstance(s, annmodel.SomeInteger) assert not s.nonneg - py.test.raises(Exception, a.build_types, fun, [int, int]) + with py.test.raises(SignatureError): + a.build_types(fun, [int, int]) def test_sig_simpler(self): def fun(x, y): @@ -2940,7 +2948,8 @@ s = a.build_types(fun, [s_nonneg, s_nonneg]) assert isinstance(s, annmodel.SomeInteger) assert not s.nonneg - py.test.raises(Exception, a.build_types, fun, [int, int]) + with py.test.raises(SignatureError): + a.build_types(fun, [int, int]) def test_sig_lambda(self): def fun(x, y): @@ -2954,7 +2963,8 @@ s = a.build_types(fun, [int, s_nonneg]) assert isinstance(s, annmodel.SomeInteger) assert not s.nonneg - py.test.raises(Exception, a.build_types, fun, [s_nonneg, int]) + with py.test.raises(SignatureError): + a.build_types(fun, [s_nonneg, int]) def test_sig_bug(self): def g(x, y=5): @@ -3004,8 +3014,8 @@ if works: a.build_types(fun, [int]) else: - from rpython.annotator.classdef import NoSuchAttrError - py.test.raises(NoSuchAttrError, a.build_types, fun, [int]) + with py.test.raises(NoSuchAttrError): + a.build_types(fun, [int]) def test_slots_enforce_attrs(self): class Superbase(object): @@ -3138,7 +3148,8 @@ return a.n() a = self.RPythonAnnotator() - py.test.raises(Exception, a.build_types, fun, [bool]) + with py.test.raises(AnnotatorError): + a.build_types(fun, [bool]) def test_float_cmp(self): def fun(x, y): @@ -3227,6 +3238,7 @@ assert isinstance(s.items[2], annmodel.SomeInstance) assert s.items[2].flags == {} + @py.test.mark.xfail def test_no_access_directly_on_heap(self): from rpython.rlib.jit import hint @@ -3243,7 +3255,8 @@ i.x = x a = self.RPythonAnnotator() - py.test.raises(Exception, a.build_types, f, []) + with py.test.raises(AnnotatorError): + a.build_types(f, []) class M: @@ -3267,7 +3280,7 @@ c.m.l.append(x) a = self.RPythonAnnotator() - py.test.raises(AssertionError, a.build_types, f, []) + py.test.raises(AnnotatorError, a.build_types, f, []) def f(): x = A() @@ -3275,7 +3288,7 @@ c.m.d[None] = x a = self.RPythonAnnotator() - py.test.raises(AssertionError, a.build_types, f, []) + py.test.raises(AnnotatorError, a.build_types, f, []) def f(): x = A() @@ -3283,7 +3296,7 @@ c.m.d[x] = None a = self.RPythonAnnotator() - py.test.raises(AssertionError, a.build_types, f, []) + py.test.raises(AnnotatorError, a.build_types, f, []) def test_ctr_location(self): class A: @@ -3342,7 +3355,8 @@ if g(x, y): g(x, r_uint(y)) a = self.RPythonAnnotator() - py.test.raises(Exception, a.build_types, f, [int, int]) + with py.test.raises(UnionError): + a.build_types(f, [int, int]) def test_compare_with_zero(self): def g(): @@ -3464,22 +3478,22 @@ return '%s' % unichr(x) a = self.RPythonAnnotator() - py.test.raises(annmodel.AnnotatorError, a.build_types, f, [int]) + py.test.raises(AnnotatorError, a.build_types, f, [int]) def f(x): return '%s' % (unichr(x) * 3) a = self.RPythonAnnotator() - py.test.raises(annmodel.AnnotatorError, a.build_types, f, [int]) + py.test.raises(AnnotatorError, a.build_types, f, [int]) def f(x): return '%s%s' % (1, unichr(x)) a = self.RPythonAnnotator() - py.test.raises(annmodel.AnnotatorError, a.build_types, f, [int]) + py.test.raises(AnnotatorError, a.build_types, f, [int]) def f(x): return '%s%s' % (1, unichr(x) * 15) a = self.RPythonAnnotator() - py.test.raises(annmodel.AnnotatorError, a.build_types, f, [int]) + py.test.raises(AnnotatorError, a.build_types, f, [int]) def test_strformatting_tuple(self): @@ -3517,7 +3531,7 @@ return [1, 2, 3][s:e] a = self.RPythonAnnotator() - py.test.raises(annmodel.AnnotatorError, "a.build_types(f, [int, int])") + py.test.raises(AnnotatorError, "a.build_types(f, [int, int])") a.build_types(f, [annmodel.SomeInteger(nonneg=True), annmodel.SomeInteger(nonneg=True)]) def f(x): @@ -3530,20 +3544,20 @@ return "xyz".find("x", s, e) a = self.RPythonAnnotator() - py.test.raises(annmodel.AnnotatorError, "a.build_types(f, [int, int])") + py.test.raises(AnnotatorError, "a.build_types(f, [int, int])") a.build_types(f, [annmodel.SomeInteger(nonneg=True), annmodel.SomeInteger(nonneg=True)]) def f(s, e): return "xyz".rfind("x", s, e) - py.test.raises(annmodel.AnnotatorError, "a.build_types(f, [int, int])") + py.test.raises(AnnotatorError, "a.build_types(f, [int, int])") a.build_types(f, [annmodel.SomeInteger(nonneg=True), annmodel.SomeInteger(nonneg=True)]) def f(s, e): return "xyz".count("x", s, e) - py.test.raises(annmodel.AnnotatorError, "a.build_types(f, [int, int])") + py.test.raises(AnnotatorError, "a.build_types(f, [int, int])") a.build_types(f, [annmodel.SomeInteger(nonneg=True), annmodel.SomeInteger(nonneg=True)]) @@ -3717,7 +3731,8 @@ raise Exception(lle) # ^^^ instead, must cast back from a base ptr to an instance a = self.RPythonAnnotator() - py.test.raises(AssertionError, a.build_types, f, []) + with py.test.raises(AssertionError): + a.build_types(f, []) def test_enumerate(self): def f(): @@ -4102,7 +4117,8 @@ e = cls() e.foo = "bar" a = self.RPythonAnnotator() - py.test.raises(Exception, a.build_types, fn, []) + with py.test.raises(NoSuchAttrError): + a.build_types(fn, []) def test_lower_char(self): def fn(c): @@ -4214,7 +4230,7 @@ return "bbb" a = self.RPythonAnnotator() - with py.test.raises(annmodel.UnionError) as exc: + with py.test.raises(UnionError) as exc: a.build_types(f, [int]) the_exc = exc.value @@ -4230,7 +4246,7 @@ return (1, 2) a = self.RPythonAnnotator() - with py.test.raises(annmodel.UnionError) as exc: + with py.test.raises(UnionError) as exc: a.build_types(f, [int]) assert "RPython cannot unify tuples of different length: 2 versus 1" in exc.value.msg @@ -4243,7 +4259,7 @@ return -1 a = self.RPythonAnnotator() - with py.test.raises(annmodel.UnionError) as exc: + with py.test.raises(UnionError) as exc: a.build_types(f, [int]) assert ("RPython cannot prove that these integers are of the " @@ -4260,7 +4276,7 @@ return B() a = self.RPythonAnnotator() - with py.test.raises(annmodel.UnionError) as exc: + with py.test.raises(UnionError) as exc: a.build_types(f, [int]) assert ("RPython cannot unify instances with no common base class" @@ -4276,7 +4292,7 @@ return d.itervalues() a = self.RPythonAnnotator() - with py.test.raises(annmodel.UnionError) as exc: + with py.test.raises(UnionError) as exc: a.build_types(f, [int]) assert ("RPython cannot unify incompatible iterator variants" in @@ -4288,7 +4304,7 @@ a = A() return getattr(a, y) a = self.RPythonAnnotator() - with py.test.raises(annmodel.AnnotatorError) as exc: + with py.test.raises(AnnotatorError) as exc: a.build_types(f, [str]) assert ("variable argument to getattr" in exc.value.msg) @@ -4296,7 +4312,7 @@ def f(x): return x() a = self.RPythonAnnotator() - with py.test.raises(annmodel.AnnotatorError) as exc: + with py.test.raises(AnnotatorError) as exc: a.build_types(f, [str]) assert ("Cannot prove that the object is callable" in exc.value.msg) @@ -4305,7 +4321,7 @@ def f(x): l.append(x) a = self.RPythonAnnotator() - with py.test.raises(annmodel.UnionError) as excinfo: + with py.test.raises(UnionError) as excinfo: a.build_types(f, [int]) assert 'Happened at file' in excinfo.value.source assert 'Known variable annotations:' in excinfo.value.source @@ -4314,7 +4330,7 @@ def f(s, x): return s.format(x) a = self.RPythonAnnotator() - with py.test.raises(annmodel.AnnotatorError) as exc: + with py.test.raises(AnnotatorError) as exc: a.build_types(f, [str, str]) assert ("format() is not RPython" in exc.value.msg) @@ -4350,7 +4366,7 @@ def f(x): a, b = x a = self.RPythonAnnotator() - py.test.raises(annmodel.AnnotatorError, + py.test.raises(AnnotatorError, a.build_types, f, [annmodel.s_None]) def test_class___name__(self): @@ -4464,10 +4480,10 @@ o = O2(n) o.x = 20 a = self.RPythonAnnotator() - with py.test.raises(annmodel.UnionError) as exc: + with py.test.raises(UnionError) as exc: a.build_types(f1, [int]) a = self.RPythonAnnotator() - with py.test.raises(annmodel.UnionError) as exc: + with py.test.raises(UnionError) as exc: a.build_types(f2, [int]) def test_property_union_2(self): @@ -4496,7 +4512,7 @@ a = self.RPythonAnnotator() # Ideally, this should translate to something sensible, # but for now, AnnotatorError is better than silently mistranslating. - with py.test.raises(annmodel.AnnotatorError): + with py.test.raises(AnnotatorError): a.build_types(f, [int]) def test_property_union_3(self): @@ -4516,7 +4532,7 @@ obj = B() return obj.x a = self.RPythonAnnotator() - with py.test.raises(annmodel.AnnotatorError): + with py.test.raises(AnnotatorError): a.build_types(f, [int]) def test_dict_can_be_none_ordering_issue(self): diff --git a/rpython/annotator/test/test_annsimplifyrpython.py b/rpython/annotator/test/test_annsimplifyrpython.py --- a/rpython/annotator/test/test_annsimplifyrpython.py +++ b/rpython/annotator/test/test_annsimplifyrpython.py @@ -3,6 +3,7 @@ from rpython.annotator.test.test_annrpython import graphof from rpython.annotator.test.test_annrpython import TestAnnotateTestCase as parent +from rpython.annotator.model import AnnotatorError class TestAnnotateAndSimplifyTestCase(parent): @@ -132,5 +133,5 @@ cls = C return cls().foo a = self.RPythonAnnotator() - with py.test.raises(Exception): + with py.test.raises(AnnotatorError): a.build_types(f, [int]) diff --git a/rpython/rlib/rdynload.py b/rpython/rlib/rdynload.py --- a/rpython/rlib/rdynload.py +++ b/rpython/rlib/rdynload.py @@ -2,13 +2,13 @@ """ from rpython.rtyper.tool import rffi_platform -from rpython.rtyper.lltypesystem import rffi +from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rlib.objectmodel import we_are_translated from rpython.rlib.rarithmetic import r_uint from rpython.translator.tool.cbuild import ExternalCompilationInfo from rpython.translator.platform import platform -import sys +import sys, os, string # maaaybe isinstance here would be better. Think _MSVC = platform.name == "msvc" @@ -103,6 +103,48 @@ return ("opening %r with ctypes.CDLL() works, " "but not with c_dlopen()??" % (name,)) + def _retry_as_ldscript(err, mode): + """ ld scripts are fairly straightforward to parse (the library we want + is in a form like 'GROUP ( <actual-filepath.so>'. A simple state machine + can parse that out (avoids regexes).""" + + parts = err.split(":") + if len(parts) != 2: + return lltype.nullptr(rffi.VOIDP.TO) + fullpath = parts[0] + actual = "" + last_five = " " + state = 0 + ldscript = os.open(fullpath, os.O_RDONLY, 0777) + c = os.read(ldscript, 1) + while c != "": + if state == 0: + last_five += c + last_five = last_five[1:6] + if last_five == "GROUP": + state = 1 + elif state == 1: + if c == "(": + state = 2 + elif state == 2: + if c not in string.whitespace: + actual += c + state = 3 + elif state == 3: + if c in string.whitespace or c == ")": + break + else: + actual += c + c = os.read(ldscript, 1) + os.close(ldscript) + if actual != "": + a = rffi.str2charp(actual) + lib = c_dlopen(a, rffi.cast(rffi.INT, mode)) + rffi.free_charp(a) + return lib + else: + return lltype.nullptr(rffi.VOIDP.TO) + def dlopen(name, mode=-1): """ Wrapper around C-level dlopen """ @@ -119,7 +161,17 @@ err = _dlerror_on_dlopen_untranslated(name) else: err = dlerror() - raise DLOpenError(err) + if platform.name == "linux" and 'invalid ELF header' in err: + # some linux distros put ld linker scripts in .so files + # to load libraries more dynamically. The error contains the + # full path to something that is probably a script to load + # the library we want. + res = _retry_as_ldscript(err, mode) + if not res: + raise DLOpenError(err) + return res + else: + raise DLOpenError(err) return res dlclose = c_dlclose diff --git a/rpython/rlib/rstrategies/rstrategies.py b/rpython/rlib/rstrategies/rstrategies.py --- a/rpython/rlib/rstrategies/rstrategies.py +++ b/rpython/rlib/rstrategies/rstrategies.py @@ -41,7 +41,7 @@ attrs['get_storage'] = get_storage attrs['set_storage'] = set_storage return type.__new__(self, name, bases, attrs) - + def strategy(generalize=None, singleton=True): """ Strategy classes must be decorated with this. @@ -71,19 +71,19 @@ class StrategyFactory(object): _immutable_fields_ = ["strategies[*]", "logger", "strategy_singleton_field"] factory_instance_counter = 0 - + def __init__(self, root_class, all_strategy_classes=None): if all_strategy_classes is None: all_strategy_classes = self._collect_subclasses(root_class) self.strategies = [] self.logger = logger.Logger() - + # This is to avoid confusion between multiple factories existing simultaneously (e.g. in tests) self.strategy_singleton_field = "__singleton_%i" % StrategyFactory.factory_instance_counter StrategyFactory.factory_instance_counter += 1 - + self._create_strategy_instances(root_class, all_strategy_classes) - + def _create_strategy_instances(self, root_class, all_strategy_classes): for strategy_class in all_strategy_classes: if strategy_class._is_strategy: @@ -91,11 +91,11 @@ self.strategies.append(strategy_class) self._patch_strategy_class(strategy_class, root_class) self._order_strategies() - + # ============================= # API methods # ============================= - + def switch_strategy(self, w_self, new_strategy_type, new_element=None): """ Switch the strategy of w_self to the new type. @@ -113,7 +113,7 @@ new_strategy.strategy_switched(w_self) self.log(w_self, new_strategy, old_strategy, new_element) return new_strategy - + def set_initial_strategy(self, w_self, strategy_type, size, elements=None): """ Initialize the strategy and storage fields of w_self. @@ -135,7 +135,7 @@ strategy.strategy_switched(w_self) self.log(w_self, strategy, None, element) return strategy - + @jit.unroll_safe def strategy_type_for(self, objects): """ @@ -153,8 +153,8 @@ for i, strategy_type in enumerate(self.strategies): if can_handle[i]: return strategy_type - raise Exception("Could not find strategy to handle: %s" % objects) - + raise ValueError("Could not find strategy to handle: %s" % objects) + def decorate_strategies(self, transitions): """ As an alternative to decorating all strategies with @strategy, @@ -165,11 +165,11 @@ "NOT_RPYTHON" for strategy_class, generalized in transitions.items(): strategy(generalized)(strategy_class) - + # ============================= # The following methods can be overwritten to customize certain aspects of the factory. # ============================= - + def instantiate_strategy(self, strategy_type, w_self=None, initial_size=0): """ Return a functional instance of strategy_type. @@ -177,7 +177,7 @@ The two additional parameters should be ignored for singleton-strategies. """ return strategy_type() - + def log(self, w_self, new_strategy, old_strategy=None, new_element=None): """ This can be overwritten into a more appropriate call to self.logger.log @@ -190,7 +190,7 @@ typename = "" cause = "Switched" if old_strategy else "Created" self.logger.log(new_strategy_str, size, cause, old_strategy_str, typename, element_typename) - + @specialize.call_location() def log_string_for_object(self, obj): """ @@ -198,8 +198,8 @@ Keep the specialize-annotation in order to handle different kinds of objects here. """ return obj.__class__.__name__ if obj else "" - - # These storage accessors are specialized because the storage field is + + # These storage accessors are specialized because the storage field is # populated by erased-objects which seem to be incompatible sometimes. @specialize.call_location() def get_storage(self, obj): @@ -207,16 +207,16 @@ @specialize.call_location() def set_storage(self, obj, val): return obj._set_storage(val) - + def get_strategy(self, obj): return obj._get_strategy() def set_strategy(self, obj, val): return obj._set_strategy(val) - + # ============================= # Internal methods # ============================= - + def _patch_strategy_class(self, strategy_class, root_class): "NOT_RPYTHON" # Patch root class: Add default handler for visitor @@ -225,12 +225,12 @@ funcname = "_convert_storage_from_" + strategy_class.__name__ _convert_storage_from_OTHER.func_name = funcname setattr(root_class, funcname, _convert_storage_from_OTHER) - + # Patch strategy class: Add polymorphic visitor function def _convert_storage_to(self, w_self, new_strategy): getattr(new_strategy, funcname)(w_self, self) strategy_class._convert_storage_to = _convert_storage_to - + def _collect_subclasses(self, cls): "NOT_RPYTHON" subclasses = [] @@ -238,7 +238,7 @@ subclasses.append(subcls) subclasses.extend(self._collect_subclasses(subcls)) return subclasses - + def _order_strategies(self): "NOT_RPYTHON" def get_generalization_depth(strategy, visited=None): @@ -256,11 +256,11 @@ else: return 0 self.strategies.sort(key=get_generalization_depth, reverse=True) - + @jit.elidable def strategy_singleton_instance(self, strategy_class): return getattr(strategy_class, self.strategy_singleton_field) - + def _freeze_(self): # Instance will be frozen at compile time, making accesses constant. # The constructor does meta stuff which is not possible after translation. @@ -271,65 +271,65 @@ == Required: strategy_factory(self) - Access to StorageFactory """ - + def strategy_switched(self, w_self): # Overwrite this method for a hook whenever the strategy # of w_self was switched to self. pass - + # Main Fixedsize API - + def store(self, w_self, index0, value): raise NotImplementedError("Abstract method") - + def fetch(self, w_self, index0): raise NotImplementedError("Abstract method") - + def size(self, w_self): raise NotImplementedError("Abstract method") - + # Fixedsize utility methods - + def slice(self, w_self, start, end): return [ self.fetch(w_self, i) for i in range(start, end)] - + def fetch_all(self, w_self): return self.slice(w_self, 0, self.size(w_self)) - + def store_all(self, w_self, elements): for i, e in enumerate(elements): self.store(w_self, i, e) - + # Main Varsize API - + def insert(self, w_self, index0, list_w): raise NotImplementedError("Abstract method") - + def delete(self, w_self, start, end): raise NotImplementedError("Abstract method") - + # Varsize utility methods - + def append(self, w_self, list_w): - self.insert(w_self, self.size(w_self), list_w) - + self.insert(w_self, self.size(w_self), list_w) + def pop(self, w_self, index0): e = self.fetch(w_self, index0) self.delete(w_self, index0, index0+1) return e # Internal methods - + def _initialize_storage(self, w_self, initial_size): raise NotImplementedError("Abstract method") - + def _check_can_handle(self, value): raise NotImplementedError("Abstract method") - + def _convert_storage_to(self, w_self, new_strategy): # This will be overwritten in _patch_strategy_class new_strategy._convert_storage_from(w_self, self) - + @jit.unroll_safe def _convert_storage_from(self, w_self, previous_strategy): # This is a very unefficient (but most generic) way to do this. @@ -338,16 +338,16 @@ self._initialize_storage(w_self, previous_strategy.size(w_self)) for i, field in enumerate(storage): self.store(w_self, i, field) - + def _generalize_for_value(self, w_self, value): strategy_type = self.generalized_strategy_for(value) new_instance = self.strategy_factory().switch_strategy(w_self, strategy_type, new_element=value) return new_instance - + def _cannot_handle_store(self, w_self, index0, value): new_instance = self._generalize_for_value(w_self, value) new_instance.store(w_self, index0, value) - + def _cannot_handle_insert(self, w_self, index0, list_w): # TODO - optimize. Prevent multiple generalizations and slicing done by callers. new_strategy = self._generalize_for_value(w_self, list_w[0]) @@ -358,7 +358,7 @@ class EmptyStrategy(AbstractStrategy): # == Required: # See AbstractStrategy - + def _initialize_storage(self, w_self, initial_size): assert initial_size == 0 self.set_storage(w_self, None) @@ -366,7 +366,7 @@ self.set_storage(w_self, None) def _check_can_handle(self, value): return False - + def fetch(self, w_self, index0): raise IndexError def store(self, w_self, index0, value): @@ -389,7 +389,7 @@ # See AbstractStrategy # check_index_*(...) - use mixin SafeIndexingMixin or UnsafeIndexingMixin # value(self) - the single value contained in this strategy. Should be constant. - + def _initialize_storage(self, w_self, initial_size): storage_obj = SingleValueStrategyStorage(initial_size) self.set_storage(w_self, storage_obj) @@ -397,7 +397,7 @@ self._initialize_storage(w_self, previous_strategy.size(w_self)) def _check_can_handle(self, value): return value is self.value() - + def fetch(self, w_self, index0): self.check_index_fetch(w_self, index0) return self.value() @@ -411,7 +411,7 @@ self.get_storage(w_self).size -= (end - start) def size(self, w_self): return self.get_storage(w_self).size - + @jit.unroll_safe def insert(self, w_self, index0, list_w): storage_obj = self.get_storage(w_self) @@ -429,18 +429,18 @@ # See AbstractStrategy # check_index_*(...) - use mixin SafeIndexingMixin or UnsafeIndexingMixin # default_value(self) - The value to be initially contained in this strategy - + def _initialize_storage(self, w_self, initial_size): default = self._unwrap(self.default_value()) self.set_storage(w_self, [default] * initial_size) - + @jit.unroll_safe def _convert_storage_from(self, w_self, previous_strategy): size = previous_strategy.size(w_self) new_storage = [ self._unwrap(previous_strategy.fetch(w_self, i)) for i in range(size) ] self.set_storage(w_self, new_storage) - + def store(self, w_self, index0, wrapped_value): self.check_index_store(w_self, index0) if self._check_can_handle(wrapped_value): @@ -448,21 +448,21 @@ self.get_storage(w_self)[index0] = unwrapped else: self._cannot_handle_store(w_self, index0, wrapped_value) - + def fetch(self, w_self, index0): self.check_index_fetch(w_self, index0) unwrapped = self.get_storage(w_self)[index0] return self._wrap(unwrapped) - + def _wrap(self, value): raise NotImplementedError("Abstract method") - + def _unwrap(self, value): raise NotImplementedError("Abstract method") - + def size(self, w_self): return len(self.get_storage(w_self)) - + @jit.unroll_safe def insert(self, w_self, start, list_w): # This is following Python's behaviour - insert automatically @@ -475,27 +475,27 @@ else: self._cannot_handle_insert(w_self, start + i, list_w[i:]) return - + def delete(self, w_self, start, end): self.check_index_range(w_self, start, end) assert start >= 0 and end >= 0 del self.get_storage(w_self)[start : end] - + class GenericStrategy(StrategyWithStorage): # == Required: # See StrategyWithStorage - + def _wrap(self, value): return value def _unwrap(self, value): return value def _check_can_handle(self, wrapped_value): return True - + class WeakGenericStrategy(StrategyWithStorage): # == Required: # See StrategyWithStorage - + def _wrap(self, value): return value() or self.default_value() def _unwrap(self, value): @@ -503,7 +503,7 @@ return weakref.ref(value) def _check_can_handle(self, wrapped_value): return True - + # ============== Mixins for index checking operations ============== class SafeIndexingMixin(object): @@ -535,37 +535,37 @@ # See StrategyWithStorage # wrap(self, value) - Return a boxed object for the primitive value # unwrap(self, value) - Return the unboxed primitive value of value - + def _unwrap(self, value): return self.unwrap(value) def _wrap(self, value): return self.wrap(value) - + class SingleTypeStrategy(SpecializedStrategy): # == Required Functions: # See SpecializedStrategy # contained_type - The wrapped type that can be stored in this strategy - + def _check_can_handle(self, value): return isinstance(value, self.contained_type) - + class TaggingStrategy(SingleTypeStrategy): """This strategy uses a special tag value to represent a single additional object.""" # == Required: # See SingleTypeStrategy # wrapped_tagged_value(self) - The tagged object # unwrapped_tagged_value(self) - The unwrapped tag value representing the tagged object - + def _check_can_handle(self, value): return value is self.wrapped_tagged_value() or \ (isinstance(value, self.contained_type) and \ self.unwrap(value) != self.unwrapped_tagged_value()) - + def _unwrap(self, value): if value is self.wrapped_tagged_value(): return self.unwrapped_tagged_value() return self.unwrap(value) - + def _wrap(self, value): if value == self.unwrapped_tagged_value(): return self.wrapped_tagged_value() diff --git a/rpython/rlib/rstrategies/test/test_rstrategies.py b/rpython/rlib/rstrategies/test/test_rstrategies.py --- a/rpython/rlib/rstrategies/test/test_rstrategies.py +++ b/rpython/rlib/rstrategies/test/test_rstrategies.py @@ -69,7 +69,7 @@ class Factory(rs.StrategyFactory): switching_log = [] - + def __init__(self, root_class): self.decorate_strategies({ EmptyStrategy: [NilStrategy, IntegerStrategy, IntegerOrNilStrategy, GenericStrategy], @@ -79,15 +79,15 @@ IntegerOrNilStrategy: [GenericStrategy], }) rs.StrategyFactory.__init__(self, root_class) - + def instantiate_strategy(self, strategy_type, w_self=None, size=0): return strategy_type(self, w_self, size) - - def set_strategy(self, w_list, strategy): + + def set_strategy(self, w_list, strategy): old_strategy = self.get_strategy(w_list) self.switching_log.append((old_strategy, strategy)) super(Factory, self).set_strategy(w_list, strategy) - + def clear_log(self): del self.switching_log[:] @@ -107,7 +107,7 @@ class WeakGenericStrategy(AbstractStrategy): import_from_mixin(rs.WeakGenericStrategy) def default_value(self): return w_nil - + class IntegerStrategy(AbstractStrategy): import_from_mixin(rs.SingleTypeStrategy) contained_type = W_Integer @@ -123,7 +123,7 @@ def default_value(self): return w_nil def wrapped_tagged_value(self): return w_nil def unwrapped_tagged_value(self): import sys; return sys.maxint - + @rs.strategy(generalize=[], singleton=False) class NonSingletonStrategy(GenericStrategy): def __init__(self, factory, w_list=None, size=0): @@ -214,22 +214,22 @@ py.test.raises(IndexError, s.fetch, l, 10) py.test.raises(IndexError, s.delete, l, 0, 1) py.test.raises(AssertionError, W_List, EmptyStrategy, 2) # Only size 0 possible. - + def test_init_Nil(): do_test_initialization(NilStrategy) def test_init_Generic(): do_test_initialization(GenericStrategy, is_safe=False) - + def test_init_WeakGeneric(): do_test_initialization(WeakGenericStrategy) - + def test_init_Integer(): do_test_initialization(IntegerStrategy, default_value=W_Integer(0)) - + def test_init_IntegerOrNil(): do_test_initialization(IntegerOrNilStrategy) - + # === Test Simple store def do_test_store(cls, stored_value=W_Object(), is_safe=True, is_varsize=False): @@ -256,13 +256,13 @@ def test_store_Generic(): do_test_store(GenericStrategy, is_safe=False) - + def test_store_WeakGeneric(): do_test_store(WeakGenericStrategy, stored_value=w_nil) - + def test_store_Integer(): do_test_store(IntegerStrategy, stored_value=W_Integer(100)) - + def test_store_IntegerOrNil(): do_test_store(IntegerOrNilStrategy, stored_value=W_Integer(100)) do_test_store(IntegerOrNilStrategy, stored_value=w_nil) @@ -289,17 +289,17 @@ def test_insert_Generic(): do_test_insert(GenericStrategy, [W_Object() for _ in range(6)]) - + def test_insert_WeakGeneric(): do_test_insert(WeakGenericStrategy, [W_Object() for _ in range(6)]) - + def test_insert_Integer(): do_test_insert(IntegerStrategy, [W_Integer(x) for x in range(6)]) - + def test_insert_IntegerOrNil(): do_test_insert(IntegerOrNilStrategy, [w_nil]+[W_Integer(x) for x in range(4)]+[w_nil]) do_test_insert(IntegerOrNilStrategy, [w_nil]*6) - + # === Test Delete def do_test_delete(cls, values, indexing_unsafe=False): @@ -319,13 +319,13 @@ def test_delete_Generic(): do_test_delete(GenericStrategy, [W_Object() for _ in range(6)], indexing_unsafe=True) - + def test_delete_WeakGeneric(): do_test_delete(WeakGenericStrategy, [W_Object() for _ in range(6)]) - + def test_delete_Integer(): do_test_delete(IntegerStrategy, [W_Integer(x) for x in range(6)]) - + def test_delete_IntegerOrNil(): do_test_delete(IntegerOrNilStrategy, [w_nil]+[W_Integer(x) for x in range(4)]+[w_nil]) do_test_delete(IntegerOrNilStrategy, [w_nil]*6) @@ -342,7 +342,7 @@ obj = W_Object() i = W_Integer(0) nil = w_nil - + assert_handles(EmptyStrategy, [], [nil, obj, i]) assert_handles(NilStrategy, [nil], [obj, i]) assert_handles(GenericStrategy, [nil, obj, i], []) @@ -392,7 +392,7 @@ o = W_Object() l = do_test_insert(NilStrategy, [w_nil, w_nil, o, o, w_nil, w_nil]) assert isinstance(l.strategy, GenericStrategy) - + def test_transition_to_nonSingleton(): l = W_List(NilStrategy, 5) factory.switch_strategy(l, NonSingletonStrategy) @@ -467,12 +467,12 @@ v3 = [W_Object() for _ in range(l.size()) ] assert v2 != v assert v3 != v - + l.store_all(v2) assert l.fetch_all() == v2+v[4:] l.store_all(v3) assert l.fetch_all() == v3 - + py.test.raises(IndexError, l.store_all, [W_Object() for _ in range(8) ]) # === Test Weak Strategy @@ -488,7 +488,7 @@ assert False, "The default convert_storage_from() should not be called!" def convert_storage_from_special(self, w_self, other): s.copied += 1 - + monkeypatch.setattr(AbstractStrategy, "_convert_storage_from_NilStrategy", convert_storage_from_special) monkeypatch.setattr(AbstractStrategy, "_convert_storage_from", convert_storage_from_default) try: @@ -507,7 +507,8 @@ assert factory.strategy_type_for([]) == EmptyStrategy monkeypatch.setattr(GenericStrategy, '_check_can_handle', lambda self, o: False) try: - py.test.raises(Exception, factory.strategy_type_for, [W_Object(), W_Object()]) + with py.test.raises(ValueError): + factory.strategy_type_for([W_Object(), W_Object()]) finally: monkeypatch.undo() @@ -549,4 +550,3 @@ 'Created (EmptyStrategy) size 0 objects 1', 'Created (IntegerStrategy) size 3 objects 1', 'Switched (IntegerStrategy -> IntegerOrNilStrategy) size 3 objects 1 elements: W_Object'] - \ No newline at end of file diff --git a/rpython/rlib/rweakref.py b/rpython/rlib/rweakref.py --- a/rpython/rlib/rweakref.py +++ b/rpython/rlib/rweakref.py @@ -2,10 +2,10 @@ Weakref support in RPython. Basic regular weakrefs without callbacks are supported. This file contains the following additions: a form of WeakKeyDictionary, and a limited version of WeakValueDictionary. -LLType only for now! """ import weakref +from rpython.annotator.model import UnionError ref = weakref.ref # basic regular weakrefs are supported in RPython @@ -191,9 +191,9 @@ class __extend__(pairtype(SomeWeakKeyDict, SomeWeakKeyDict)): def union((s_wkd1, s_wkd2)): if s_wkd1.keyclassdef is not s_wkd2.keyclassdef: - raise UnionError(w_wkd1, s_wkd2, "not the same key class!") + raise UnionError(s_wkd1, s_wkd2, "not the same key class!") if s_wkd1.valueclassdef is not s_wkd2.valueclassdef: - raise UnionError(w_wkd1, s_wkd2, "not the same value class!") + raise UnionError(s_wkd1, s_wkd2, "not the same value class!") return SomeWeakKeyDict(s_wkd1.keyclassdef, s_wkd1.valueclassdef) class Entry(extregistry.ExtRegistryEntry): diff --git a/rpython/rlib/test/ldscript_broken1.so b/rpython/rlib/test/ldscript_broken1.so new file mode 100644 --- /dev/null +++ b/rpython/rlib/test/ldscript_broken1.so @@ -0,0 +1,4 @@ +/* GNU ld script +*/ +OUTPUT_FORMAT(elf64-x86-64) +GROUP libc.so.6 ) diff --git a/rpython/rlib/test/ldscript_broken2.so b/rpython/rlib/test/ldscript_broken2.so new file mode 100644 --- /dev/null +++ b/rpython/rlib/test/ldscript_broken2.so @@ -0,0 +1,4 @@ +/* GNU ld script +*/ +OUTPUT_FORMAT(elf64-x86-64) +libc.so.6 diff --git a/rpython/rlib/test/ldscript_working1.so b/rpython/rlib/test/ldscript_working1.so new file mode 100644 --- /dev/null +++ b/rpython/rlib/test/ldscript_working1.so @@ -0,0 +1,4 @@ +/* GNU ld script +*/ +OUTPUT_FORMAT(elf64-x86-64) +GROUP ( libc.so.6 ) diff --git a/rpython/rlib/test/ldscript_working2.so b/rpython/rlib/test/ldscript_working2.so new file mode 100644 --- /dev/null +++ b/rpython/rlib/test/ldscript_working2.so @@ -0,0 +1,4 @@ +/* GNU ld script +*/ +OUTPUT_FORMAT(elf64-x86-64) +GROUP(libc.so.6) diff --git a/rpython/rlib/test/test_rdynload.py b/rpython/rlib/test/test_rdynload.py --- a/rpython/rlib/test/test_rdynload.py +++ b/rpython/rlib/test/test_rdynload.py @@ -1,6 +1,7 @@ from rpython.rlib.rdynload import * from rpython.rlib.clibffi import get_libc_name from rpython.rtyper.lltypesystem import rffi, lltype +from rpython.translator.platform import platform import py class TestDLOperations: @@ -21,3 +22,28 @@ lltype.Signed)), dlsym(lib, 'abs')) assert 1 == handle(1) assert 1 == handle(-1) + + def test_ldscripts(self): + # this test only makes sense on linux + if platform.name != "linux": + return + + fname = os.path.join(os.path.dirname(__file__), "ldscript_working1.so") + s = rffi.str2charp(fname) + assert "C object" in str(dlopen(s)) + rffi.free_charp(s) + + fname = os.path.join(os.path.dirname(__file__), "ldscript_working2.so") + s = rffi.str2charp(fname) + assert "C object" in str(dlopen(s)) + rffi.free_charp(s) + + fname = os.path.join(os.path.dirname(__file__), "ldscript_broken1.so") + s = rffi.str2charp(fname) + py.test.raises(DLOpenError, 'dlopen(s)') + rffi.free_charp(s) + + fname = os.path.join(os.path.dirname(__file__), "ldscript_broken2.so") _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit