Author: Christian Tismer <tis...@stackless.com> Branch: win64-stage1 Changeset: r53740:3281ca232497 Date: 2012-03-16 17:08 -0700 http://bitbucket.org/pypy/pypy/changeset/3281ca232497/
Log: Merge with default diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py --- a/pypy/interpreter/gateway.py +++ b/pypy/interpreter/gateway.py @@ -901,15 +901,17 @@ def __init__(self, source, filename=None, modname='__builtin__'): # HAAACK (but a good one) + self.filename = filename + self.source = str(py.code.Source(source).deindent()) + self.modname = modname if filename is None: f = sys._getframe(1) filename = '<%s:%d>' % (f.f_code.co_filename, f.f_lineno) + if not os.path.exists(filename): + # make source code available for tracebacks + lines = [x + "\n" for x in source.split("\n")] + py.std.linecache.cache[filename] = (1, None, lines, filename) self.filename = filename - self.source = str(py.code.Source(source).deindent()) - self.modname = modname - # make source code available for tracebacks - lines = [x + "\n" for x in source.split("\n")] - py.std.linecache.cache[filename] = (1, None, lines, filename) def __repr__(self): return "<ApplevelClass filename=%r>" % (self.filename,) diff --git a/pypy/module/cpyext/pystate.py b/pypy/module/cpyext/pystate.py --- a/pypy/module/cpyext/pystate.py +++ b/pypy/module/cpyext/pystate.py @@ -10,7 +10,7 @@ [('next', PyInterpreterState)], PyInterpreterStateStruct) PyThreadState = lltype.Ptr(cpython_struct( - "PyThreadState", + "PyThreadState", [('interp', PyInterpreterState), ('dict', PyObject), ])) @@ -19,12 +19,15 @@ def PyEval_SaveThread(space): """Release the global interpreter lock (if it has been created and thread support is enabled) and reset the thread state to NULL, returning the - previous thread state (which is not NULL except in PyPy). If the lock has been created, + previous thread state. If the lock has been created, the current thread must have acquired it. (This function is available even when thread support is disabled at compile time.)""" + state = space.fromcache(InterpreterState) if rffi.aroundstate.before: rffi.aroundstate.before() - return lltype.nullptr(PyThreadState.TO) + tstate = state.swap_thread_state( + space, lltype.nullptr(PyThreadState.TO)) + return tstate @cpython_api([PyThreadState], lltype.Void) def PyEval_RestoreThread(space, tstate): @@ -35,6 +38,8 @@ when thread support is disabled at compile time.)""" if rffi.aroundstate.after: rffi.aroundstate.after() + state = space.fromcache(InterpreterState) + state.swap_thread_state(space, tstate) @cpython_api([], lltype.Void) def PyEval_InitThreads(space): @@ -67,28 +72,91 @@ dealloc=ThreadState_dealloc) from pypy.interpreter.executioncontext import ExecutionContext + +# Keep track of the ThreadStateCapsule for a particular execution context. The +# default is for new execution contexts not to have one; it is allocated on the +# first cpyext-based request for it. ExecutionContext.cpyext_threadstate = ThreadStateCapsule(None) +# Also keep track of whether it has been initialized yet or not (None is a valid +# PyThreadState for an execution context to have, when the GIL has been +# released, so a check against that can't be used to determine the need for +# initialization). +ExecutionContext.cpyext_initialized_threadstate = False + +def cleanup_cpyext_state(self): + try: + del self.cpyext_threadstate + except AttributeError: + pass + self.cpyext_initialized_threadstate = False +ExecutionContext.cleanup_cpyext_state = cleanup_cpyext_state + class InterpreterState(object): def __init__(self, space): self.interpreter_state = lltype.malloc( PyInterpreterState.TO, flavor='raw', zero=True, immortal=True) def new_thread_state(self, space): + """ + Create a new ThreadStateCapsule to hold the PyThreadState for a + particular execution context. + + :param space: A space. + + :returns: A new ThreadStateCapsule holding a newly allocated + PyThreadState and referring to this interpreter state. + """ capsule = ThreadStateCapsule(space) ts = capsule.memory ts.c_interp = self.interpreter_state ts.c_dict = make_ref(space, space.newdict()) return capsule + def get_thread_state(self, space): + """ + Get the current PyThreadState for the current execution context. + + :param space: A space. + + :returns: The current PyThreadState for the current execution context, + or None if it does not have one. + """ ec = space.getexecutioncontext() return self._get_thread_state(space, ec).memory + + def swap_thread_state(self, space, tstate): + """ + Replace the current thread state of the current execution context with a + new thread state. + + :param space: The space. + + :param tstate: The new PyThreadState for the current execution context. + + :returns: The old thread state for the current execution context, either + None or a PyThreadState. + """ + ec = space.getexecutioncontext() + capsule = self._get_thread_state(space, ec) + old_tstate = capsule.memory + capsule.memory = tstate + return old_tstate + def _get_thread_state(self, space, ec): - if ec.cpyext_threadstate.memory == lltype.nullptr(PyThreadState.TO): + """ + Get the ThreadStateCapsule for the given execution context, possibly + creating a new one if it does not already have one. + + :param space: The space. + :param ec: The ExecutionContext of which to get the thread state. + :returns: The ThreadStateCapsule for the given execution context. + """ + if not ec.cpyext_initialized_threadstate: ec.cpyext_threadstate = self.new_thread_state(space) - + ec.cpyext_initialized_threadstate = True return ec.cpyext_threadstate @cpython_api([], PyThreadState, error=CANNOT_FAIL) @@ -105,13 +173,8 @@ def PyThreadState_Swap(space, tstate): """Swap the current thread state with the thread state given by the argument tstate, which may be NULL. The global interpreter lock must be held.""" - # All cpyext calls release and acquire the GIL, so this function has no - # side-effects - if tstate: - return lltype.nullptr(PyThreadState.TO) - else: - state = space.fromcache(InterpreterState) - return state.get_thread_state(space) + state = space.fromcache(InterpreterState) + return state.swap_thread_state(space, tstate) @cpython_api([PyThreadState], lltype.Void) def PyEval_AcquireThread(space, tstate): diff --git a/pypy/module/cpyext/src/getargs.c b/pypy/module/cpyext/src/getargs.c --- a/pypy/module/cpyext/src/getargs.c +++ b/pypy/module/cpyext/src/getargs.c @@ -23,16 +23,33 @@ #define FLAG_COMPAT 1 #define FLAG_SIZE_T 2 +typedef int (*destr_t)(PyObject *, void *); + + +/* Keep track of "objects" that have been allocated or initialized and + which will need to be deallocated or cleaned up somehow if overall + parsing fails. +*/ +typedef struct { + void *item; + destr_t destructor; +} freelistentry_t; + +typedef struct { + int first_available; + freelistentry_t *entries; +} freelist_t; + /* Forward */ static int vgetargs1(PyObject *, const char *, va_list *, int); static void seterror(int, const char *, int *, const char *, const char *); static char *convertitem(PyObject *, const char **, va_list *, int, int *, - char *, size_t, PyObject **); + char *, size_t, freelist_t *); static char *converttuple(PyObject *, const char **, va_list *, int, - int *, char *, size_t, int, PyObject **); + int *, char *, size_t, int, freelist_t *); static char *convertsimple(PyObject *, const char **, va_list *, int, char *, - size_t, PyObject **); + size_t, freelist_t *); static Py_ssize_t convertbuffer(PyObject *, void **p, char **); static int getbuffer(PyObject *, Py_buffer *, char**); @@ -129,57 +146,56 @@ /* Handle cleanup of allocated memory in case of exception */ -static void -cleanup_ptr(void *ptr) +static int +cleanup_ptr(PyObject *self, void *ptr) { - PyMem_FREE(ptr); -} - -static void -cleanup_buffer(void *ptr) -{ - PyBuffer_Release((Py_buffer *) ptr); + if (ptr) { + PyMem_FREE(ptr); + } + return 0; } static int -addcleanup(void *ptr, PyObject **freelist, void (*destr)(void *)) +cleanup_buffer(PyObject *self, void *ptr) { - PyObject *cobj; - if (!*freelist) { - *freelist = PyList_New(0); - if (!*freelist) { - destr(ptr); - return -1; - } - } - cobj = PyCObject_FromVoidPtr(ptr, destr); - if (!cobj) { - destr(ptr); - return -1; - } - if (PyList_Append(*freelist, cobj)) { - Py_DECREF(cobj); - return -1; - } - Py_DECREF(cobj); - return 0; + Py_buffer *buf = (Py_buffer *)ptr; + if (buf) { + PyBuffer_Release(buf); + } + return 0; } static int -cleanreturn(int retval, PyObject *freelist) +addcleanup(void *ptr, freelist_t *freelist, destr_t destructor) { - if (freelist && retval != 0) { - /* We were successful, reset the destructors so that they - don't get called. */ - Py_ssize_t len = PyList_GET_SIZE(freelist), i; - for (i = 0; i < len; i++) - ((PyCObject *) PyList_GET_ITEM(freelist, i)) - ->destructor = NULL; - } - Py_XDECREF(freelist); - return retval; + int index; + + index = freelist->first_available; + freelist->first_available += 1; + + freelist->entries[index].item = ptr; + freelist->entries[index].destructor = destructor; + + return 0; } +static int +cleanreturn(int retval, freelist_t *freelist) +{ + int index; + + if (retval == 0) { + /* A failure occurred, therefore execute all of the cleanup + functions. + */ + for (index = 0; index < freelist->first_available; ++index) { + freelist->entries[index].destructor(NULL, + freelist->entries[index].item); + } + } + PyMem_Free(freelist->entries); + return retval; +} static int vgetargs1(PyObject *args, const char *format, va_list *p_va, int flags) @@ -195,7 +211,7 @@ const char *formatsave = format; Py_ssize_t i, len; char *msg; - PyObject *freelist = NULL; + freelist_t freelist = {0, NULL}; int compat = flags & FLAG_COMPAT; assert(compat || (args != (PyObject*)NULL)); @@ -251,16 +267,18 @@ format = formatsave; + freelist.entries = PyMem_New(freelistentry_t, max); + if (compat) { if (max == 0) { if (args == NULL) - return 1; + return cleanreturn(1, &freelist); PyOS_snprintf(msgbuf, sizeof(msgbuf), "%.200s%s takes no arguments", fname==NULL ? "function" : fname, fname==NULL ? "" : "()"); PyErr_SetString(PyExc_TypeError, msgbuf); - return 0; + return cleanreturn(0, &freelist); } else if (min == 1 && max == 1) { if (args == NULL) { @@ -269,26 +287,26 @@ fname==NULL ? "function" : fname, fname==NULL ? "" : "()"); PyErr_SetString(PyExc_TypeError, msgbuf); - return 0; + return cleanreturn(0, &freelist); } msg = convertitem(args, &format, p_va, flags, levels, msgbuf, sizeof(msgbuf), &freelist); if (msg == NULL) - return cleanreturn(1, freelist); + return cleanreturn(1, &freelist); seterror(levels[0], msg, levels+1, fname, message); - return cleanreturn(0, freelist); + return cleanreturn(0, &freelist); } else { PyErr_SetString(PyExc_SystemError, "old style getargs format uses new features"); - return 0; + return cleanreturn(0, &freelist); } } if (!PyTuple_Check(args)) { PyErr_SetString(PyExc_SystemError, "new style getargs format but argument is not a tuple"); - return 0; + return cleanreturn(0, &freelist); } len = PyTuple_GET_SIZE(args); @@ -308,7 +326,7 @@ message = msgbuf; } PyErr_SetString(PyExc_TypeError, message); - return 0; + return cleanreturn(0, &freelist); } for (i = 0; i < len; i++) { @@ -319,7 +337,7 @@ sizeof(msgbuf), &freelist); if (msg) { seterror(i+1, msg, levels, fname, message); - return cleanreturn(0, freelist); + return cleanreturn(0, &freelist); } } @@ -328,10 +346,10 @@ *format != '|' && *format != ':' && *format != ';') { PyErr_Format(PyExc_SystemError, "bad format string: %.200s", formatsave); - return cleanreturn(0, freelist); + return cleanreturn(0, &freelist); } - return cleanreturn(1, freelist); + return cleanreturn(1, &freelist); } @@ -395,7 +413,7 @@ static char * converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags, int *levels, char *msgbuf, size_t bufsize, int toplevel, - PyObject **freelist) + freelist_t *freelist) { int level = 0; int n = 0; @@ -472,7 +490,7 @@ static char * convertitem(PyObject *arg, const char **p_format, va_list *p_va, int flags, - int *levels, char *msgbuf, size_t bufsize, PyObject **freelist) + int *levels, char *msgbuf, size_t bufsize, freelist_t *freelist) { char *msg; const char *format = *p_format; @@ -539,7 +557,7 @@ static char * convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, - char *msgbuf, size_t bufsize, PyObject **freelist) + char *msgbuf, size_t bufsize, freelist_t *freelist) { /* For # codes */ #define FETCH_SIZE int *q=NULL;Py_ssize_t *q2=NULL;\ @@ -1501,7 +1519,9 @@ const char *fname, *msg, *custom_msg, *keyword; int min = INT_MAX; int i, len, nargs, nkeywords; - PyObject *freelist = NULL, *current_arg; + PyObject *current_arg; + freelist_t freelist = {0, NULL}; + assert(args != NULL && PyTuple_Check(args)); assert(keywords == NULL || PyDict_Check(keywords)); @@ -1525,6 +1545,8 @@ for (len=0; kwlist[len]; len++) continue; + freelist.entries = PyMem_New(freelistentry_t, len); + nargs = PyTuple_GET_SIZE(args); nkeywords = (keywords == NULL) ? 0 : PyDict_Size(keywords); if (nargs + nkeywords > len) { @@ -1535,7 +1557,7 @@ len, (len == 1) ? "" : "s", nargs + nkeywords); - return 0; + return cleanreturn(0, &freelist); } /* convert tuple args and keyword args in same loop, using kwlist to drive process */ @@ -1549,7 +1571,7 @@ PyErr_Format(PyExc_RuntimeError, "More keyword list entries (%d) than " "format specifiers (%d)", len, i); - return cleanreturn(0, freelist); + return cleanreturn(0, &freelist); } current_arg = NULL; if (nkeywords) { @@ -1563,11 +1585,11 @@ "Argument given by name ('%s') " "and position (%d)", keyword, i+1); - return cleanreturn(0, freelist); + return cleanreturn(0, &freelist); } } else if (nkeywords && PyErr_Occurred()) - return cleanreturn(0, freelist); + return cleanreturn(0, &freelist); else if (i < nargs) current_arg = PyTuple_GET_ITEM(args, i); @@ -1576,7 +1598,7 @@ levels, msgbuf, sizeof(msgbuf), &freelist); if (msg) { seterror(i+1, msg, levels, fname, custom_msg); - return cleanreturn(0, freelist); + return cleanreturn(0, &freelist); } continue; } @@ -1585,14 +1607,14 @@ PyErr_Format(PyExc_TypeError, "Required argument " "'%s' (pos %d) not found", keyword, i+1); - return cleanreturn(0, freelist); + return cleanreturn(0, &freelist); } /* current code reports success when all required args * fulfilled and no keyword args left, with no further * validation. XXX Maybe skip this in debug build ? */ if (!nkeywords) - return cleanreturn(1, freelist); + return cleanreturn(1, &freelist); /* We are into optional args, skip thru to any remaining * keyword args */ @@ -1600,7 +1622,7 @@ if (msg) { PyErr_Format(PyExc_RuntimeError, "%s: '%s'", msg, format); - return cleanreturn(0, freelist); + return cleanreturn(0, &freelist); } } @@ -1608,7 +1630,7 @@ PyErr_Format(PyExc_RuntimeError, "more argument specifiers than keyword list entries " "(remaining format:'%s')", format); - return cleanreturn(0, freelist); + return cleanreturn(0, &freelist); } /* make sure there are no extraneous keyword arguments */ @@ -1621,7 +1643,7 @@ if (!PyString_Check(key)) { PyErr_SetString(PyExc_TypeError, "keywords must be strings"); - return cleanreturn(0, freelist); + return cleanreturn(0, &freelist); } ks = PyString_AsString(key); for (i = 0; i < len; i++) { @@ -1635,12 +1657,12 @@ "'%s' is an invalid keyword " "argument for this function", ks); - return cleanreturn(0, freelist); + return cleanreturn(0, &freelist); } } } - return cleanreturn(1, freelist); + return cleanreturn(1, &freelist); } diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py --- a/pypy/module/cpyext/test/test_cpyext.py +++ b/pypy/module/cpyext/test/test_cpyext.py @@ -106,10 +106,7 @@ del obj import gc; gc.collect() - try: - del space.getexecutioncontext().cpyext_threadstate - except AttributeError: - pass + space.getexecutioncontext().cleanup_cpyext_state() for w_obj in state.non_heaptypes_w: Py_DecRef(space, w_obj) diff --git a/pypy/module/cpyext/test/test_pystate.py b/pypy/module/cpyext/test/test_pystate.py --- a/pypy/module/cpyext/test/test_pystate.py +++ b/pypy/module/cpyext/test/test_pystate.py @@ -3,6 +3,10 @@ from pypy.rpython.lltypesystem.lltype import nullptr from pypy.module.cpyext.pystate import PyInterpreterState, PyThreadState from pypy.module.cpyext.pyobject import from_ref +from pypy.rpython.lltypesystem import lltype +from pypy.module.cpyext.test.test_cpyext import LeakCheckingTest, freeze_refcnts +from pypy.module.cpyext.pystate import PyThreadState_Get, PyInterpreterState_Head +from pypy.tool import leakfinder class AppTestThreads(AppTestCpythonExtensionBase): def test_allow_threads(self): @@ -21,6 +25,93 @@ # Should compile at least module.test() + + def test_thread_state_get(self): + module = self.import_extension('foo', [ + ("get", "METH_NOARGS", + """ + PyThreadState *tstate = PyThreadState_Get(); + if (tstate == NULL) { + return PyLong_FromLong(0); + } + if (tstate->interp != PyInterpreterState_Head()) { + return PyLong_FromLong(1); + } + if (tstate->interp->next != NULL) { + return PyLong_FromLong(2); + } + return PyLong_FromLong(3); + """), + ]) + assert module.get() == 3 + + def test_basic_threadstate_dance(self): + module = self.import_extension('foo', [ + ("dance", "METH_NOARGS", + """ + PyThreadState *old_tstate, *new_tstate; + + old_tstate = PyThreadState_Swap(NULL); + if (old_tstate == NULL) { + return PyLong_FromLong(0); + } + + new_tstate = PyThreadState_Get(); + if (new_tstate != NULL) { + return PyLong_FromLong(1); + } + + new_tstate = PyThreadState_Swap(old_tstate); + if (new_tstate != NULL) { + return PyLong_FromLong(2); + } + + new_tstate = PyThreadState_Get(); + if (new_tstate != old_tstate) { + return PyLong_FromLong(3); + } + + return PyLong_FromLong(4); + """), + ]) + assert module.dance() == 4 + + def test_threadstate_dict(self): + module = self.import_extension('foo', [ + ("getdict", "METH_NOARGS", + """ + PyObject *dict = PyThreadState_GetDict(); + Py_INCREF(dict); + return dict; + """), + ]) + assert isinstance(module.getdict(), dict) + + def test_savethread(self): + module = self.import_extension('foo', [ + ("bounce", "METH_NOARGS", + """ + PyThreadState *tstate = PyEval_SaveThread(); + if (tstate == NULL) { + return PyLong_FromLong(0); + } + + if (PyThreadState_Get() != NULL) { + return PyLong_FromLong(1); + } + + PyEval_RestoreThread(tstate); + + if (PyThreadState_Get() != tstate) { + return PyLong_FromLong(2); + } + + return PyLong_FromLong(3); + """), + ]) + + + class TestInterpreterState(BaseApiTest): def test_interpreter_head(self, space, api): state = api.PyInterpreterState_Head() @@ -29,31 +120,3 @@ def test_interpreter_next(self, space, api): state = api.PyInterpreterState_Head() assert nullptr(PyInterpreterState.TO) == api.PyInterpreterState_Next(state) - -class TestThreadState(BaseApiTest): - def test_thread_state_get(self, space, api): - ts = api.PyThreadState_Get() - assert ts != nullptr(PyThreadState.TO) - - def test_thread_state_interp(self, space, api): - ts = api.PyThreadState_Get() - assert ts.c_interp == api.PyInterpreterState_Head() - assert ts.c_interp.c_next == nullptr(PyInterpreterState.TO) - - def test_basic_threadstate_dance(self, space, api): - # Let extension modules call these functions, - # Not sure of the semantics in pypy though. - # (cpyext always acquires and releases the GIL around calls) - tstate = api.PyThreadState_Swap(None) - assert tstate is not None - assert not api.PyThreadState_Swap(tstate) - - api.PyEval_AcquireThread(tstate) - api.PyEval_ReleaseThread(tstate) - - def test_threadstate_dict(self, space, api): - ts = api.PyThreadState_Get() - ref = ts.c_dict - assert ref == api.PyThreadState_GetDict() - w_obj = from_ref(space, ref) - assert space.isinstance_w(w_obj, space.w_dict) 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 @@ -67,6 +67,7 @@ ("arccos", "arccos"), ("arcsin", "arcsin"), ("arctan", "arctan"), + ("arctan2", "arctan2"), ("arccosh", "arccosh"), ("arcsinh", "arcsinh"), ("arctanh", "arctanh"), @@ -77,7 +78,10 @@ ("true_divide", "true_divide"), ("equal", "equal"), ("exp", "exp"), + ("exp2", "exp2"), + ("expm1", "expm1"), ("fabs", "fabs"), + ("fmod", "fmod"), ("floor", "floor"), ("ceil", "ceil"), ("greater", "greater"), @@ -92,8 +96,10 @@ ("radians", "radians"), ("degrees", "degrees"), ("deg2rad", "radians"), + ("rad2deg", "degrees"), ("reciprocal", "reciprocal"), ("sign", "sign"), + ("signbit", "signbit"), ("sin", "sin"), ("sinh", "sinh"), ("subtract", "subtract"), @@ -106,6 +112,9 @@ ('bitwise_not', 'invert'), ('isnan', 'isnan'), ('isinf', 'isinf'), + ('isneginf', 'isneginf'), + ('isposinf', 'isposinf'), + ('isfinite', 'isfinite'), ('logical_and', 'logical_and'), ('logical_xor', 'logical_xor'), ('logical_not', 'logical_not'), @@ -116,6 +125,8 @@ ('log1p', 'log1p'), ('power', 'power'), ('floor_divide', 'floor_divide'), + ('logaddexp', 'logaddexp'), + ('logaddexp2', 'logaddexp2'), ]: interpleveldefs[exposed] = "interp_ufuncs.get(space).%s" % impl 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 @@ -478,7 +478,9 @@ def reshape(self, space, new_shape): concrete = self.get_concrete() # Since we got to here, prod(new_shape) == self.size - new_strides = calc_new_strides(new_shape, concrete.shape, + new_strides = None + if self.size > 0: + new_strides = calc_new_strides(new_shape, concrete.shape, concrete.strides, concrete.order) if new_strides: # We can create a view, strides somehow match up. @@ -1031,7 +1033,7 @@ def setshape(self, space, new_shape): if len(self.shape) < 1: return - elif len(self.shape) < 2: + elif len(self.shape) < 2 or self.size < 1: # TODO: this code could be refactored into calc_strides # but then calc_strides would have to accept a stepping factor strides = [] @@ -1042,7 +1044,7 @@ for sh in new_shape: strides.append(s) backstrides.append(s * (sh - 1)) - s *= sh + s *= max(1, sh) if self.order == 'C': strides.reverse() backstrides.reverse() 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 @@ -404,6 +404,9 @@ ("greater_equal", "ge", 2, {"comparison_func": True}), ("isnan", "isnan", 1, {"bool_result": True}), ("isinf", "isinf", 1, {"bool_result": True}), + ("isneginf", "isneginf", 1, {"bool_result": True}), + ("isposinf", "isposinf", 1, {"bool_result": True}), + ("isfinite", "isfinite", 1, {"bool_result": True}), ('logical_and', 'logical_and', 2, {'comparison_func': True, 'identity': 1}), @@ -421,12 +424,16 @@ ("negative", "neg", 1), ("absolute", "abs", 1), ("sign", "sign", 1, {"promote_bools": True}), + ("signbit", "signbit", 1, {"bool_result": True}), ("reciprocal", "reciprocal", 1), ("fabs", "fabs", 1, {"promote_to_float": True}), + ("fmod", "fmod", 2, {"promote_to_float": True}), ("floor", "floor", 1, {"promote_to_float": True}), ("ceil", "ceil", 1, {"promote_to_float": True}), ("exp", "exp", 1, {"promote_to_float": True}), + ("exp2", "exp2", 1, {"promote_to_float": True}), + ("expm1", "expm1", 1, {"promote_to_float": True}), ('sqrt', 'sqrt', 1, {'promote_to_float': True}), @@ -436,6 +443,7 @@ ("arcsin", "arcsin", 1, {"promote_to_float": True}), ("arccos", "arccos", 1, {"promote_to_float": True}), ("arctan", "arctan", 1, {"promote_to_float": True}), + ("arctan2", "arctan2", 2, {"promote_to_float": True}), ("sinh", "sinh", 1, {"promote_to_float": True}), ("cosh", "cosh", 1, {"promote_to_float": True}), ("tanh", "tanh", 1, {"promote_to_float": True}), @@ -450,6 +458,8 @@ ("log2", "log2", 1, {"promote_to_float": True}), ("log10", "log10", 1, {"promote_to_float": True}), ("log1p", "log1p", 1, {"promote_to_float": True}), + ("logaddexp", "logaddexp", 2, {"promote_to_float": True}), + ("logaddexp2", "logaddexp2", 2, {"promote_to_float": True}), ]: self.add_ufunc(space, *ufunc_def) diff --git a/pypy/module/micronumpy/test/test_numarray.py b/pypy/module/micronumpy/test/test_numarray.py --- a/pypy/module/micronumpy/test/test_numarray.py +++ b/pypy/module/micronumpy/test/test_numarray.py @@ -434,6 +434,8 @@ a = zeros((4, 2, 3)) a.shape = (12, 2) (a + a).reshape(2, 12) # assert did not explode + a = array([[[[]]]]) + assert a.reshape((0,)).shape == (0,) def test_slice_reshape(self): from _numpypy import zeros, arange 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 @@ -113,14 +113,37 @@ assert (divide(array([-10]), array([2])) == array([-5])).all() + def test_true_divide(self): + from _numpypy import array, true_divide + + a = array([0, 1, 2, 3, 4, 1, -1]) + b = array([4, 4, 4, 4, 4, 0, 0]) + c = true_divide(a, b) + assert (c == [0.0, 0.25, 0.5, 0.75, 1.0, float('inf'), float('-inf')]).all() + + assert math.isnan(true_divide(0, 0)) + def test_fabs(self): from _numpypy import array, fabs - from math import fabs as math_fabs + from math import fabs as math_fabs, isnan a = array([-5.0, -0.0, 1.0]) b = fabs(a) for i in range(3): assert b[i] == math_fabs(a[i]) + assert fabs(float('inf')) == float('inf') + assert fabs(float('-inf')) == float('inf') + assert isnan(fabs(float('nan'))) + + def test_fmod(self): + from _numpypy import fmod + import math + + assert fmod(-1e-100, 1e100) == -1e-100 + assert fmod(3, float('inf')) == 3 + assert (fmod([-3, -2, -1, 1, 2, 3], 2) == [-1, 0, -1, 1, 0, 1]).all() + for v in [float('inf'), float('-inf'), float('nan'), float('-nan')]: + assert math.isnan(fmod(v, 2)) def test_minimum(self): from _numpypy import array, minimum @@ -172,6 +195,15 @@ assert a[0] == 1 assert a[1] == 0 + def test_signbit(self): + from _numpypy import signbit, copysign + import struct + + assert (signbit([0, 0.0, 1, 1.0, float('inf'), float('nan')]) == + [False, False, False, False, False, False]).all() + assert (signbit([-0, -0.0, -1, -1.0, float('-inf'), -float('nan'), float('-nan')]) == + [False, True, True, True, True, True, True]).all() + def test_reciporocal(self): from _numpypy import array, reciprocal @@ -231,13 +263,46 @@ a = array([-5.0, -0.0, 0.0, 12345678.0, float("inf"), -float('inf'), -12343424.0]) b = exp(a) - for i in range(4): + for i in range(len(a)): try: res = math.exp(a[i]) except OverflowError: res = float('inf') assert b[i] == res + def test_exp2(self): + import math + from _numpypy import array, exp2 + + a = array([-5.0, -0.0, 0.0, 2, 12345678.0, float("inf"), + -float('inf'), -12343424.0]) + b = exp2(a) + for i in range(len(a)): + try: + res = 2 ** a[i] + except OverflowError: + res = float('inf') + assert b[i] == res + + assert exp2(3) == 8 + assert math.isnan(exp2(float("nan"))) + + def test_expm1(self): + import math + from _numpypy import array, expm1 + + a = array([-5.0, -0.0, 0.0, 12345678.0, float("inf"), + -float('inf'), -12343424.0]) + b = expm1(a) + for i in range(4): + try: + res = math.exp(a[i]) - 1 + except OverflowError: + res = float('inf') + assert b[i] == res + + assert expm1(1e-50) == 1e-50 + def test_sin(self): import math from _numpypy import array, sin @@ -310,6 +375,21 @@ b = arctan(a) assert math.isnan(b[0]) + def test_arctan2(self): + import math + from _numpypy import array, arctan2 + + # From the numpy documentation + assert ( + arctan2( + [0., 0., 1., -1., float('inf'), float('inf')], + [0., -0., float('inf'), float('inf'), float('inf'), float('-inf')]) == + [0., math.pi, 0., -0., math.pi/4, 3*math.pi/4]).all() + + a = array([float('nan')]) + b = arctan2(a, 0) + assert math.isnan(b[0]) + def test_sinh(self): import math from _numpypy import array, sinh @@ -415,6 +495,19 @@ for i in range(len(a)): assert b[i] == math.degrees(a[i]) + def test_rad2deg(self): + import math + from _numpypy import rad2deg, array + a = array([ + -181, -180, -179, + 181, 180, 179, + 359, 360, 361, + 400, -1, 0, 1, + float('inf'), float('-inf')]) + b = rad2deg(a) + for i in range(len(a)): + assert b[i] == math.degrees(a[i]) + def test_reduce_errors(self): from _numpypy import sin, add @@ -510,6 +603,26 @@ assert (isinf(array([0.2, float('inf'), float('nan')])) == [False, True, False]).all() assert isinf(array([0.2])).dtype.kind == 'b' + def test_isposinf_isneginf(self): + from _numpypy import isneginf, isposinf + assert isposinf(float('inf')) + assert not isposinf(float('-inf')) + assert not isposinf(float('nan')) + assert not isposinf(0) + assert not isposinf(0.0) + assert isneginf(float('-inf')) + assert not isneginf(float('inf')) + assert not isneginf(float('nan')) + assert not isneginf(0) + assert not isneginf(0.0) + + def test_isfinite(self): + from _numpypy import isfinite + assert (isfinite([0, 0.0, 1e50, -1e-50]) == + [True, True, True, True]).all() + assert (isfinite([float('-inf'), float('inf'), float('-nan'), float('nan')]) == + [False, False, False, False]).all() + def test_logical_ops(self): from _numpypy import logical_and, logical_or, logical_xor, logical_not @@ -544,7 +657,7 @@ assert log1p(float('inf')) == float('inf') assert (log1p([0, 1e-50, math.e - 1]) == [0, 1e-50, 1]).all() - def test_power(self): + def test_power_float(self): import math from _numpypy import power, array a = array([1., 2., 3.]) @@ -558,9 +671,94 @@ for i in range(len(a)): assert c[i] == a[i] ** b[i] + assert power(2, float('inf')) == float('inf') + assert power(float('inf'), float('inf')) == float('inf') + assert power(12345.0, 12345.0) == float('inf') + assert power(-12345.0, 12345.0) == float('-inf') + assert power(-12345.0, 12346.0) == float('inf') + assert math.isnan(power(-1, 1.1)) + assert math.isnan(power(-1, -1.1)) + assert power(-2.0, -1) == -0.5 + assert power(-2.0, -2) == 0.25 + assert power(12345.0, -12345.0) == 0 + assert power(float('-inf'), 2) == float('inf') + assert power(float('-inf'), 2.5) == float('inf') + assert power(float('-inf'), 3) == float('-inf') + + def test_power_int(self): + import math + from _numpypy import power, array + a = array([1, 2, 3]) + b = power(a, 3) + for i in range(len(a)): + assert b[i] == a[i] ** 3 + + a = array([1, 2, 3]) + b = array([1, 2, 3]) + c = power(a, b) + for i in range(len(a)): + assert c[i] == a[i] ** b[i] + + # assert power(12345, 12345) == -9223372036854775808 + # assert power(-12345, 12345) == -9223372036854775808 + # assert power(-12345, 12346) == -9223372036854775808 + assert power(2, 0) == 1 + assert power(2, -1) == 0 + assert power(2, -2) == 0 + assert power(-2, -1) == 0 + assert power(-2, -2) == 0 + assert power(12345, -12345) == 0 + def test_floordiv(self): from _numpypy import floor_divide, array a = array([1., 2., 3., 4., 5., 6., 6.01]) b = floor_divide(a, 2.5) for i in range(len(a)): assert b[i] == a[i] // 2.5 + + def test_logaddexp(self): + import math + from _numpypy import logaddexp + + # From the numpy documentation + prob1 = math.log(1e-50) + prob2 = math.log(2.5e-50) + prob12 = logaddexp(prob1, prob2) + assert math.fabs(-113.87649168120691 - prob12) < 0.000000000001 + + assert logaddexp(0, 0) == math.log(2) + assert logaddexp(float('-inf'), 0) == 0 + assert logaddexp(12345678, 12345678) == float('inf') + + assert math.isnan(logaddexp(float('nan'), 1)) + assert math.isnan(logaddexp(1, float('nan'))) + assert math.isnan(logaddexp(float('nan'), float('inf'))) + assert math.isnan(logaddexp(float('inf'), float('nan'))) + assert logaddexp(float('-inf'), float('-inf')) == float('-inf') + assert logaddexp(float('-inf'), float('inf')) == float('inf') + assert logaddexp(float('inf'), float('-inf')) == float('inf') + assert logaddexp(float('inf'), float('inf')) == float('inf') + + def test_logaddexp2(self): + import math + from _numpypy import logaddexp2 + log2 = math.log(2) + + # From the numpy documentation + prob1 = math.log(1e-50) / log2 + prob2 = math.log(2.5e-50) / log2 + prob12 = logaddexp2(prob1, prob2) + assert math.fabs(-164.28904982231052 - prob12) < 0.000000000001 + + assert logaddexp2(0, 0) == 1 + assert logaddexp2(float('-inf'), 0) == 0 + assert logaddexp2(12345678, 12345678) == float('inf') + + assert math.isnan(logaddexp2(float('nan'), 1)) + assert math.isnan(logaddexp2(1, float('nan'))) + assert math.isnan(logaddexp2(float('nan'), float('inf'))) + assert math.isnan(logaddexp2(float('inf'), float('nan'))) + assert logaddexp2(float('-inf'), float('-inf')) == float('-inf') + assert logaddexp2(float('-inf'), float('inf')) == float('inf') + assert logaddexp2(float('inf'), float('-inf')) == float('inf') + assert logaddexp2(float('inf'), float('inf')) == float('inf') 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 @@ -155,6 +155,14 @@ def isinf(self, v): return False + @raw_unary_op + def isneginf(self, v): + return False + + @raw_unary_op + def isposinf(self, v): + return False + @raw_binary_op def eq(self, v1, v2): return v1 == v2 @@ -293,6 +301,8 @@ @simple_binary_op def pow(self, v1, v2): + if v2 < 0: + return 0 res = 1 while v2 > 0: if v2 & 1: @@ -440,7 +450,15 @@ @simple_binary_op def pow(self, v1, v2): - return math.pow(v1, v2) + try: + return math.pow(v1, v2) + except ValueError: + return rfloat.NAN + except OverflowError: + if math.modf(v2)[0] == 0 and math.modf(v2 / 2)[0] != 0: + # Odd integer powers result in the same sign as the base + return rfloat.copysign(rfloat.INFINITY, v1) + return rfloat.INFINITY @simple_binary_op def copysign(self, v1, v2): @@ -452,10 +470,21 @@ return 0.0 return rfloat.copysign(1.0, v) + @raw_unary_op + def signbit(self, v): + return rfloat.copysign(1.0, v) < 0.0 + @simple_unary_op def fabs(self, v): return math.fabs(v) + @simple_binary_op + def fmod(self, v1, v2): + try: + return math.fmod(v1, v2) + except ValueError: + return rfloat.NAN + @simple_unary_op def reciprocal(self, v): if v == 0.0: @@ -478,6 +507,20 @@ return rfloat.INFINITY @simple_unary_op + def exp2(self, v): + try: + return math.pow(2, v) + except OverflowError: + return rfloat.INFINITY + + @simple_unary_op + def expm1(self, v): + try: + return rfloat.expm1(v) + except OverflowError: + return rfloat.INFINITY + + @simple_unary_op def sin(self, v): return math.sin(v) @@ -505,6 +548,10 @@ def arctan(self, v): return math.atan(v) + @simple_binary_op + def arctan2(self, v1, v2): + return math.atan2(v1, v2) + @simple_unary_op def sinh(self, v): return math.sinh(v) @@ -550,6 +597,18 @@ def isinf(self, v): return rfloat.isinf(v) + @raw_unary_op + def isneginf(self, v): + return rfloat.isinf(v) and v < 0 + + @raw_unary_op + def isposinf(self, v): + return rfloat.isinf(v) and v > 0 + + @raw_unary_op + def isfinite(self, v): + return not (rfloat.isinf(v) or rfloat.isnan(v)) + @simple_unary_op def radians(self, v): return v * degToRad @@ -601,6 +660,48 @@ except ValueError: return rfloat.NAN + @simple_binary_op + def logaddexp(self, v1, v2): + try: + v1e = math.exp(v1) + except OverflowError: + v1e = rfloat.INFINITY + try: + v2e = math.exp(v2) + except OverflowError: + v2e = rfloat.INFINITY + + v12e = v1e + v2e + try: + return math.log(v12e) + except ValueError: + if v12e == 0.0: + # CPython raises ValueError here, so we have to check + # the value to find the correct numpy return value + return -rfloat.INFINITY + return rfloat.NAN + + @simple_binary_op + def logaddexp2(self, v1, v2): + try: + v1e = math.pow(2, v1) + except OverflowError: + v1e = rfloat.INFINITY + try: + v2e = math.pow(2, v2) + except OverflowError: + v2e = rfloat.INFINITY + + v12e = v1e + v2e + try: + return math.log(v12e) / log2 + except ValueError: + if v12e == 0.0: + # CPython raises ValueError here, so we have to check + # the value to find the correct numpy return value + return -rfloat.INFINITY + return rfloat.NAN + class Float32(BaseType, Float): T = rffi.FLOAT diff --git a/pypy/objspace/std/test/test_userobject.py b/pypy/objspace/std/test/test_userobject.py --- a/pypy/objspace/std/test/test_userobject.py +++ b/pypy/objspace/std/test/test_userobject.py @@ -10,10 +10,10 @@ from pypy import conftest cls.space = conftest.gettestobjspace(**cls.OPTIONS) cls.w_runappdirect = cls.space.wrap(bool(conftest.option.runappdirect)) - - def w_rand(self): - import random - return random.randrange(0, 5) + def rand(space): + import random + return space.wrap(random.randrange(0, 5)) + cls.w_rand = cls.space.wrap(gateway.interp2app(rand)) def test_emptyclass(self): class empty(object): pass _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit