Skip to content

Commit

Permalink
Add print and log facilities to error handling and change the default…
Browse files Browse the repository at this point in the history
… error mode to divide='print', over='print', invalid='print', and under='ignore'
  • Loading branch information
teoliphant committed Oct 19, 2006
1 parent 42ec061 commit 448f385
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 34 deletions.
2 changes: 1 addition & 1 deletion numpy/core/include/numpy/ndarrayobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ extern "C" CONFUSE_EMACS
#define NPY_SUCCEED 1

/* Helpful to distinguish what is installed */
#define NPY_VERSION 0x01000008
#define NPY_VERSION 0x01000009

/* Some platforms don't define bool, long long, or long double.
Handle that here.
Expand Down
41 changes: 26 additions & 15 deletions numpy/core/include/numpy/ufuncobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,20 @@ typedef struct {
#define UFUNC_ERR_WARN 1
#define UFUNC_ERR_RAISE 2
#define UFUNC_ERR_CALL 3
#define UFUNC_ERR_PRINT 4
#define UFUNC_ERR_LOG 5

/* Python side integer mask */

#define UFUNC_MASK_DIVIDEBYZERO 0x03
#define UFUNC_MASK_OVERFLOW 0x0c
#define UFUNC_MASK_UNDERFLOW 0x30
#define UFUNC_MASK_INVALID 0xc0
#define UFUNC_MASK_DIVIDEBYZERO 0x07
#define UFUNC_MASK_OVERFLOW 0x3f
#define UFUNC_MASK_UNDERFLOW 0x1ff
#define UFUNC_MASK_INVALID 0xfff

#define UFUNC_SHIFT_DIVIDEBYZERO 0
#define UFUNC_SHIFT_OVERFLOW 2
#define UFUNC_SHIFT_UNDERFLOW 4
#define UFUNC_SHIFT_INVALID 6
#define UFUNC_SHIFT_OVERFLOW 3
#define UFUNC_SHIFT_UNDERFLOW 6
#define UFUNC_SHIFT_INVALID 9


/* platform-dependent code translates floating point
Expand All @@ -49,7 +51,13 @@ typedef struct {
#define UFUNC_FPE_UNDERFLOW 4
#define UFUNC_FPE_INVALID 8

#define UFUNC_ERR_DEFAULT 0 /* Default error mode */
#define UFUNC_ERR_DEFAULT 0 /* Error mode that avoids look-up (no checking) */

/* Default user error mode */
#define UFUNC_ERR_DEFAULT2 \
(UFUNC_ERR_PRINT << UFUNC_SHIFT_DIVIDEBYZERO) + \
(UFUNC_ERR_PRINT << UFUNC_SHIFT_OVERFLOW) + \
(UFUNC_ERR_PRINT << UFUNC_SHIFT_INVALID)

