Skip to content

Commit

Permalink
Merge pull request #2815 from certik/backport444
Browse files Browse the repository at this point in the history
Backport444
  • Loading branch information
certik committed Dec 14, 2012
2 parents cb0ee58 + 590811f commit 27a9c36
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 102 deletions.
19 changes: 17 additions & 2 deletions numpy/core/src/multiarray/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -341,9 +341,24 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims,
if (ip != NULL) {
if (PyDict_Check(ip)) {
PyObject *typestr;
#if defined(NPY_PY3K)
PyObject *tmp = NULL;
#endif
typestr = PyDict_GetItemString(ip, "typestr");
if (typestr && PyString_Check(typestr)) {
dtype =_array_typedescr_fromstr(PyString_AS_STRING(typestr));
#if defined(NPY_PY3K)
/* Allow unicode type strings */
if (PyUnicode_Check(typestr)) {
tmp = PyUnicode_AsASCIIString(typestr);
typestr = tmp;
}
#endif
if (typestr && PyBytes_Check(typestr)) {
dtype =_array_typedescr_fromstr(PyBytes_AS_STRING(typestr));
#if defined(NPY_PY3K)
if (tmp == typestr) {
Py_DECREF(tmp);
}
#endif
Py_DECREF(ip);
if (dtype == NULL) {
goto fail;
Expand Down
241 changes: 141 additions & 100 deletions numpy/core/src/multiarray/ctors.c
Original file line number Diff line number Diff line change
Expand Up @@ -1977,84 +1977,113 @@ PyArray_FromStructInterface(PyObject *input)

/*NUMPY_API*/
NPY_NO_EXPORT PyObject *
PyArray_FromInterface(PyObject *input)
PyArray_FromInterface(PyObject *origin)
{
PyObject *attr = NULL, *item = NULL;
PyObject *tstr = NULL, *shape = NULL;
PyObject *inter = NULL;
PyObject *tmp = NULL;
PyObject *iface = NULL;
PyObject *attr = NULL;
PyObject *base = NULL;
PyArrayObject *ret;
PyArray_Descr *type=NULL;
char *data;
PyArray_Descr *dtype = NULL;
char *data = NULL;
Py_ssize_t buffer_len;
int res, i, n;
npy_intp dims[NPY_MAXDIMS], strides[NPY_MAXDIMS];
int dataflags = NPY_ARRAY_BEHAVED;

/* Get the memory from __array_data__ and __array_offset__ */
/* Get the shape */
/* Get the typestring -- ignore array_descr */
/* Get the shape */
/* Get the memory from __array_data__ and __array_offset__ */
/* Get the strides */

inter = PyObject_GetAttrString(input, "__array_interface__");
if (inter == NULL) {
iface = PyObject_GetAttrString(origin, "__array_interface__");
if (iface == NULL) {
PyErr_Clear();
return Py_NotImplemented;
}
if (!PyDict_Check(inter)) {
Py_DECREF(inter);
if (!PyDict_Check(iface)) {
Py_DECREF(iface);
PyErr_SetString(PyExc_ValueError,
"Invalid __array_interface__ value, must be a dict");
return NULL;
}
shape = PyDict_GetItemString(inter, "shape");
if (shape == NULL) {
Py_DECREF(inter);
PyErr_SetString(PyExc_ValueError,
"Missing __array_interface__ shape");
return NULL;
}
tstr = PyDict_GetItemString(inter, "typestr");
if (tstr == NULL) {
Py_DECREF(inter);

/* Get type string from interface specification */
attr = PyDict_GetItemString(iface, "typestr");
if (attr == NULL) {
Py_DECREF(iface);
PyErr_SetString(PyExc_ValueError,
"Missing __array_interface__ typestr");
return NULL;
}
#if defined(NPY_PY3K)
/* Allow unicode type strings */
if (PyUnicode_Check(attr)) {
tmp = PyUnicode_AsASCIIString(attr);
attr = tmp;
}
#endif
if (!PyBytes_Check(attr)) {
PyErr_SetString(PyExc_TypeError,
"__array_interface__ typestr must be a string");
goto fail;
}
/* Get dtype from type string */
dtype = _array_typedescr_fromstr(PyString_AS_STRING(attr));
#if defined(NPY_PY3K)
if (tmp == attr) {
Py_DECREF(tmp);
}
#endif
if (dtype == NULL) {
goto fail;
}

attr = PyDict_GetItemString(inter, "data");
base = input;
if ((attr == NULL) || (attr==Py_None) || (!PyTuple_Check(attr))) {
if (attr && (attr != Py_None)) {
item = attr;
/* Get shape tuple from interface specification */
attr = PyDict_GetItemString(iface, "shape");
if (attr == NULL) {
/* Shape must be specified when 'data' is specified */
if (PyDict_GetItemString(iface, "data") != NULL) {
Py_DECREF(iface);
PyErr_SetString(PyExc_ValueError,
"Missing __array_interface__ shape");
return NULL;
}
/* Assume shape as scalar otherwise */
else {
item = input;
}
res = PyObject_AsWriteBuffer(item, (void **)&data, &buffer_len);
if (res < 0) {
PyErr_Clear();
res = PyObject_AsReadBuffer(
item, (const void **)&data, &buffer_len);
if (res < 0) {
goto fail;
}
dataflags &= ~NPY_ARRAY_WRITEABLE;
/* NOTE: pointers to data and base should be NULL */
n = dims[0] = 0;
}
attr = PyDict_GetItemString(inter, "offset");
if (attr) {
npy_longlong num = PyLong_AsLongLong(attr);
if (error_converting(num)) {
PyErr_SetString(PyExc_TypeError,
"__array_interface__ offset must be an integer");
}
/* Make sure 'shape' is a tuple */
else if (!PyTuple_Check(attr)) {
PyErr_SetString(PyExc_TypeError,
"shape must be a tuple");
goto fail;
}
/* Get dimensions from shape tuple */
else {
n = PyTuple_GET_SIZE(attr);
for (i = 0; i < n; i++) {
tmp = PyTuple_GET_ITEM(attr, i);
dims[i] = PyArray_PyIntAsIntp(tmp);
if (error_converting(dims[i])) {
goto fail;
}
data += num;
}
base = item;
}
else {

/* Get data buffer from interface specification */
attr = PyDict_GetItemString(iface, "data");

/* Case for data access through pointer */
if (attr && PyTuple_Check(attr)) {
PyObject *dataptr;
if (n == 0) {

This comment has been minimized.

Copy link
@teoliphant

teoliphant Jan 9, 2013

Member

This check seems un-necessary and it causes at least one failure in code that uses np.ndindex(). The array interface should support passing array scalars around.

This comment has been minimized.

Copy link
@certik

certik Jan 10, 2013

Author Contributor

Ok, this is now tracked in #2895 and I'll backport the fix to 1.7 as well.

PyErr_SetString(PyExc_ValueError,
"__array_interface__ shape must be at least size 1");
goto fail;
}
if (PyTuple_GET_SIZE(attr) != 2) {
PyErr_SetString(PyExc_TypeError,
"__array_interface__ data must be a 2-tuple with "
Expand Down Expand Up @@ -2083,90 +2112,102 @@ PyArray_FromInterface(PyObject *input)
if (PyObject_IsTrue(PyTuple_GET_ITEM(attr,1))) {
dataflags &= ~NPY_ARRAY_WRITEABLE;
}
base = origin;
}
attr = tstr;
#if defined(NPY_PY3K)
if (PyUnicode_Check(tstr)) {
/* Allow unicode type strings */
attr = PyUnicode_AsASCIIString(tstr);
}
#endif
if (!PyBytes_Check(attr)) {
PyErr_SetString(PyExc_TypeError,
"__array_interface__ typestr must be a string");
goto fail;
}
type = _array_typedescr_fromstr(PyString_AS_STRING(attr));
#if defined(NPY_PY3K)
if (attr != tstr) {
Py_DECREF(attr);
}
#endif
if (type == NULL) {
goto fail;
}
attr = shape;
if (!PyTuple_Check(attr)) {
PyErr_SetString(PyExc_TypeError,
"shape must be a tuple");
Py_DECREF(type);
goto fail;
}
n = PyTuple_GET_SIZE(attr);
for (i = 0; i < n; i++) {
item = PyTuple_GET_ITEM(attr, i);
dims[i] = PyArray_PyIntAsIntp(item);
if (error_converting(dims[i])) {
break;

/* Case for data access through buffer */
else if (attr) {
if (n == 0) {
PyErr_SetString(PyExc_ValueError,
"__array_interface__ shape must be at least size 1");
goto fail;
}
if (attr && (attr != Py_None)) {
base = attr;
}
else {
base = origin;
}
res = PyObject_AsWriteBuffer(base, (void **)&data, &buffer_len);
if (res < 0) {
PyErr_Clear();
res = PyObject_AsReadBuffer(
base, (const void **)&data, &buffer_len);
if (res < 0) {
goto fail;
}
dataflags &= ~NPY_ARRAY_WRITEABLE;
}
/* Get offset number from interface specification */
attr = PyDict_GetItemString(origin, "offset");
if (attr) {
npy_longlong num = PyLong_AsLongLong(attr);
if (error_converting(num)) {
PyErr_SetString(PyExc_TypeError,
"__array_interface__ offset must be an integer");
goto fail;
}
data += num;
}
}

ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, type,
ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, dtype,
n, dims,
NULL, data,
dataflags, NULL);
if (ret == NULL) {
return NULL;
goto fail;
}
Py_INCREF(base);
if (PyArray_SetBaseObject(ret, base) < 0) {
Py_DECREF(ret);
return NULL;
if (data == NULL) {
if (PyArray_SIZE(ret) > 1) {
PyErr_SetString(PyExc_ValueError,
"cannot coerce scalar to array with size > 1");
Py_DECREF(ret);
goto fail;
}
if (PyArray_SETITEM(ret, PyArray_DATA(ret), origin) < 0) {
Py_DECREF(ret);
goto fail;
}
}

attr = PyDict_GetItemString(inter, "strides");
if (base) {
Py_INCREF(base);
if (PyArray_SetBaseObject(ret, base) < 0) {
Py_DECREF(ret);
goto fail;
}
}
attr = PyDict_GetItemString(iface, "strides");
if (attr != NULL && attr != Py_None) {
if (!PyTuple_Check(attr)) {
PyErr_SetString(PyExc_TypeError,
"strides must be a tuple");
Py_DECREF(ret);
return NULL;
goto fail;
}
if (n != PyTuple_GET_SIZE(attr)) {
PyErr_SetString(PyExc_ValueError,
"mismatch in length of strides and shape");
Py_DECREF(ret);
return NULL;
goto fail;
}
for (i = 0; i < n; i++) {
item = PyTuple_GET_ITEM(attr, i);
strides[i] = PyArray_PyIntAsIntp(item);
tmp = PyTuple_GET_ITEM(attr, i);
strides[i] = PyArray_PyIntAsIntp(tmp);
if (error_converting(strides[i])) {
break;
Py_DECREF(ret);
goto fail;
}
}
if (PyErr_Occurred()) {
PyErr_Clear();
}
memcpy(PyArray_STRIDES(ret), strides, n*sizeof(npy_intp));
}
else PyErr_Clear();
PyArray_UpdateFlags(ret, NPY_ARRAY_UPDATE_ALL);
Py_DECREF(inter);
Py_DECREF(iface);
return (PyObject *)ret;

fail:
Py_XDECREF(inter);
Py_XDECREF(dtype);
Py_XDECREF(iface);
return NULL;
}

Expand Down
25 changes: 25 additions & 0 deletions numpy/core/tests/test_multiarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -2809,6 +2809,31 @@ def test_multiarray_flags_not_writable_attribute_deletion(self):
for s in attr:
assert_raises(AttributeError, delattr, a, s)

def test_array_interface():
# Test scalar coercion within the array interface
class Foo(object):
def __init__(self, value):
self.value = value
self.iface = {'typestr' : '=f8'}
def __float__(self):
return float(self.value)
@property
def __array_interface__(self):
return self.iface
f = Foo(0.5)
assert_equal(np.array(f), 0.5)
assert_equal(np.array([f]), [0.5])
assert_equal(np.array([f, f]), [0.5, 0.5])
assert_equal(np.array(f).dtype, np.dtype('=f8'))
# Test various shape definitions
f.iface['shape'] = ()
assert_equal(np.array(f), 0.5)
f.iface['shape'] = None
assert_raises(TypeError, np.array, f)
f.iface['shape'] = (1,1)
assert_equal(np.array(f), [[0.5]])
f.iface['shape'] = (2,)
assert_raises(ValueError, np.array, f)

def test_flat_element_deletion():
it = np.ones(3).flat
Expand Down

0 comments on commit 27a9c36

Please sign in to comment.