Author: Alex Gaynor <alex.gay...@gmail.com> Branch: Changeset: r46928:9751168d555a Date: 2011-08-30 15:19 -0400 http://bitbucket.org/pypy/pypy/changeset/9751168d555a/
Log: Merged numpy-ufunc-object. This makes ufuncs into objects at app- level, and gives them a reduce method, plus several attributes. array's sum and prod methods now use the ufuncs. diff --git a/pypy/module/micronumpy/__init__.py b/pypy/module/micronumpy/__init__.py --- a/pypy/module/micronumpy/__init__.py +++ b/pypy/module/micronumpy/__init__.py @@ -1,42 +1,45 @@ - from pypy.interpreter.mixedmodule import MixedModule + class Module(MixedModule): - applevel_name = 'numpy' interpleveldefs = { 'array': 'interp_numarray.SingleDimArray', 'dtype': 'interp_dtype.W_Dtype', + 'ufunc': 'interp_ufuncs.W_Ufunc', 'zeros': 'interp_numarray.zeros', 'empty': 'interp_numarray.zeros', 'ones': 'interp_numarray.ones', 'fromstring': 'interp_support.fromstring', + } - # ufuncs - 'abs': 'interp_ufuncs.absolute', - 'absolute': 'interp_ufuncs.absolute', - 'add': 'interp_ufuncs.add', - 'copysign': 'interp_ufuncs.copysign', - 'divide': 'interp_ufuncs.divide', - 'exp': 'interp_ufuncs.exp', - 'fabs': 'interp_ufuncs.fabs', - 'floor': 'interp_ufuncs.floor', - 'maximum': 'interp_ufuncs.maximum', - 'minimum': 'interp_ufuncs.minimum', - 'multiply': 'interp_ufuncs.multiply', - 'negative': 'interp_ufuncs.negative', - 'reciprocal': 'interp_ufuncs.reciprocal', - 'sign': 'interp_ufuncs.sign', - 'subtract': 'interp_ufuncs.subtract', - 'sin': 'interp_ufuncs.sin', - 'cos': 'interp_ufuncs.cos', - 'tan': 'interp_ufuncs.tan', - 'arcsin': 'interp_ufuncs.arcsin', - 'arccos': 'interp_ufuncs.arccos', - 'arctan': 'interp_ufuncs.arctan', - } + # ufuncs + for exposed, impl in [ + ("abs", "absolute"), + ("absolute", "absolute"), + ("add", "add"), + ("arccos", "arccos"), + ("arcsin", "arcsin"), + ("arctan", "arctan"), + ("copysign", "copysign"), + ("cos", "cos"), + ("divide", "divide"), + ("exp", "exp"), + ("fabs", "fabs"), + ("floor", "floor"), + ("maximum", "maximum"), + ("minimum", "minimum"), + ("multiply", "multiply"), + ("negative", "negative"), + ("reciprocal", "reciprocal"), + ("sign", "sign"), + ("sin", "sin"), + ("subtract", "subtract"), + ("tan", "tan"), + ]: + interpleveldefs[exposed] = "interp_ufuncs.get(space).%s" % impl appleveldefs = { 'average': 'app_numpy.average', 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 @@ -20,6 +20,7 @@ class FakeSpace(object): w_ValueError = None + w_TypeError = None def __init__(self): """NOT_RPYTHON""" diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py --- a/pypy/module/micronumpy/interp_numarray.py +++ b/pypy/module/micronumpy/interp_numarray.py @@ -53,67 +53,50 @@ i += 1 return arr - def _unaryop_impl(w_ufunc): + def _unaryop_impl(ufunc_name): def impl(self, space): - return w_ufunc(space, self) - return func_with_new_name(impl, "unaryop_%s_impl" % w_ufunc.__name__) + return getattr(interp_ufuncs.get(space), ufunc_name).call(space, [self]) + return func_with_new_name(impl, "unaryop_%s_impl" % ufunc_name) - descr_pos = _unaryop_impl(interp_ufuncs.positive) - descr_neg = _unaryop_impl(interp_ufuncs.negative) - descr_abs = _unaryop_impl(interp_ufuncs.absolute) + descr_pos = _unaryop_impl("positive") + descr_neg = _unaryop_impl("negative") + descr_abs = _unaryop_impl("absolute") - def _binop_impl(w_ufunc): + def _binop_impl(ufunc_name): def impl(self, space, w_other): - return w_ufunc(space, self, w_other) - return func_with_new_name(impl, "binop_%s_impl" % w_ufunc.__name__) + return getattr(interp_ufuncs.get(space), ufunc_name).call(space, [self, w_other]) + return func_with_new_name(impl, "binop_%s_impl" % ufunc_name) - descr_add = _binop_impl(interp_ufuncs.add) - descr_sub = _binop_impl(interp_ufuncs.subtract) - descr_mul = _binop_impl(interp_ufuncs.multiply) - descr_div = _binop_impl(interp_ufuncs.divide) - descr_pow = _binop_impl(interp_ufuncs.power) - descr_mod = _binop_impl(interp_ufuncs.mod) + descr_add = _binop_impl("add") + descr_sub = _binop_impl("subtract") + descr_mul = _binop_impl("multiply") + descr_div = _binop_impl("divide") + descr_pow = _binop_impl("power") + descr_mod = _binop_impl("mod") - def _binop_right_impl(w_ufunc): + def _binop_right_impl(ufunc_name): def impl(self, space, w_other): w_other = scalar_w(space, interp_ufuncs.find_dtype_for_scalar(space, w_other, self.find_dtype()), w_other ) - return w_ufunc(space, w_other, self) - return func_with_new_name(impl, "binop_right_%s_impl" % w_ufunc.__name__) + return getattr(interp_ufuncs.get(space), ufunc_name).call(space, [w_other, self]) + return func_with_new_name(impl, "binop_right_%s_impl" % ufunc_name) - descr_radd = _binop_right_impl(interp_ufuncs.add) - descr_rsub = _binop_right_impl(interp_ufuncs.subtract) - descr_rmul = _binop_right_impl(interp_ufuncs.multiply) - descr_rdiv = _binop_right_impl(interp_ufuncs.divide) - descr_rpow = _binop_right_impl(interp_ufuncs.power) - descr_rmod = _binop_right_impl(interp_ufuncs.mod) + descr_radd = _binop_right_impl("add") + descr_rsub = _binop_right_impl("subtract") + descr_rmul = _binop_right_impl("multiply") + descr_rdiv = _binop_right_impl("divide") + descr_rpow = _binop_right_impl("power") + descr_rmod = _binop_right_impl("mod") - def _reduce_sum_prod_impl(op_name, init): - reduce_driver = jit.JitDriver(greens=['signature'], - reds = ['i', 'size', 'self', 'result', 'res_dtype']) + def _reduce_ufunc_impl(ufunc_name): + def impl(self, space): + return getattr(interp_ufuncs.get(space), ufunc_name).descr_reduce(space, self) + return func_with_new_name(impl, "reduce_%s_impl" % ufunc_name) - def loop(self, res_dtype, result, size): - i = 0 - while i < size: - reduce_driver.jit_merge_point(signature=self.signature, - self=self, res_dtype=res_dtype, - size=size, i=i, result=result) - result = getattr(res_dtype, op_name)( - result, - self.eval(i).convert_to(res_dtype) - ) - i += 1 - return result - - def impl(self, space): - dtype = interp_ufuncs.find_unaryop_result_dtype( - space, self.find_dtype(), promote_to_largest=True - ) - result = dtype.adapt_val(init) - return loop(self, dtype, result, self.find_size()).wrap(space) - return func_with_new_name(impl, "reduce_%s_impl" % op_name) + descr_sum = _reduce_ufunc_impl("add") + descr_prod = _reduce_ufunc_impl("multiply") def _reduce_max_min_impl(op_name): reduce_driver = jit.JitDriver(greens=['signature'], @@ -192,8 +175,6 @@ def descr_any(self, space): return space.wrap(self._any()) - descr_sum = _reduce_sum_prod_impl("add", 0) - descr_prod = _reduce_sum_prod_impl("mul", 1) descr_max = _reduce_max_min_impl("max") descr_min = _reduce_max_min_impl("min") descr_argmax = _reduce_argmax_argmin_impl("max") diff --git a/pypy/module/micronumpy/interp_ufuncs.py b/pypy/module/micronumpy/interp_ufuncs.py --- a/pypy/module/micronumpy/interp_ufuncs.py +++ b/pypy/module/micronumpy/interp_ufuncs.py @@ -1,57 +1,152 @@ +from pypy.interpreter.baseobjspace import Wrappable +from pypy.interpreter.error import OperationError +from pypy.interpreter.gateway import interp2app +from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty from pypy.module.micronumpy import interp_dtype, signature +from pypy.rlib import jit from pypy.tool.sourcetools import func_with_new_name -def ufunc(func=None, promote_to_float=False, promote_bools=False): - if func is None: - return lambda func: ufunc(func, promote_to_float, promote_bools) - call_sig = signature.Call1(func) - def impl(space, w_obj): +reduce_driver = jit.JitDriver( + greens = ["signature"], + reds = ["i", "size", "self", "dtype", "value", "obj"] +) + +class W_Ufunc(Wrappable): + _attrs_ = ["name", "promote_to_float", "promote_bools", "identity"] + + def __init__(self, name, promote_to_float, promote_bools, identity): + self.name = name + self.promote_to_float = promote_to_float + self.promote_bools = promote_bools + + self.identity = identity + + def descr_repr(self, space): + return space.wrap("<ufunc '%s'>" % self.name) + + def descr_get_identity(self, space): + if self.identity is None: + return space.w_None + return self.identity.wrap(space) + + def descr_call(self, space, __args__): + try: + args_w = __args__.fixedunpack(self.argcount) + except ValueError, e: + raise OperationError(space.w_TypeError, space.wrap(str(e))) + return self.call(space, args_w) + + def descr_reduce(self, space, w_obj): + from pypy.module.micronumpy.interp_numarray import convert_to_array, Scalar + + if self.argcount != 2: + raise OperationError(space.w_ValueError, space.wrap("reduce only supported for binary functions")) + if self.identity is None: + raise OperationError(space.w_NotImplementedError, space.wrap("%s is missing its identity value" % self.name)) + + assert isinstance(self, W_Ufunc2) + obj = convert_to_array(space, w_obj) + if isinstance(obj, Scalar): + raise OperationError(space.w_TypeError, space.wrap("cannot reduce on a scalar")) + + size = obj.find_size() + dtype = find_unaryop_result_dtype( + space, obj.find_dtype(), + promote_to_largest=True + ) + value = self.identity.convert_to(dtype) + new_sig = signature.Signature.find_sig([ + self.reduce_signature, obj.signature + ]) + return self.reduce(new_sig, value, obj, dtype, size).wrap(space) + + def reduce(self, signature, value, obj, dtype, size): + i = 0 + while i < size: + reduce_driver.jit_merge_point(signature=signature, self=self, + value=value, obj=obj, i=i, + dtype=dtype, size=size) + value = self.func(dtype, value, obj.eval(i).convert_to(dtype)) + i += 1 + return value + +class W_Ufunc1(W_Ufunc): + argcount = 1 + + def __init__(self, func, name, promote_to_float=False, promote_bools=False, + identity=None): + + W_Ufunc.__init__(self, name, promote_to_float, promote_bools, identity) + self.func = func + self.signature = signature.Call1(func) + + def call(self, space, args_w): from pypy.module.micronumpy.interp_numarray import (Call1, convert_to_array, Scalar) + [w_obj] = args_w w_obj = convert_to_array(space, w_obj) res_dtype = find_unaryop_result_dtype(space, w_obj.find_dtype(), - promote_to_float=promote_to_float, - promote_bools=promote_bools, + promote_to_float=self.promote_to_float, + promote_bools=self.promote_bools, ) if isinstance(w_obj, Scalar): - return func(res_dtype, w_obj.value.convert_to(res_dtype)).wrap(space) + return self.func(res_dtype, w_obj.value.convert_to(res_dtype)).wrap(space) - new_sig = signature.Signature.find_sig([call_sig, w_obj.signature]) + new_sig = signature.Signature.find_sig([self.signature, w_obj.signature]) w_res = Call1(new_sig, res_dtype, w_obj) w_obj.add_invalidates(w_res) return w_res - return func_with_new_name(impl, "%s_dispatcher" % func.__name__) -def ufunc2(func=None, promote_to_float=False, promote_bools=False): - if func is None: - return lambda func: ufunc2(func, promote_to_float, promote_bools) - call_sig = signature.Call2(func) - def impl(space, w_lhs, w_rhs): +class W_Ufunc2(W_Ufunc): + argcount = 2 + + def __init__(self, func, name, promote_to_float=False, promote_bools=False, + identity=None): + + W_Ufunc.__init__(self, name, promote_to_float, promote_bools, identity) + self.func = func + self.signature = signature.Call2(func) + self.reduce_signature = signature.BaseSignature() + + def call(self, space, args_w): from pypy.module.micronumpy.interp_numarray import (Call2, convert_to_array, Scalar) + [w_lhs, w_rhs] = args_w w_lhs = convert_to_array(space, w_lhs) w_rhs = convert_to_array(space, w_rhs) res_dtype = find_binop_result_dtype(space, w_lhs.find_dtype(), w_rhs.find_dtype(), - promote_to_float=promote_to_float, - promote_bools=promote_bools, + promote_to_float=self.promote_to_float, + promote_bools=self.promote_bools, ) if isinstance(w_lhs, Scalar) and isinstance(w_rhs, Scalar): - return func(res_dtype, w_lhs.value, w_rhs.value).wrap(space) + return self.func(res_dtype, w_lhs.value, w_rhs.value).wrap(space) new_sig = signature.Signature.find_sig([ - call_sig, w_lhs.signature, w_rhs.signature + self.signature, w_lhs.signature, w_rhs.signature ]) w_res = Call2(new_sig, res_dtype, w_lhs, w_rhs) w_lhs.add_invalidates(w_res) w_rhs.add_invalidates(w_res) return w_res - return func_with_new_name(impl, "%s_dispatcher" % func.__name__) + + +W_Ufunc.typedef = TypeDef("ufunc", + __module__ = "numpy", + + __call__ = interp2app(W_Ufunc.descr_call), + __repr__ = interp2app(W_Ufunc.descr_repr), + + identity = GetSetProperty(W_Ufunc.descr_get_identity), + nin = interp_attrproperty("argcount", cls=W_Ufunc), + + reduce = interp2app(W_Ufunc.descr_reduce), +) def find_binop_result_dtype(space, dt1, dt2, promote_to_float=False, promote_bools=False): @@ -74,7 +169,7 @@ assert False def find_unaryop_result_dtype(space, dt, promote_to_float=False, - promote_to_largest=False, promote_bools=False): + promote_bools=False, promote_to_largest=False): if promote_bools and (dt.kind == interp_dtype.BOOLLTR): return space.fromcache(interp_dtype.W_Int8Dtype) if promote_to_float: @@ -106,53 +201,65 @@ return space.fromcache(interp_dtype.W_Float64Dtype) -def ufunc_dtype_caller(ufunc_name, op_name, argcount, **kwargs): +def ufunc_dtype_caller(ufunc_name, op_name, argcount): if argcount == 1: - @ufunc(**kwargs) def impl(res_dtype, value): return getattr(res_dtype, op_name)(value) elif argcount == 2: - @ufunc2(**kwargs) def impl(res_dtype, lvalue, rvalue): return getattr(res_dtype, op_name)(lvalue, rvalue) return func_with_new_name(impl, ufunc_name) -for ufunc_def in [ - ("add", "add", 2), - ("subtract", "sub", 2), - ("multiply", "mul", 2), - ("divide", "div", 2, {"promote_bools": True}), - ("mod", "mod", 2, {"promote_bools": True}), - ("power", "pow", 2, {"promote_bools": True}), +class UfuncState(object): + def __init__(self, space): + "NOT_RPYTHON" + for ufunc_def in [ + ("add", "add", 2, {"identity": 0}), + ("subtract", "sub", 2), + ("multiply", "mul", 2, {"identity": 1}), + ("divide", "div", 2, {"promote_bools": True}), + ("mod", "mod", 2, {"promote_bools": True}), + ("power", "pow", 2, {"promote_bools": True}), - ("maximum", "max", 2), - ("minimum", "min", 2), + ("maximum", "max", 2), + ("minimum", "min", 2), - ("copysign", "copysign", 2, {"promote_to_float": True}), + ("copysign", "copysign", 2, {"promote_to_float": True}), - ("positive", "pos", 1), - ("negative", "neg", 1), - ("absolute", "abs", 1), - ("sign", "sign", 1, {"promote_bools": True}), - ("reciprocal", "reciprocal", 1), + ("positive", "pos", 1), + ("negative", "neg", 1), + ("absolute", "abs", 1), + ("sign", "sign", 1, {"promote_bools": True}), + ("reciprocal", "reciprocal", 1), - ("fabs", "fabs", 1, {"promote_to_float": True}), - ("floor", "floor", 1, {"promote_to_float": True}), - ("exp", "exp", 1, {"promote_to_float": True}), + ("fabs", "fabs", 1, {"promote_to_float": True}), + ("floor", "floor", 1, {"promote_to_float": True}), + ("exp", "exp", 1, {"promote_to_float": True}), - ("sin", "sin", 1, {"promote_to_float": True}), - ("cos", "cos", 1, {"promote_to_float": True}), - ("tan", "tan", 1, {"promote_to_float": True}), - ("arcsin", "arcsin", 1, {"promote_to_float": True}), - ("arccos", "arccos", 1, {"promote_to_float": True}), - ("arctan", "arctan", 1, {"promote_to_float": True}), -]: - ufunc_name = ufunc_def[0] - op_name = ufunc_def[1] - argcount = ufunc_def[2] - try: - extra_kwargs = ufunc_def[3] - except IndexError: - extra_kwargs = {} + ("sin", "sin", 1, {"promote_to_float": True}), + ("cos", "cos", 1, {"promote_to_float": True}), + ("tan", "tan", 1, {"promote_to_float": True}), + ("arcsin", "arcsin", 1, {"promote_to_float": True}), + ("arccos", "arccos", 1, {"promote_to_float": True}), + ("arctan", "arctan", 1, {"promote_to_float": True}), + ]: + self.add_ufunc(space, *ufunc_def) - globals()[ufunc_name] = ufunc_dtype_caller(ufunc_name, op_name, argcount, **extra_kwargs) + def add_ufunc(self, space, ufunc_name, op_name, argcount, extra_kwargs=None): + if extra_kwargs is None: + extra_kwargs = {} + + identity = extra_kwargs.get("identity") + if identity is not None: + identity = space.fromcache(interp_dtype.W_Int64Dtype).adapt_val(identity) + extra_kwargs["identity"] = identity + + func = ufunc_dtype_caller(ufunc_name, op_name, argcount) + if argcount == 1: + ufunc = W_Ufunc1(func, ufunc_name, **extra_kwargs) + elif argcount == 2: + ufunc = W_Ufunc2(func, ufunc_name, **extra_kwargs) + setattr(self, ufunc_name, ufunc) + +def get(space): + return space.fromcache(UfuncState) \ No newline at end of file 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 @@ -3,6 +3,32 @@ class AppTestUfuncs(BaseNumpyAppTest): + def test_ufunc_instance(self): + from numpy import add, ufunc + + assert isinstance(add, ufunc) + assert repr(add) == "<ufunc 'add'>" + assert repr(ufunc) == "<type 'numpy.ufunc'>" + + def test_ufunc_attrs(self): + from numpy import add, multiply, sin + + assert add.identity == 0 + assert multiply.identity == 1 + assert sin.identity is None + + assert add.nin == 2 + assert multiply.nin == 2 + assert sin.nin == 1 + + def test_wrong_arguments(self): + from numpy import add, sin + + raises(TypeError, add, 1) + raises(TypeError, add, 1, 2, 3) + raises(TypeError, sin, 1, 2) + raises(TypeError, sin) + def test_single_item(self): from numpy import negative, sign, minimum @@ -272,3 +298,13 @@ b = arctan(a) assert math.isnan(b[0]) + def test_reduce_errors(self): + from numpy import sin, add + + raises(ValueError, sin.reduce, [1, 2, 3]) + raises(TypeError, add.reduce, 1) + + def test_reduce(self): + from numpy import add + + assert add.reduce([1, 2, 3]) == 6 \ No newline at end of file diff --git a/pypy/module/micronumpy/test/test_zjit.py b/pypy/module/micronumpy/test/test_zjit.py --- a/pypy/module/micronumpy/test/test_zjit.py +++ b/pypy/module/micronumpy/test/test_zjit.py @@ -19,7 +19,7 @@ def test_add(self): def f(i): ar = SingleDimArray(i, dtype=self.float64_dtype) - v = interp_ufuncs.add(self.space, ar, ar) + v = interp_ufuncs.get(self.space).add.call(self.space, [ar, ar]) return v.get_concrete().eval(3).val result = self.meta_interp(f, [5], listops=True, backendopt=True) @@ -31,9 +31,10 @@ def test_floatadd(self): def f(i): ar = SingleDimArray(i, dtype=self.float64_dtype) - v = interp_ufuncs.add(self.space, - ar, - scalar_w(self.space, self.float64_dtype, self.space.wrap(4.5)) + v = interp_ufuncs.get(self.space).add.call(self.space, [ + ar, + scalar_w(self.space, self.float64_dtype, self.space.wrap(4.5)) + ], ) assert isinstance(v, BaseArray) return v.get_concrete().eval(3).val @@ -180,9 +181,9 @@ def f(i): ar = SingleDimArray(i, dtype=self.float64_dtype) - v1 = interp_ufuncs.add(space, ar, scalar_w(space, self.float64_dtype, space.wrap(4.5))) + v1 = interp_ufuncs.get(self.space).add.call(space, [ar, scalar_w(space, self.float64_dtype, space.wrap(4.5))]) assert isinstance(v1, BaseArray) - v2 = interp_ufuncs.multiply(space, v1, scalar_w(space, self.float64_dtype, space.wrap(4.5))) + v2 = interp_ufuncs.get(self.space).multiply.call(space, [v1, scalar_w(space, self.float64_dtype, space.wrap(4.5))]) v1.force_if_needed() assert isinstance(v2, BaseArray) return v2.get_concrete().eval(3).val @@ -200,8 +201,8 @@ space = self.space def f(i): ar = SingleDimArray(i, dtype=self.float64_dtype) - v1 = interp_ufuncs.add(space, ar, ar) - v2 = interp_ufuncs.negative(space, v1) + v1 = interp_ufuncs.get(self.space).add.call(space, [ar, ar]) + v2 = interp_ufuncs.get(self.space).negative.call(space, [v1]) return v2.get_concrete().eval(3).val result = self.meta_interp(f, [5], listops=True, backendopt=True) @@ -216,13 +217,13 @@ def f(i): ar = SingleDimArray(i, dtype=self.float64_dtype) - v1 = interp_ufuncs.add(space, ar, ar) - v2 = interp_ufuncs.negative(space, v1) + v1 = interp_ufuncs.get(self.space).add.call(space, [ar, ar]) + v2 = interp_ufuncs.get(self.space).negative.call(space, [v1]) v2.get_concrete() for i in xrange(5): - v1 = interp_ufuncs.multiply(space, ar, ar) - v2 = interp_ufuncs.negative(space, v1) + v1 = interp_ufuncs.get(self.space).multiply.call(space, [ar, ar]) + v2 = interp_ufuncs.get(self.space).negative.call(space, [v1]) v2.get_concrete() self.meta_interp(f, [5], listops=True, backendopt=True) @@ -237,7 +238,7 @@ SingleDimSlice.signature, ar.signature ]) s = SingleDimSlice(0, step*i, step, i, ar, new_sig) - v = interp_ufuncs.add(self.space, s, s) + v = interp_ufuncs.get(self.space).add.call(self.space, [s, s]) return v.get_concrete().eval(3).val result = self.meta_interp(f, [5], listops=True, backendopt=True) @@ -259,7 +260,7 @@ SingleDimSlice.signature, s1.signature ]) s2 = SingleDimSlice(0, step2*i, step2, i, ar, new_sig) - v = interp_ufuncs.add(self.space, s1, s2) + v = interp_ufuncs.get(self.space).add.call(self.space, [s1, s2]) return v.get_concrete().eval(3).val result = self.meta_interp(f, [5], listops=True, backendopt=True) _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit