diff options
-rw-r--r-- | numpy/core/src/multiarray/common.c | 122 | ||||
-rw-r--r-- | numpy/core/src/multiarray/common.h | 3 | ||||
-rw-r--r-- | numpy/core/src/multiarray/ctors.c | 62 | ||||
-rw-r--r-- | numpy/core/src/multiarray/multiarraymodule.c | 3 | ||||
-rw-r--r-- | numpy/core/tests/test_api.py | 150 |
5 files changed, 275 insertions, 65 deletions
diff --git a/numpy/core/src/multiarray/common.c b/numpy/core/src/multiarray/common.c index 01bf9cd74..4787c6482 100644 --- a/numpy/core/src/multiarray/common.c +++ b/numpy/core/src/multiarray/common.c @@ -27,6 +27,60 @@ * be allowed under the NPY_SAME_KIND_CASTING rules, and if not we issue a * warning (that people's code will be broken in a future release.) */ + +/* + * PyArray_GetAttrString_SuppressException: + * + * Stripped down version of PyObject_GetAttrString, + * avoids lookups for None, tuple, and List objects, + * and doesn't create a PyErr since this code ignores it. + * + * This can be much faster then PyObject_GetAttrString where + * exceptions are not used by caller. + * + * 'obj' is the object to search for attribute. + * + * 'name' is the attribute to search for. + * + * Returns attribute value on success, 0 on failure. + */ +PyObject * +PyArray_GetAttrString_SuppressException(PyObject *obj, char *name) +{ + PyTypeObject *tp = Py_TYPE(obj); + PyObject *res = (PyObject *)NULL; + if (/* Is not trivial type */ + obj != Py_None && + !PyList_CheckExact(obj) && + !PyTuple_CheckExact(obj)) { + /* Attribute referenced by (char *)name */ + if (tp->tp_getattr != NULL) { + res = (*tp->tp_getattr)(obj, name); + if (res == NULL) { + PyErr_Clear(); + } + } + /* Attribute referenced by (PyObject *)name */ + else if (tp->tp_getattro != NULL) { +#if defined(NPY_PY3K) + PyObject *w = PyUnicode_InternFromString(name); +#else + PyObject *w = PyString_InternFromString(name); +#endif + if (w == NULL) + return (PyObject *)NULL; + Py_DECREF(w); + res = (*tp->tp_getattro)(obj, w); + if (res == NULL) { + PyErr_Clear(); + } + } + } + return res; +} + + + NPY_NO_EXPORT NPY_CASTING NPY_DEFAULT_ASSIGN_CASTING = NPY_INTERNAL_UNSAFE_CASTING_BUT_WARN_UNLESS_SAME_KIND; @@ -156,8 +210,17 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims, goto promote_types; } + /* See if it's a python None */ + if (obj == Py_None) { + dtype = PyArray_DescrFromType(NPY_OBJECT); + if (dtype == NULL) { + goto fail; + } + Py_INCREF(dtype); + goto promote_types; + } /* Check if it's a NumPy scalar */ - if (PyArray_IsScalar(obj, Generic)) { + else if (PyArray_IsScalar(obj, Generic)) { if (!string_type) { dtype = PyArray_DescrFromScalar(obj); if (dtype == NULL) { @@ -308,32 +371,35 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims, } /* PEP 3118 buffer interface */ - memset(&buffer_view, 0, sizeof(Py_buffer)); - if (PyObject_GetBuffer(obj, &buffer_view, PyBUF_FORMAT|PyBUF_STRIDES) == 0 || - PyObject_GetBuffer(obj, &buffer_view, PyBUF_FORMAT) == 0) { - - PyErr_Clear(); - dtype = _descriptor_from_pep3118_format(buffer_view.format); - PyBuffer_Release(&buffer_view); - if (dtype) { + if (PyObject_CheckBuffer(obj) == 1) { + memset(&buffer_view, 0, sizeof(Py_buffer)); + if (PyObject_GetBuffer(obj, &buffer_view, + PyBUF_FORMAT|PyBUF_STRIDES) == 0 || + PyObject_GetBuffer(obj, &buffer_view, PyBUF_FORMAT) == 0) { + + PyErr_Clear(); + dtype = _descriptor_from_pep3118_format(buffer_view.format); + PyBuffer_Release(&buffer_view); + if (dtype) { + goto promote_types; + } + } + else if (PyObject_GetBuffer(obj, &buffer_view, PyBUF_STRIDES) == 0 || + PyObject_GetBuffer(obj, &buffer_view, PyBUF_SIMPLE) == 0) { + + PyErr_Clear(); + dtype = PyArray_DescrNewFromType(NPY_VOID); + dtype->elsize = buffer_view.itemsize; + PyBuffer_Release(&buffer_view); goto promote_types; } - } - else if (PyObject_GetBuffer(obj, &buffer_view, PyBUF_STRIDES) == 0 || - PyObject_GetBuffer(obj, &buffer_view, PyBUF_SIMPLE) == 0) { - - PyErr_Clear(); - dtype = PyArray_DescrNewFromType(NPY_VOID); - dtype->elsize = buffer_view.itemsize; - PyBuffer_Release(&buffer_view); - goto promote_types; - } - else { - PyErr_Clear(); + else { + PyErr_Clear(); + } } /* The array interface */ - ip = PyObject_GetAttrString(obj, "__array_interface__"); + ip = PyArray_GetAttrString_SuppressException(obj, "__array_interface__"); if (ip != NULL) { if (PyDict_Check(ip)) { PyObject *typestr; @@ -364,12 +430,9 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims, } Py_DECREF(ip); } - else { - PyErr_Clear(); - } /* The array struct interface */ - ip = PyObject_GetAttrString(obj, "__array_struct__"); + ip = PyArray_GetAttrString_SuppressException(obj, "__array_struct__"); if (ip != NULL) { PyArrayInterface *inter; char buf[40]; @@ -389,9 +452,6 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims, } Py_DECREF(ip); } - else { - PyErr_Clear(); - } /* The old buffer interface */ #if !defined(NPY_PY3K) @@ -407,7 +467,9 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims, #endif /* The __array__ attribute */ - if (PyObject_HasAttrString(obj, "__array__")) { + ip = PyArray_GetAttrString_SuppressException(obj, "__array__"); + if (ip != NULL) { + Py_DECREF(ip); ip = PyObject_CallMethod(obj, "__array__", NULL); if(ip && PyArray_Check(ip)) { dtype = PyArray_DESCR((PyArrayObject *)ip); diff --git a/numpy/core/src/multiarray/common.h b/numpy/core/src/multiarray/common.h index a474cf820..68f8d246d 100644 --- a/numpy/core/src/multiarray/common.h +++ b/numpy/core/src/multiarray/common.h @@ -25,6 +25,9 @@ NPY_NO_EXPORT int PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims, PyArray_Descr **out_dtype, int string_status); +NPY_NO_EXPORT PyObject * +PyArray_GetAttrString_SuppressException(PyObject *v, char *name); + /* * Returns NULL without setting an exception if no scalar is matched, a * new dtype reference otherwise. diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index 5c692bd02..c96258417 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -646,32 +646,35 @@ discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it, /* obj is a PEP 3118 buffer */ #if PY_VERSION_HEX >= 0x02060000 /* PEP 3118 buffer interface */ - memset(&buffer_view, 0, sizeof(Py_buffer)); - if (PyObject_GetBuffer(obj, &buffer_view, PyBUF_STRIDES) == 0 || - PyObject_GetBuffer(obj, &buffer_view, PyBUF_ND) == 0) { - int nd = buffer_view.ndim; - if (nd < *maxndim) { - *maxndim = nd; + if (PyObject_CheckBuffer(obj) == 1) { + memset(&buffer_view, 0, sizeof(Py_buffer)); + if (PyObject_GetBuffer(obj, &buffer_view, PyBUF_STRIDES) == 0 || + PyObject_GetBuffer(obj, &buffer_view, PyBUF_ND) == 0) { + int nd = buffer_view.ndim; + if (nd < *maxndim) { + *maxndim = nd; + } + for (i=0; i<*maxndim; i++) { + d[i] = buffer_view.shape[i]; + } + PyBuffer_Release(&buffer_view); + return 0; } - for (i=0; i<*maxndim; i++) { - d[i] = buffer_view.shape[i]; + else if (PyObject_GetBuffer(obj, &buffer_view, PyBUF_SIMPLE) == 0) { + d[0] = buffer_view.len; + *maxndim = 1; + PyBuffer_Release(&buffer_view); + return 0; + } + else { + PyErr_Clear(); } - PyBuffer_Release(&buffer_view); - return 0; - } - else if (PyObject_GetBuffer(obj, &buffer_view, PyBUF_SIMPLE) == 0) { - d[0] = buffer_view.len; - *maxndim = 1; - PyBuffer_Release(&buffer_view); - return 0; - } - else { - PyErr_Clear(); } #endif /* obj has the __array_struct__ interface */ - if ((e = PyObject_GetAttrString(obj, "__array_struct__")) != NULL) { + e = PyArray_GetAttrString_SuppressException(obj, "__array_struct__"); + if (e != NULL) { int nd = -1; if (NpyCapsule_Check(e)) { PyArrayInterface *inter; @@ -693,12 +696,10 @@ discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it, return 0; } } - else { - PyErr_Clear(); - } /* obj has the __array_interface__ interface */ - if ((e = PyObject_GetAttrString(obj, "__array_interface__")) != NULL) { + e = PyArray_GetAttrString_SuppressException(obj, "__array_interface__"); + if (e != NULL) { int nd = -1; if (PyDict_Check(e)) { PyObject *new; @@ -728,9 +729,6 @@ discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it, return 0; } } - else { - PyErr_Clear(); - } n = PySequence_Size(obj); @@ -1934,9 +1932,8 @@ PyArray_FromStructInterface(PyObject *input) PyArrayObject *ret; char endian = NPY_NATBYTE; - attr = PyObject_GetAttrString(input, "__array_struct__"); + attr = PyArray_GetAttrString_SuppressException(input, "__array_struct__"); if (attr == NULL) { - PyErr_Clear(); return Py_NotImplemented; } if (!NpyCapsule_Check(attr)) { @@ -2009,9 +2006,9 @@ PyArray_FromInterface(PyObject *origin) /* Get the memory from __array_data__ and __array_offset__ */ /* Get the strides */ - iface = PyObject_GetAttrString(origin, "__array_interface__"); + iface = PyArray_GetAttrString_SuppressException(origin, + "__array_interface__"); if (iface == NULL) { - PyErr_Clear(); return Py_NotImplemented; } if (!PyDict_Check(iface)) { @@ -2226,9 +2223,8 @@ PyArray_FromArrayAttr(PyObject *op, PyArray_Descr *typecode, PyObject *context) PyObject *new; PyObject *array_meth; - array_meth = PyObject_GetAttrString(op, "__array__"); + array_meth = PyArray_GetAttrString_SuppressException(op, "__array__"); if (array_meth == NULL) { - PyErr_Clear(); return Py_NotImplemented; } if (context == NULL) { diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c index 7332a26d0..426ab695c 100644 --- a/numpy/core/src/multiarray/multiarraymodule.c +++ b/numpy/core/src/multiarray/multiarraymodule.c @@ -52,6 +52,7 @@ NPY_NO_EXPORT int NPY_NUMUSERTYPES = 0; #include "shape.h" #include "ctors.h" #include "array_assign.h" +#include "common.h" /* Only here for API compatibility */ NPY_NO_EXPORT PyTypeObject PyBigArray_Type; @@ -68,7 +69,7 @@ PyArray_GetPriority(PyObject *obj, double default_) if (PyArray_CheckExact(obj)) return priority; - ret = PyObject_GetAttrString(obj, "__array_priority__"); + ret = PyArray_GetAttrString_SuppressException(obj, "__array_priority__"); if (ret != NULL) { priority = PyFloat_AsDouble(ret); } diff --git a/numpy/core/tests/test_api.py b/numpy/core/tests/test_api.py index 1f56d6cf6..c681b57ab 100644 --- a/numpy/core/tests/test_api.py +++ b/numpy/core/tests/test_api.py @@ -9,7 +9,155 @@ import warnings from numpy.compat import sixu # Switch between new behaviour when NPY_RELAXED_STRIDES_CHECKING is set. -NPY_RELAXED_STRIDES_CHECKING = np.ones((10,1), order='C').flags.f_contiguous +NPY_RELAXED_STRIDES_CHECKING = np.ones((10, 1), order='C').flags.f_contiguous + + +def test_array_array(): + obj = object() + tobj = type(object) + ones11 = np.ones((1, 1), np.float64) + tndarray = type(ones11) + # Test is_ndarary + assert_equal(np.array(ones11, dtype=np.float64), ones11) + old_refcount = sys.getrefcount(tndarray) + np.array(ones11) + assert_equal(old_refcount, sys.getrefcount(tndarray)) + + # test None + assert_equal(np.array(None, dtype=np.float64), + np.array(np.nan, dtype=np.float64)) + old_refcount = sys.getrefcount(tobj) + np.array(None, dtype=np.float64) + assert_equal(old_refcount, sys.getrefcount(tobj)) + + # test scalar + assert_equal(np.array(1.0, dtype=np.float64), + np.ones((), dtype=np.float64)) + old_refcount = sys.getrefcount(np.float64) + np.array(np.array(1.0, dtype=np.float64), dtype=np.float64) + assert_equal(old_refcount, sys.getrefcount(np.float64)) + + # test string + S2 = np.dtype((str, 2)) + S3 = np.dtype((str, 3)) + S5 = np.dtype((str, 5)) + assert_equal(np.array("1.0", dtype=np.float64), + np.ones((), dtype=np.float64)) + assert_equal(np.array("1.0").dtype, S3) + assert_equal(np.array("1.0", dtype=str).dtype, S3) + assert_equal(np.array("1.0", dtype=S2), np.array("1.")) + assert_equal(np.array("1", dtype=S5), np.ones((), dtype=S5)) + + # test unicode + _unicode = globals().get("unicode") + if _unicode: + U2 = np.dtype((_unicode, 2)) + U3 = np.dtype((_unicode, 3)) + U5 = np.dtype((_unicode, 5)) + assert_equal(np.array(_unicode("1.0"), dtype=np.float64), + np.ones((), dtype=np.float64)) + assert_equal(np.array(_unicode("1.0")).dtype, U3) + assert_equal(np.array(_unicode("1.0"), dtype=_unicode).dtype, U3) + assert_equal(np.array(_unicode("1.0"), dtype=U2), + np.array(_unicode("1."))) + assert_equal(np.array(_unicode("1"), dtype=U5), + np.ones((), dtype=U5)) + + builtins = getattr(__builtins__, '__dict__', __builtins__) + assert_(isinstance(builtins, dict)) + + # test buffer + _buffer = builtins.get("buffer") + if _buffer: + assert_equal(np.array(_buffer("1.0"), dtype=np.float64), + np.array(1.0, dtype=np.float64)) + assert_equal(np.array(_buffer("1.0"), dtype=np.float64).dtype, + np.dtype("float64")) + assert_equal(np.array([_buffer("1.0")], dtype=np.float64), + np.array([1.0], dtype=np.float64)) + + # test memoryview, new version of buffer + _memoryview = builtins.get("memoryview") + if _memoryview: + assert_equal(np.array(_memoryview(bytearray(b'1.0')), + dtype=np.float64), + np.array([49.0, 46.0, 48.0], + dtype=np.float64)) + assert_equal(np.array(_memoryview(bytearray(b'1.0')), + dtype=np.float64).dtype, + np.dtype("float64")) + assert_equal(np.array(_memoryview(bytearray(b'1.0'))).dtype, + np.dtype('uint8')) + + # test array interface + a = np.array(100.0, dtype=np.float64) + o = type("o", (object,), + dict(__array_interface__=a.__array_interface__)) + assert_equal(np.array(o, dtype=np.float64), a) + + # test array_struct interface + a = np.array([(1, 4.0, 'Hello'), (2, 6.0, 'World')], + dtype=[('f0', int), ('f1', float), ('f2', str)]) + o = type("o", (object,), + dict(__array_struct__=a.__array_struct__)) + ## wasn't what I expected... is np.array(o) supposed to equal a ? + ## instead we get a array([...], dtype=">V18") + assert_equal(str(np.array(o).data), str(a.data)) + + # test array + o = type("o", (object,), + dict(__array__=lambda *x: np.array(100.0, dtype=np.float64)))() + assert_equal(np.array(o, dtype=np.float64), np.array(100.0, np.float64)) + + # test recursion + nested = 1.5 + for i in range(np.MAXDIMS): + nested = [nested] + + # no error + np.array(nested) + + # Exceeds recursion limit + assert_raises(ValueError, np.array, [nested], dtype=np.float64) + + # Try with lists... + assert_equal(np.array([None] * 10, dtype=np.float64), + np.empty((10,), dtype=np.float64) + np.nan) + assert_equal(np.array([[None]] * 10, dtype=np.float64), + np.empty((10, 1), dtype=np.float64) + np.nan) + assert_equal(np.array([[None] * 10], dtype=np.float64), + np.empty((1, 10), dtype=np.float64) + np.nan) + assert_equal(np.array([[None] * 10] * 10, dtype=np.float64), + np.empty((10, 10), dtype=np.float64) + np.nan) + + assert_equal(np.array([1.0] * 10, dtype=np.float64), + np.ones((10,), dtype=np.float64)) + assert_equal(np.array([[1.0]] * 10, dtype=np.float64), + np.ones((10, 1), dtype=np.float64)) + assert_equal(np.array([[1.0] * 10], dtype=np.float64), + np.ones((1, 10), dtype=np.float64)) + assert_equal(np.array([[1.0] * 10] * 10, dtype=np.float64), + np.ones((10, 10), dtype=np.float64)) + + # Try with tuples + assert_equal(np.array((None,) * 10, dtype=np.float64), + np.empty((10,), dtype=np.float64) + np.nan) + assert_equal(np.array([(None,)] * 10, dtype=np.float64), + np.empty((10, 1), dtype=np.float64) + np.nan) + assert_equal(np.array([(None,) * 10], dtype=np.float64), + np.empty((1, 10), dtype=np.float64) + np.nan) + assert_equal(np.array([(None,) * 10] * 10, dtype=np.float64), + np.empty((10, 10), dtype=np.float64) + np.nan) + + assert_equal(np.array((1.0,) * 10, dtype=np.float64), + np.ones((10,), dtype=np.float64)) + assert_equal(np.array([(1.0,)] * 10, dtype=np.float64), + np.ones((10, 1), dtype=np.float64)) + assert_equal(np.array([(1.0,) * 10], dtype=np.float64), + np.ones((1, 10), dtype=np.float64)) + assert_equal(np.array([(1.0,) * 10] * 10, dtype=np.float64), + np.ones((10, 10), dtype=np.float64)) + def test_fastCopyAndTranspose(): # 0D array |