/* Only internal -- not exported, yet*/
typedef struct {
Expand All @@ -70,8 +78,9 @@ typedef struct {
/* The error handling */
int errormask; /* Integer showing desired error handling */
PyObject *errobj; /* currently a tuple with
(string, func or None)
(string, func or obj with write method or None)
*/
int first;

/* Specific function and data to use */
PyUFuncGenericFunction function;
Expand Down Expand Up @@ -134,6 +143,7 @@ typedef struct {
/* The error handling */
int errormask;
PyObject *errobj;
int first;

PyUFuncGenericFunction function;
void *funcdata;
Expand Down Expand Up @@ -201,12 +211,13 @@ typedef struct _loop1d_info {
#include "__ufunc_api.h"

#define UFUNC_PYVALS_NAME "UFUNC_PYVALS"

#define UFUNC_CHECK_ERROR(arg) \
if (((arg)->obj && PyErr_Occurred()) || \
((arg)->errormask && \
PyUFunc_checkfperr((arg)->errormask, \
(arg)->errobj))) \

#define UFUNC_CHECK_ERROR(arg) \
if (((arg)->obj && PyErr_Occurred()) || \
((arg)->errormask && \
PyUFunc_checkfperr((arg)->errormask, \
(arg)->errobj, \
&(arg)->first))) \
goto fail

/* This code checks the IEEE status flags in a platform-dependent way */
Expand Down
16 changes: 10 additions & 6 deletions numpy/core/numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,9 @@ def array_equiv(a1, a2):
_errdict = {"ignore":ERR_IGNORE,
"warn":ERR_WARN,
"raise":ERR_RAISE,
"call":ERR_CALL}
"call":ERR_CALL,
"print":ERR_PRINT,
"log":ERR_LOG}

_errdict_rev = {}
for key in _errdict.keys():
Expand Down Expand Up @@ -690,10 +692,10 @@ def geterr():
Returns a dictionary with entries "divide", "over", "under", and
"invalid", whose values are from the strings
"ignore", "warn", "raise", and "call".
"ignore", "print", "log", "warn", "raise", and "call".
"""
maskvalue = umath.geterrobj()[1]
mask = 3
mask = 7
res = {}
val = (maskvalue >> SHIFT_DIVIDEBYZERO) & mask
res['divide'] = _errdict_rev[val]
Expand Down Expand Up @@ -728,7 +730,8 @@ def getbufsize():

def seterrcall(func):
"""Set the callback function used when a floating-point error handler
is set to 'call'.
is set to 'call' or the object with a write method for use when
the floating-point error handler is set to 'log'
'func' should be a function that takes two arguments. The first is
type of error ("divide", "over", "under", or "invalid"), and the second
Expand All @@ -737,7 +740,8 @@ def seterrcall(func):
Returns the old handler.
"""
if func is not None and not callable(func):
raise ValueError, "Only callable can be used as callback"
if not hasattr(func, 'write') or not callable(func.write):
raise ValueError, "Only callable can be used as callback"
pyvals = umath.geterrobj()
old = geterrcall()
pyvals[2] = func
Expand Down Expand Up @@ -784,7 +788,7 @@ def __exit__(self, *exc_info):
seterr(**self.oldstate)

def _setdef():
defval = [UFUNC_BUFSIZE_DEFAULT, ERR_DEFAULT, None]
defval = [UFUNC_BUFSIZE_DEFAULT, ERR_DEFAULT2, None]
umath.seterrobj(defval)

# set the default values
Expand Down
8 changes: 6 additions & 2 deletions numpy/core/src/scalarmathmodule.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ static PyObject *

#if @fperr@
int retstatus;
int first;
#endif

switch(_@name@_convert2_to_ctypes(a, &arg1, b, &arg2)) {
Expand Down Expand Up @@ -584,7 +585,8 @@ static PyObject *
if (PyUFunc_GetPyValues("@name@_scalars", &bufsize, &errmask,
&errobj) < 0)
return NULL;
if (PyUFunc_handlefperr(errmask, errobj, retstatus))
first = 1;
if (PyUFunc_handlefperr(errmask, errobj, retstatus, &first))
return NULL;
}
#endif
Expand Down Expand Up @@ -625,6 +627,7 @@ static PyObject *
PyObject *ret;
@name@ arg1, arg2;
int retstatus;
int first;

#if @cmplx@
@name@ out = {0,0};
Expand Down Expand Up @@ -678,7 +681,8 @@ static PyObject *
if (PyUFunc_GetPyValues("@name@_scalars", &bufsize, &errmask,
&errobj) < 0)
return NULL;
if (PyUFunc_handlefperr(errmask, errobj, retstatus))
first = 1;
if (PyUFunc_handlefperr(errmask, errobj, retstatus, &first))
return NULL;
}

Expand Down
52 changes: 42 additions & 10 deletions numpy/core/src/ufuncobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ PyUFunc_On_Om(char **args, intp *dimensions, intp *steps, void *func)
*/

static int
_error_handler(int method, PyObject *errobj, char *errtype, int retstatus)
_error_handler(int method, PyObject *errobj, char *errtype, int retstatus, int *first)
{
PyObject *pyfunc, *ret, *args;
char *name=PyString_AS_STRING(PyTuple_GET_ITEM(errobj,0));
Expand Down Expand Up @@ -512,6 +512,29 @@ _error_handler(int method, PyObject *errobj, char *errtype, int retstatus)
Py_DECREF(ret);

break;
case UFUNC_ERR_PRINT:
if (*first) {
fprintf(stderr, "Warning: %s encountered in %s\n", errtype, name);
*first = 0;
}
break;
case UFUNC_ERR_LOG:
if (first) {
*first = 0;
pyfunc = PyTuple_GET_ITEM(errobj, 1);
if (pyfunc == Py_None) {
PyErr_Format(PyExc_NameError,
"log specified for %s (in %s) but no " \
"object with write method found.",
errtype, name);
goto fail;
}
snprintf(msg, 100, "Warning: %s encountered in %s\n", errtype, name);
ret = PyObject_CallMethod(pyfunc, "write", "s", msg);
if (ret == NULL) goto fail;
Py_DECREF(ret);
}
break;
}
DISABLE_C_API
return 0;
Expand All @@ -535,13 +558,13 @@ PyUFunc_getfperr(void)
handle = errmask & UFUNC_MASK_##NAME;\
if (handle && \
_error_handler(handle >> UFUNC_SHIFT_##NAME, \
errobj, str, retstatus) < 0) \
errobj, str, retstatus, first) < 0) \
return -1; \
}}

/*UFUNC_API*/
static int
PyUFunc_handlefperr(int errmask, PyObject *errobj, int retstatus)
PyUFunc_handlefperr(int errmask, PyObject *errobj, int retstatus, int *first)
{
int handle;
if (errmask && retstatus) {
Expand All @@ -558,13 +581,13 @@ PyUFunc_handlefperr(int errmask, PyObject *errobj, int retstatus)

/*UFUNC_API*/
static int
PyUFunc_checkfperr(int errmask, PyObject *errobj)
PyUFunc_checkfperr(int errmask, PyObject *errobj, int *first)
{
int retstatus;

/* 1. check hardware flag --- this is platform dependent code */
retstatus = PyUFunc_getfperr();
return PyUFunc_handlefperr(errmask, errobj, retstatus);
return PyUFunc_handlefperr(errmask, errobj, retstatus, first);
}


Expand Down Expand Up @@ -958,14 +981,21 @@ _extract_pyvals(PyObject *ref, char *name, int *bufsize,
*errmask);
return -1;
}

retval = PyList_GET_ITEM(ref, 2);
if (retval != Py_None && !PyCallable_Check(retval)) {
PyErr_SetString(PyExc_TypeError,
"callback function must be callable");
return -1;
PyObject *temp;
temp = PyObject_GetAttrString(retval, "write");
if (temp == NULL || !PyCallable_Check(temp)) {
PyErr_SetString(PyExc_TypeError,
"python object must be callable or have " \
"a callable write method");
Py_XDECREF(temp);
return -1;
}
Py_DECREF(temp);
}

*errobj = Py_BuildValue("NO",
PyString_FromString(name),
retval);
Expand Down Expand Up @@ -1571,6 +1601,7 @@ construct_loop(PyUFuncObject *self, PyObject *args, PyObject *kwds, PyArrayObjec
}
loop->errobj = NULL;
loop->notimplemented = 0;
loop->first = 1;

name = self->name ? self->name : "";

Expand Down Expand Up @@ -2057,6 +2088,7 @@ construct_reduce(PyUFuncObject *self, PyArrayObject **arr, PyArrayObject *out,
loop->it = NULL;
loop->rit = NULL;
loop->errobj = NULL;
loop->first = 1;
loop->decref=NULL;
loop->N = (*arr)->dimensions[axis];
loop->instrides = (*arr)->strides[axis];
Expand Down
3 changes: 3 additions & 0 deletions numpy/core/src/umathmodule.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -2181,7 +2181,10 @@ PyMODINIT_FUNC initumath(void) {
ADDCONST(ERR_WARN);
ADDCONST(ERR_CALL);
ADDCONST(ERR_RAISE);
ADDCONST(ERR_PRINT);
ADDCONST(ERR_LOG);
ADDCONST(ERR_DEFAULT);
ADDCONST(ERR_DEFAULT2);

ADDCONST(SHIFT_DIVIDEBYZERO);
ADDCONST(SHIFT_OVERFLOW);
Expand Down

0 comments on commit 448f385

Please sign in to comment.