blob: 0471a2a3e3ad784bcacb4e8bfe24fcf5a895a87e [file] [log] [blame]
/* Array Descr Object */
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "structmember.h"
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#define _MULTIARRAYMODULE
#include "numpy/arrayobject.h"
#include "numpy/arrayscalars.h"
#include "npy_config.h"
#include "npy_ctypes.h"
#include "npy_pycompat.h"
#include "_datetime.h"
#include "common.h"
#include "templ_common.h" /* for npy_mul_with_overflow_intp */
#include "descriptor.h"
#include "alloc.h"
#include "assert.h"
#include "buffer.h"
/*
* offset: A starting offset.
* alignment: A power-of-two alignment.
*
* This macro returns the smallest value >= 'offset'
* that is divisible by 'alignment'. Because 'alignment'
* is a power of two and integers are twos-complement,
* it is possible to use some simple bit-fiddling to do this.
*/
#define NPY_NEXT_ALIGNED_OFFSET(offset, alignment) \
(((offset) + (alignment) - 1) & (-(alignment)))
#ifndef PyDictProxy_Check
#define PyDictProxy_Check(obj) (Py_TYPE(obj) == &PyDictProxy_Type)
#endif
static PyObject *typeDict = NULL; /* Must be explicitly loaded */
static PyArray_Descr *
_use_inherit(PyArray_Descr *type, PyObject *newobj, int *errflag);
/*
* Returns value of PyMapping_GetItemString but as a borrowed reference instead
* of a new reference.
*/
static PyObject *
Borrowed_PyMapping_GetItemString(PyObject *o, char *key)
{
PyObject *ret = PyMapping_GetItemString(o, key);
Py_XDECREF(ret);
return ret;
}
static PyArray_Descr *
_arraydescr_from_ctypes_type(PyTypeObject *type)
{
PyObject *_numpy_dtype_ctypes;
PyObject *res;
/* Call the python function of the same name. */
_numpy_dtype_ctypes = PyImport_ImportModule("numpy.core._dtype_ctypes");
if (_numpy_dtype_ctypes == NULL) {
return NULL;
}
res = PyObject_CallMethod(_numpy_dtype_ctypes, "dtype_from_ctypes_type", "O", (PyObject *)type);
Py_DECREF(_numpy_dtype_ctypes);
if (res == NULL) {
return NULL;
}
/*
* sanity check that dtype_from_ctypes_type returned the right type,
* since getting it wrong would give segfaults.
*/
if (!PyObject_TypeCheck(res, &PyArrayDescr_Type)) {
Py_DECREF(res);
PyErr_BadInternalCall();
return NULL;
}
return (PyArray_Descr *)res;
}
/*
* This function creates a dtype object when the object has a "dtype" attribute,
* and it can be converted to a dtype object.
*
* Returns a new reference to a dtype object, or NULL
* if this is not possible. When it returns NULL, it does
* not set a Python exception.
*/
NPY_NO_EXPORT PyArray_Descr *
_arraydescr_from_dtype_attr(PyObject *obj)
{
PyObject *dtypedescr;
PyArray_Descr *newdescr = NULL;
int ret;
/* For arbitrary objects that have a "dtype" attribute */
dtypedescr = PyObject_GetAttrString(obj, "dtype");
PyErr_Clear();
if (dtypedescr == NULL) {
return NULL;
}
ret = PyArray_DescrConverter(dtypedescr, &newdescr);
Py_DECREF(dtypedescr);
if (ret != NPY_SUCCEED) {
PyErr_Clear();
return NULL;
}
return newdescr;
}
/*
* Sets the global typeDict object, which is a dictionary mapping
* dtype names to numpy scalar types.
*/
NPY_NO_EXPORT PyObject *
array_set_typeDict(PyObject *NPY_UNUSED(ignored), PyObject *args)
{
PyObject *dict;
if (!PyArg_ParseTuple(args, "O:set_typeDict", &dict)) {
return NULL;
}
/* Decrement old reference (if any)*/
Py_XDECREF(typeDict);
typeDict = dict;
/* Create an internal reference to it */
Py_INCREF(dict);
Py_RETURN_NONE;
}
#define _chk_byteorder(arg) (arg == '>' || arg == '<' || \
arg == '|' || arg == '=')
static int
_check_for_commastring(char *type, Py_ssize_t len)
{
Py_ssize_t i;
int sqbracket;
/* Check for ints at start of string */
if ((type[0] >= '0'
&& type[0] <= '9')
|| ((len > 1)
&& _chk_byteorder(type[0])
&& (type[1] >= '0'
&& type[1] <= '9'))) {
return 1;
}
/* Check for empty tuple */
if (((len > 1)
&& (type[0] == '('
&& type[1] == ')'))
|| ((len > 3)
&& _chk_byteorder(type[0])
&& (type[1] == '('
&& type[2] == ')'))) {
return 1;
}
/*
* Check for presence of commas outside square [] brackets. This
* allows commas inside of [], for parameterized dtypes to use.
*/
sqbracket = 0;
for (i = 0; i < len; i++) {
switch (type[i]) {
case ',':
if (sqbracket == 0) {
return 1;
}
break;
case '[':
++sqbracket;
break;
case ']':
--sqbracket;
break;
}
}
return 0;
}
#undef _chk_byteorder
static int
is_datetime_typestr(char *type, Py_ssize_t len)
{
if (len < 2) {
return 0;
}
if (type[1] == '8' && (type[0] == 'M' || type[0] == 'm')) {
return 1;
}
if (len < 10) {
return 0;
}
if (strncmp(type, "datetime64", 10) == 0) {
return 1;
}
if (len < 11) {
return 0;
}
if (strncmp(type, "timedelta64", 11) == 0) {
return 1;
}
return 0;
}
static PyArray_Descr *
_convert_from_tuple(PyObject *obj, int align)
{
PyArray_Descr *type, *res;
PyObject *val;
int errflag;
if (PyTuple_GET_SIZE(obj) != 2) {
return NULL;
}
if (align) {
if (!PyArray_DescrAlignConverter(PyTuple_GET_ITEM(obj, 0), &type)) {
return NULL;
}
}
else {
if (!PyArray_DescrConverter(PyTuple_GET_ITEM(obj, 0), &type)) {
return NULL;
}
}
val = PyTuple_GET_ITEM(obj,1);
/* try to interpret next item as a type */
res = _use_inherit(type, val, &errflag);
if (res || errflag) {
Py_DECREF(type);
return res;
}
PyErr_Clear();
/*
* We get here if res was NULL but errflag wasn't set
* --- i.e. the conversion to a data-descr failed in _use_inherit
*/
if (PyDataType_ISUNSIZED(type)) {
/* interpret next item as a typesize */
int itemsize = PyArray_PyIntAsInt(PyTuple_GET_ITEM(obj,1));
if (error_converting(itemsize)) {
PyErr_SetString(PyExc_ValueError,
"invalid itemsize in generic type tuple");
Py_DECREF(type);
return NULL;
}
PyArray_DESCR_REPLACE(type);
if (type == NULL) {
return NULL;
}
if (type->type_num == NPY_UNICODE) {
type->elsize = itemsize << 2;
}
else {
type->elsize = itemsize;
}
return type;
}
else if (type->metadata && (PyDict_Check(val) || PyDictProxy_Check(val))) {
/* Assume it's a metadata dictionary */
if (PyDict_Merge(type->metadata, val, 0) == -1) {
Py_DECREF(type);
return NULL;
}
return type;
}
else {
/*
* interpret next item as shape (if it's a tuple)
* and reset the type to NPY_VOID with
* a new fields attribute.
*/
PyArray_Dims shape = {NULL, -1};
PyArray_Descr *newdescr = NULL;
npy_intp items;
int i, overflowed;
int nbytes;
if (!(PyArray_IntpConverter(val, &shape)) || (shape.len > NPY_MAXDIMS)) {
PyErr_SetString(PyExc_ValueError,
"invalid shape in fixed-type tuple.");
goto fail;
}
/*
* If (type, 1) was given, it is equivalent to type...
* or (type, ()) was given it is equivalent to type...
*/
if ((shape.len == 1
&& shape.ptr[0] == 1
&& PyNumber_Check(val))
|| (shape.len == 0
&& PyTuple_Check(val))) {
npy_free_cache_dim_obj(shape);
return type;
}
/* validate and set shape */
for (i=0; i < shape.len; i++) {
if (shape.ptr[i] < 0) {
PyErr_SetString(PyExc_ValueError,
"invalid shape in fixed-type tuple: "
"dimension smaller then zero.");
goto fail;
}
if (shape.ptr[i] > NPY_MAX_INT) {
PyErr_SetString(PyExc_ValueError,
"invalid shape in fixed-type tuple: "
"dimension does not fit into a C int.");
goto fail;
}
}
items = PyArray_OverflowMultiplyList(shape.ptr, shape.len);
if (items < 0 || items > NPY_MAX_INT) {
overflowed = 1;
}
else {
overflowed = npy_mul_with_overflow_int(
&nbytes, type->elsize, (int) items);
}
if (overflowed) {
PyErr_SetString(PyExc_ValueError,
"invalid shape in fixed-type tuple: dtype size in "
"bytes must fit into a C int.");
goto fail;
}
newdescr = PyArray_DescrNewFromType(NPY_VOID);
if (newdescr == NULL) {
goto fail;
}
newdescr->elsize = nbytes;
newdescr->subarray = PyArray_malloc(sizeof(PyArray_ArrayDescr));
if (newdescr->subarray == NULL) {
PyErr_NoMemory();
goto fail;
}
newdescr->flags = type->flags;
newdescr->alignment = type->alignment;
newdescr->subarray->base = type;
type = NULL;
Py_XDECREF(newdescr->fields);
Py_XDECREF(newdescr->names);
newdescr->fields = NULL;
newdescr->names = NULL;
/*
* Create a new subarray->shape tuple (it can be an arbitrary
* sequence of integer like objects, neither of which is safe.
*/
newdescr->subarray->shape = PyTuple_New(shape.len);
if (newdescr->subarray->shape == NULL) {
goto fail;
}
for (i=0; i < shape.len; i++) {
PyTuple_SET_ITEM(newdescr->subarray->shape, i,
PyInt_FromLong((long)shape.ptr[i]));
if (PyTuple_GET_ITEM(newdescr->subarray->shape, i) == NULL) {
goto fail;
}
}
npy_free_cache_dim_obj(shape);
return newdescr;
fail:
Py_XDECREF(type);
Py_XDECREF(newdescr);
npy_free_cache_dim_obj(shape);
return NULL;
}
}
/*
* obj is a list. Each item is a tuple with
*
* (field-name, data-type (either a list or a string), and an optional
* shape parameter).
*
* field-name can be a string or a 2-tuple
* data-type can now be a list, string, or 2-tuple
* (string, metadata dictionary)
*/
static PyArray_Descr *
_convert_from_array_descr(PyObject *obj, int align)
{
int n, i, totalsize;
int ret;
PyObject *fields, *item, *newobj;
PyObject *name, *tup, *title;
PyObject *nameslist;
PyArray_Descr *new;
PyArray_Descr *conv;
/* Types with fields need the Python C API for field access */
char dtypeflags = NPY_NEEDS_PYAPI;
int maxalign = 0;
n = PyList_GET_SIZE(obj);
nameslist = PyTuple_New(n);
if (!nameslist) {
return NULL;
}
totalsize = 0;
fields = PyDict_New();
for (i = 0; i < n; i++) {
item = PyList_GET_ITEM(obj, i);
if (!PyTuple_Check(item) || (PyTuple_GET_SIZE(item) < 2)) {
goto fail;
}
name = PyTuple_GET_ITEM(item, 0);
if (PyBaseString_Check(name)) {
title = NULL;
}
else if (PyTuple_Check(name)) {
if (PyTuple_GET_SIZE(name) != 2) {
goto fail;
}
title = PyTuple_GET_ITEM(name, 0);
name = PyTuple_GET_ITEM(name, 1);
if (!PyBaseString_Check(name)) {
goto fail;
}
}
else {
goto fail;
}
/* Insert name into nameslist */
Py_INCREF(name);
#if !defined(NPY_PY3K)
/* convert unicode name to ascii on Python 2 if possible */
if (PyUnicode_Check(name)) {
PyObject *tmp = PyUnicode_AsASCIIString(name);
Py_DECREF(name);
if (tmp == NULL) {
goto fail;
}
name = tmp;
}
#endif
if (PyUString_GET_SIZE(name) == 0) {
Py_DECREF(name);
if (title == NULL) {
name = PyUString_FromFormat("f%d", i);
}
#if defined(NPY_PY3K)
/* On Py3, allow only non-empty Unicode strings as field names */
else if (PyUString_Check(title) && PyUString_GET_SIZE(title) > 0) {
name = title;
Py_INCREF(name);
}
else {
goto fail;
}
#else
else {
name = title;
Py_INCREF(name);
}
#endif
}
PyTuple_SET_ITEM(nameslist, i, name);
/* Process rest */
if (PyTuple_GET_SIZE(item) == 2) {
if (align) {
ret = PyArray_DescrAlignConverter(PyTuple_GET_ITEM(item, 1),
&conv);
}
else {
ret = PyArray_DescrConverter(PyTuple_GET_ITEM(item, 1), &conv);
}
if (ret == NPY_FAIL) {
PyObject_Print(PyTuple_GET_ITEM(item, 1), stderr, 0);
}
}
else if (PyTuple_GET_SIZE(item) == 3) {
newobj = PyTuple_GetSlice(item, 1, 3);
if (align) {
ret = PyArray_DescrAlignConverter(newobj, &conv);
}
else {
ret = PyArray_DescrConverter(newobj, &conv);
}
Py_DECREF(newobj);
}
else {
goto fail;
}
if (ret == NPY_FAIL) {
goto fail;
}
if ((PyDict_GetItem(fields, name) != NULL)
|| (title
&& PyBaseString_Check(title)
&& (PyDict_GetItem(fields, title) != NULL))) {
#if defined(NPY_PY3K)
name = PyUnicode_AsUTF8String(name);
#endif
PyErr_Format(PyExc_ValueError,
"field '%s' occurs more than once", PyString_AsString(name));
#if defined(NPY_PY3K)
Py_DECREF(name);
#endif
Py_DECREF(conv);
goto fail;
}
dtypeflags |= (conv->flags & NPY_FROM_FIELDS);
if (align) {
int _align;
_align = conv->alignment;
if (_align > 1) {
totalsize = NPY_NEXT_ALIGNED_OFFSET(totalsize, _align);
}
maxalign = PyArray_MAX(maxalign, _align);
}
tup = PyTuple_New((title == NULL ? 2 : 3));
PyTuple_SET_ITEM(tup, 0, (PyObject *)conv);
PyTuple_SET_ITEM(tup, 1, PyInt_FromLong((long) totalsize));
/*
* Title can be "meta-data". Only insert it
* into the fields dictionary if it is a string
* and if it is not the same as the name.
*/
if (title != NULL) {
Py_INCREF(title);
PyTuple_SET_ITEM(tup, 2, title);
PyDict_SetItem(fields, name, tup);
if (PyBaseString_Check(title)) {
if (PyDict_GetItem(fields, title) != NULL) {
PyErr_SetString(PyExc_ValueError,
"title already used as a name or title.");
Py_DECREF(tup);
goto fail;
}
PyDict_SetItem(fields, title, tup);
}
}
else {
PyDict_SetItem(fields, name, tup);
}
totalsize += conv->elsize;
Py_DECREF(tup);
}
if (maxalign > 1) {
totalsize = NPY_NEXT_ALIGNED_OFFSET(totalsize, maxalign);
}
new = PyArray_DescrNewFromType(NPY_VOID);
if (new == NULL) {
Py_XDECREF(fields);
Py_XDECREF(nameslist);
return NULL;
}
new->fields = fields;
new->names = nameslist;
new->elsize = totalsize;
new->flags = dtypeflags;
/* Structured arrays get a sticky aligned bit */
if (align) {
new->flags |= NPY_ALIGNED_STRUCT;
new->alignment = maxalign;
}
return new;
fail:
Py_DECREF(fields);
Py_DECREF(nameslist);
return NULL;
}
/*
* a list specifying a data-type can just be
* a list of formats. The names for the fields
* will default to f0, f1, f2, and so forth.
*/
static PyArray_Descr *
_convert_from_list(PyObject *obj, int align)
{
int n, i;
int totalsize;
PyObject *fields;
PyArray_Descr *conv = NULL;
PyArray_Descr *new;
PyObject *key, *tup;
PyObject *nameslist = NULL;
int ret;
int maxalign = 0;
/* Types with fields need the Python C API for field access */
char dtypeflags = NPY_NEEDS_PYAPI;
n = PyList_GET_SIZE(obj);
/*
* Ignore any empty string at end which _internal._commastring
* can produce
*/
key = PyList_GET_ITEM(obj, n-1);
if (PyBytes_Check(key) && PyBytes_GET_SIZE(key) == 0) {
n = n - 1;
}
/* End ignore code.*/
totalsize = 0;
if (n == 0) {
return NULL;
}
nameslist = PyTuple_New(n);
if (!nameslist) {
return NULL;
}
fields = PyDict_New();
for (i = 0; i < n; i++) {
tup = PyTuple_New(2);
key = PyUString_FromFormat("f%d", i);
if (align) {
ret = PyArray_DescrAlignConverter(PyList_GET_ITEM(obj, i), &conv);
}
else {
ret = PyArray_DescrConverter(PyList_GET_ITEM(obj, i), &conv);
}
if (ret == NPY_FAIL) {
Py_DECREF(tup);
Py_DECREF(key);
goto fail;
}
dtypeflags |= (conv->flags & NPY_FROM_FIELDS);
PyTuple_SET_ITEM(tup, 0, (PyObject *)conv);
if (align) {
int _align;
_align = conv->alignment;
if (_align > 1) {
totalsize = NPY_NEXT_ALIGNED_OFFSET(totalsize, _align);
}
maxalign = PyArray_MAX(maxalign, _align);
}
PyTuple_SET_ITEM(tup, 1, PyInt_FromLong((long) totalsize));
PyDict_SetItem(fields, key, tup);
Py_DECREF(tup);
PyTuple_SET_ITEM(nameslist, i, key);
totalsize += conv->elsize;
}
new = PyArray_DescrNewFromType(NPY_VOID);
new->fields = fields;
new->names = nameslist;
new->flags = dtypeflags;
if (maxalign > 1) {
totalsize = NPY_NEXT_ALIGNED_OFFSET(totalsize, maxalign);
}
/* Structured arrays get a sticky aligned bit */
if (align) {
new->flags |= NPY_ALIGNED_STRUCT;
new->alignment = maxalign;
}
new->elsize = totalsize;
return new;
fail:
Py_DECREF(nameslist);
Py_DECREF(fields);
return NULL;
}
/*
* comma-separated string
* this is the format developed by the numarray records module and implemented
* by the format parser in that module this is an alternative implementation
* found in the _internal.py file patterned after that one -- the approach is
* to try to convert to a list (with tuples if any repeat information is
* present) and then call the _convert_from_list)
*
* TODO: Calling Python from C like this in critical-path code is not
* a good idea. This should all be converted to C code.
*/
static PyArray_Descr *
_convert_from_commastring(PyObject *obj, int align)
{
PyObject *listobj;
PyArray_Descr *res;
PyObject *_numpy_internal;
if (!PyBytes_Check(obj)) {
return NULL;
}
_numpy_internal = PyImport_ImportModule("numpy.core._internal");
if (_numpy_internal == NULL) {
return NULL;
}
listobj = PyObject_CallMethod(_numpy_internal, "_commastring", "O", obj);
Py_DECREF(_numpy_internal);
if (listobj == NULL) {
return NULL;
}
if (!PyList_Check(listobj) || PyList_GET_SIZE(listobj) < 1) {
PyErr_SetString(PyExc_RuntimeError,
"_commastring is not returning a list with len >= 1");
Py_DECREF(listobj);
return NULL;
}
if (PyList_GET_SIZE(listobj) == 1) {
int retcode;
retcode = PyArray_DescrConverter(PyList_GET_ITEM(listobj, 0),
&res);
if (retcode == NPY_FAIL) {
res = NULL;
}
}
else {
res = _convert_from_list(listobj, align);
}
Py_DECREF(listobj);
if (!res && !PyErr_Occurred()) {
PyErr_SetString(PyExc_ValueError,
"invalid data-type");
return NULL;
}
return res;
}
static int
_is_tuple_of_integers(PyObject *obj)
{
int i;
if (!PyTuple_Check(obj)) {
return 0;
}
for (i = 0; i < PyTuple_GET_SIZE(obj); i++) {
if (!PyArray_IsIntegerScalar(PyTuple_GET_ITEM(obj, i))) {
return 0;
}
}
return 1;
}
/*
* helper function for _use_inherit to disallow dtypes of the form
* (old_dtype, new_dtype) where either of the dtypes contains python
* objects - these dtypes are not useful and can be a source of segfaults,
* when an attempt is made to interpret a python object as a different dtype
* or vice versa
* an exception is made for dtypes of the form ('O', [('name', 'O')]), which
* people have been using to add a field to an object array without fields
*/
static int
invalid_union_object_dtype(PyArray_Descr *new, PyArray_Descr *conv)
{
PyObject *name, *tup;
PyArray_Descr *dtype;
if (!PyDataType_REFCHK(new) && !PyDataType_REFCHK(conv)) {
return 0;
}
if (PyDataType_HASFIELDS(new) || new->kind != 'O') {
goto fail;
}
if (!PyDataType_HASFIELDS(conv) || PyTuple_GET_SIZE(conv->names) != 1) {
goto fail;
}
name = PyTuple_GET_ITEM(conv->names, 0);
if (name == NULL) {
return -1;
}
tup = PyDict_GetItem(conv->fields, name);
if (tup == NULL) {
return -1;
}
dtype = (PyArray_Descr *)PyTuple_GET_ITEM(tup, 0);
if (dtype == NULL) {
return -1;
}
if (dtype->kind != 'O') {
goto fail;
}
return 0;
fail:
PyErr_SetString(PyExc_ValueError,
"dtypes of the form (old_dtype, new_dtype) containing the object "
"dtype are not supported");
return -1;
}
/*
* A tuple type would be either (generic typeobject, typesize)
* or (fixed-length data-type, shape)
*
* or (inheriting data-type, new-data-type)
* The new data-type must have the same itemsize as the inheriting data-type
* unless the latter is 0
*
* Thus (int32, {'real':(int16,0),'imag',(int16,2)})
*
* is one way to specify a descriptor that will give
* a['real'] and a['imag'] to an int32 array.
*
* leave type reference alone
*/
static PyArray_Descr *
_use_inherit(PyArray_Descr *type, PyObject *newobj, int *errflag)
{
PyArray_Descr *new;
PyArray_Descr *conv;
*errflag = 0;
if (PyArray_IsScalar(newobj, Integer)
|| _is_tuple_of_integers(newobj)
|| !PyArray_DescrConverter(newobj, &conv)) {
return NULL;
}
*errflag = 1;
new = PyArray_DescrNew(type);
if (new == NULL) {
goto fail;
}
if (PyDataType_ISUNSIZED(new)) {
new->elsize = conv->elsize;
}
else if (new->elsize != conv->elsize) {
PyErr_SetString(PyExc_ValueError,
"mismatch in size of old and new data-descriptor");
Py_DECREF(new);
goto fail;
}
else if (invalid_union_object_dtype(new, conv)) {
Py_DECREF(new);
goto fail;
}
if (PyDataType_HASFIELDS(conv)) {
Py_XDECREF(new->fields);
new->fields = conv->fields;
Py_XINCREF(new->fields);
Py_XDECREF(new->names);
new->names = conv->names;
Py_XINCREF(new->names);
}
if (conv->metadata != NULL) {
Py_XDECREF(new->metadata);
new->metadata = conv->metadata;
Py_XINCREF(new->metadata);
}
new->flags = conv->flags;
Py_DECREF(conv);
*errflag = 0;
return new;
fail:
Py_DECREF(conv);
return NULL;
}
/*
* Validates that any field of the structured array 'dtype' which has
* the NPY_ITEM_HASOBJECT flag set does not overlap with another field.
*
* This algorithm is worst case O(n^2). It could be done with a sort
* and sweep algorithm, but the structured dtype representation is
* rather ugly right now, so writing something better can wait until
* that representation is made sane.
*
* Returns 0 on success, -1 if an exception is raised.
*/
static int
validate_object_field_overlap(PyArray_Descr *dtype)
{
PyObject *names, *fields, *key, *tup, *title;
Py_ssize_t i, j, names_size;
PyArray_Descr *fld_dtype, *fld2_dtype;
int fld_offset, fld2_offset;
/* Get some properties from the dtype */
names = dtype->names;
names_size = PyTuple_GET_SIZE(names);
fields = dtype->fields;
for (i = 0; i < names_size; ++i) {
key = PyTuple_GET_ITEM(names, i);
if (key == NULL) {
return -1;
}
tup = PyDict_GetItem(fields, key);
if (tup == NULL) {
return -1;
}
if (!PyArg_ParseTuple(tup, "Oi|O", &fld_dtype, &fld_offset, &title)) {
return -1;
}
/* If this field has objects, check for overlaps */
if (PyDataType_REFCHK(fld_dtype)) {
for (j = 0; j < names_size; ++j) {
if (i != j) {
key = PyTuple_GET_ITEM(names, j);
if (key == NULL) {
return -1;
}
tup = PyDict_GetItem(fields, key);
if (tup == NULL) {
return -1;
}
if (!PyArg_ParseTuple(tup, "Oi|O", &fld2_dtype,
&fld2_offset, &title)) {
return -1;
}
/* Raise an exception if it overlaps */
if (fld_offset < fld2_offset + fld2_dtype->elsize &&
fld2_offset < fld_offset + fld_dtype->elsize) {
PyErr_SetString(PyExc_TypeError,
"Cannot create a NumPy dtype with overlapping "
"object fields");
return -1;
}
}
}
}
}
/* It passed all the overlap tests */
return 0;
}
/*
* a dictionary specifying a data-type
* must have at least two and up to four
* keys These must all be sequences of the same length.
*
* can also have an additional key called "metadata" which can be any dictionary
*
* "names" --- field names
* "formats" --- the data-type descriptors for the field.
*
* Optional:
*
* "offsets" --- integers indicating the offset into the
* record of the start of the field.
* if not given, then "consecutive offsets"
* will be assumed and placed in the dictionary.
*
* "titles" --- Allows the use of an additional key
* for the fields dictionary.(if these are strings
* or unicode objects) or
* this can also be meta-data to
* be passed around with the field description.
*
* Attribute-lookup-based field names merely has to query the fields
* dictionary of the data-descriptor. Any result present can be used
* to return the correct field.
*
* So, the notion of what is a name and what is a title is really quite
* arbitrary.
*
* What does distinguish a title, however, is that if it is not None,
* it will be placed at the end of the tuple inserted into the
* fields dictionary.and can therefore be used to carry meta-data around.
*
* If the dictionary does not have "names" and "formats" entries,
* then it will be checked for conformity and used directly.
*/
static PyArray_Descr *
_use_fields_dict(PyObject *obj, int align)
{
PyObject *_numpy_internal;
PyArray_Descr *res;
_numpy_internal = PyImport_ImportModule("numpy.core._internal");
if (_numpy_internal == NULL) {
return NULL;
}
res = (PyArray_Descr *)PyObject_CallMethod(_numpy_internal,
"_usefields", "Oi", obj, align);
Py_DECREF(_numpy_internal);
return res;
}
/*
* Creates a struct dtype object from a Python dictionary.
*/
static PyArray_Descr *
_convert_from_dict(PyObject *obj, int align)
{
PyArray_Descr *new;
PyObject *fields = NULL;
PyObject *names, *offsets, *descrs, *titles, *tmp;
PyObject *metadata;
int n, i;
int totalsize, itemsize;
int maxalign = 0;
/* Types with fields need the Python C API for field access */
char dtypeflags = NPY_NEEDS_PYAPI;
int has_out_of_order_fields = 0;
fields = PyDict_New();
if (fields == NULL) {
return (PyArray_Descr *)PyErr_NoMemory();
}
/*
* Use PyMapping_GetItemString to support dictproxy objects as well.
*/
names = Borrowed_PyMapping_GetItemString(obj, "names");
descrs = Borrowed_PyMapping_GetItemString(obj, "formats");
if (!names || !descrs) {
Py_DECREF(fields);
PyErr_Clear();
return _use_fields_dict(obj, align);
}
n = PyObject_Length(names);
offsets = Borrowed_PyMapping_GetItemString(obj, "offsets");
if (!offsets) {
PyErr_Clear();
}
titles = Borrowed_PyMapping_GetItemString(obj, "titles");
if (!titles) {
PyErr_Clear();
}
if ((n > PyObject_Length(descrs))
|| (offsets && (n > PyObject_Length(offsets)))
|| (titles && (n > PyObject_Length(titles)))) {
PyErr_SetString(PyExc_ValueError,
"'names', 'formats', 'offsets', and 'titles' dict "
"entries must have the same length");
goto fail;
}
/*
* If a property 'aligned' is in the dict, it overrides the align flag
* to be True if it not already true.
*/
tmp = Borrowed_PyMapping_GetItemString(obj, "aligned");
if (tmp == NULL) {
PyErr_Clear();
} else {
if (tmp == Py_True) {
align = 1;
}
else if (tmp != Py_False) {
PyErr_SetString(PyExc_ValueError,
"NumPy dtype descriptor includes 'aligned' entry, "
"but its value is neither True nor False");
return NULL;
}
}
totalsize = 0;
for (i = 0; i < n; i++) {
PyObject *tup, *descr, *ind, *title, *name, *off;
int len, ret, _align = 1;
PyArray_Descr *newdescr;
/* Build item to insert (descr, offset, [title])*/
len = 2;
title = NULL;
ind = PyInt_FromLong(i);
if (titles) {
title=PyObject_GetItem(titles, ind);
if (title && title != Py_None) {
len = 3;
}
else {
Py_XDECREF(title);
}
PyErr_Clear();
}
tup = PyTuple_New(len);
descr = PyObject_GetItem(descrs, ind);
if (!descr) {
Py_DECREF(tup);
Py_DECREF(ind);
goto fail;
}
if (align) {
ret = PyArray_DescrAlignConverter(descr, &newdescr);
}
else {
ret = PyArray_DescrConverter(descr, &newdescr);
}
Py_DECREF(descr);
if (ret == NPY_FAIL) {
Py_DECREF(tup);
Py_DECREF(ind);
goto fail;
}
PyTuple_SET_ITEM(tup, 0, (PyObject *)newdescr);
if (align) {
_align = newdescr->alignment;
maxalign = PyArray_MAX(maxalign,_align);
}
if (offsets) {
long offset;
off = PyObject_GetItem(offsets, ind);
if (!off) {
Py_DECREF(tup);
Py_DECREF(ind);
goto fail;
}
offset = PyArray_PyIntAsInt(off);
if (error_converting(offset)) {
Py_DECREF(off);
Py_DECREF(tup);
Py_DECREF(ind);
goto fail;
}
Py_DECREF(off);
if (offset < 0) {
PyErr_Format(PyExc_ValueError, "offset %d cannot be negative",
(int)offset);
Py_DECREF(tup);
Py_DECREF(ind);
goto fail;
}
PyTuple_SET_ITEM(tup, 1, PyInt_FromLong(offset));
/* Flag whether the fields are specified out of order */
if (offset < totalsize) {
has_out_of_order_fields = 1;
}
/* If align=True, enforce field alignment */
if (align && offset % newdescr->alignment != 0) {
PyErr_Format(PyExc_ValueError,
"offset %d for NumPy dtype with fields is "
"not divisible by the field alignment %d "
"with align=True",
(int)offset, (int)newdescr->alignment);
ret = NPY_FAIL;
}
else if (offset + newdescr->elsize > totalsize) {
totalsize = offset + newdescr->elsize;
}
}
else {
if (align && _align > 1) {
totalsize = NPY_NEXT_ALIGNED_OFFSET(totalsize, _align);
}
PyTuple_SET_ITEM(tup, 1, PyInt_FromLong(totalsize));
totalsize += newdescr->elsize;
}
if (ret == NPY_FAIL) {
Py_DECREF(ind);
Py_DECREF(tup);
goto fail;
}
if (len == 3) {
PyTuple_SET_ITEM(tup, 2, title);
}
name = PyObject_GetItem(names, ind);
Py_DECREF(ind);
if (!name) {
Py_DECREF(tup);
goto fail;
}
if (!PyBaseString_Check(name)) {
PyErr_SetString(PyExc_ValueError,
"field names must be strings");
Py_DECREF(tup);
goto fail;
}
/* Insert into dictionary */
if (PyDict_GetItem(fields, name) != NULL) {
PyErr_SetString(PyExc_ValueError,
"name already used as a name or title");
Py_DECREF(tup);
goto fail;
}
PyDict_SetItem(fields, name, tup);
Py_DECREF(name);
if (len == 3) {
if (PyBaseString_Check(title)) {
if (PyDict_GetItem(fields, title) != NULL) {
PyErr_SetString(PyExc_ValueError,
"title already used as a name or title.");
Py_DECREF(tup);
goto fail;
}
PyDict_SetItem(fields, title, tup);
}
}
Py_DECREF(tup);
if (ret == NPY_FAIL) {
goto fail;
}
dtypeflags |= (newdescr->flags & NPY_FROM_FIELDS);
}
new = PyArray_DescrNewFromType(NPY_VOID);
if (new == NULL) {
goto fail;
}
if (maxalign > 1) {
totalsize = NPY_NEXT_ALIGNED_OFFSET(totalsize, maxalign);
}
if (align) {
new->alignment = maxalign;
}
new->elsize = totalsize;
if (!PyTuple_Check(names)) {
names = PySequence_Tuple(names);
}
else {
Py_INCREF(names);
}
new->names = names;
new->fields = fields;
new->flags = dtypeflags;
/*
* If the fields weren't in order, and there was an OBJECT type,
* need to verify that no OBJECT types overlap with something else.
*/
if (has_out_of_order_fields && PyDataType_REFCHK(new)) {
if (validate_object_field_overlap(new) < 0) {
Py_DECREF(new);
return NULL;
}
}
/* Structured arrays get a sticky aligned bit */
if (align) {
new->flags |= NPY_ALIGNED_STRUCT;
}
/* Override the itemsize if provided */
tmp = Borrowed_PyMapping_GetItemString(obj, "itemsize");
if (tmp == NULL) {
PyErr_Clear();
} else {
itemsize = (int)PyArray_PyIntAsInt(tmp);
if (error_converting(itemsize)) {
Py_DECREF(new);
return NULL;
}
/* Make sure the itemsize isn't made too small */
if (itemsize < new->elsize) {
PyErr_Format(PyExc_ValueError,
"NumPy dtype descriptor requires %d bytes, "
"cannot override to smaller itemsize of %d",
(int)new->elsize, (int)itemsize);
Py_DECREF(new);
return NULL;
}
/* If align is set, make sure the alignment divides into the size */
if (align && itemsize % new->alignment != 0) {
PyErr_Format(PyExc_ValueError,
"NumPy dtype descriptor requires alignment of %d bytes, "
"which is not divisible into the specified itemsize %d",
(int)new->alignment, (int)itemsize);
Py_DECREF(new);
return NULL;
}
/* Set the itemsize */
new->elsize = itemsize;
}
/* Add the metadata if provided */
metadata = Borrowed_PyMapping_GetItemString(obj, "metadata");
if (metadata == NULL) {
PyErr_Clear();
}
else if (new->metadata == NULL) {
new->metadata = metadata;
Py_XINCREF(new->metadata);
}
else if (PyDict_Merge(new->metadata, metadata, 0) == -1) {
Py_DECREF(new);
return NULL;
}
return new;
fail:
Py_XDECREF(fields);
return NULL;
}
/*NUMPY_API*/
NPY_NO_EXPORT PyArray_Descr *
PyArray_DescrNewFromType(int type_num)
{
PyArray_Descr *old;
PyArray_Descr *new;
old = PyArray_DescrFromType(type_num);
new = PyArray_DescrNew(old);
Py_DECREF(old);
return new;
}
/*NUMPY_API
* Get typenum from an object -- None goes to NULL
*/
NPY_NO_EXPORT int
PyArray_DescrConverter2(PyObject *obj, PyArray_Descr **at)
{
if (obj == Py_None) {
*at = NULL;
return NPY_SUCCEED;
}
else {
return PyArray_DescrConverter(obj, at);
}
}
/*NUMPY_API
* Get typenum from an object -- None goes to NPY_DEFAULT_TYPE
* This function takes a Python object representing a type and converts it
* to a the correct PyArray_Descr * structure to describe the type.
*
* Many objects can be used to represent a data-type which in NumPy is
* quite a flexible concept.
*
* This is the central code that converts Python objects to
* Type-descriptor objects that are used throughout numpy.
*
* Returns a new reference in *at, but the returned should not be
* modified as it may be one of the canonical immutable objects or
* a reference to the input obj.
*/
NPY_NO_EXPORT int
PyArray_DescrConverter(PyObject *obj, PyArray_Descr **at)
{
int check_num = NPY_NOTYPE + 10;
PyObject *item;
int elsize = 0;
char endian = '=';
*at = NULL;
/* default */
if (obj == Py_None) {
*at = PyArray_DescrFromType(NPY_DEFAULT_TYPE);
return NPY_SUCCEED;
}
if (PyArray_DescrCheck(obj)) {
*at = (PyArray_Descr *)obj;
Py_INCREF(*at);
return NPY_SUCCEED;
}
if (PyType_Check(obj)) {
if (PyType_IsSubtype((PyTypeObject *)obj, &PyGenericArrType_Type)) {
*at = PyArray_DescrFromTypeObject(obj);
return (*at) ? NPY_SUCCEED : NPY_FAIL;
}
check_num = NPY_OBJECT;
#if !defined(NPY_PY3K)
if (obj == (PyObject *)(&PyInt_Type)) {
check_num = NPY_LONG;
}
else if (obj == (PyObject *)(&PyLong_Type)) {
check_num = NPY_LONGLONG;
}
#else
if (obj == (PyObject *)(&PyLong_Type)) {
check_num = NPY_LONG;
}
#endif
else if (obj == (PyObject *)(&PyFloat_Type)) {
check_num = NPY_DOUBLE;
}
else if (obj == (PyObject *)(&PyComplex_Type)) {
check_num = NPY_CDOUBLE;
}
else if (obj == (PyObject *)(&PyBool_Type)) {
check_num = NPY_BOOL;
}
else if (obj == (PyObject *)(&PyBytes_Type)) {
check_num = NPY_STRING;
}
else if (obj == (PyObject *)(&PyUnicode_Type)) {
check_num = NPY_UNICODE;
}
#if defined(NPY_PY3K)
else if (obj == (PyObject *)(&PyMemoryView_Type)) {
#else
else if (obj == (PyObject *)(&PyBuffer_Type)) {
#endif
check_num = NPY_VOID;
}
else {
*at = _arraydescr_from_dtype_attr(obj);
if (*at) {
return NPY_SUCCEED;
}
/*
* Note: this comes after _arraydescr_from_dtype_attr because the ctypes
* type might override the dtype if numpy does not otherwise
* support it.
*/
if (npy_ctypes_check((PyTypeObject *)obj)) {
*at = _arraydescr_from_ctypes_type((PyTypeObject *)obj);
return *at ? NPY_SUCCEED : NPY_FAIL;
}
}
goto finish;
}
/* or a typecode string */
if (PyUnicode_Check(obj)) {
/* Allow unicode format strings: convert to bytes */
int retval;
PyObject *obj2;
obj2 = PyUnicode_AsASCIIString(obj);
if (obj2 == NULL) {
/* Convert the exception into a TypeError */
PyObject *err = PyErr_Occurred();
if (PyErr_GivenExceptionMatches(err, PyExc_UnicodeEncodeError)) {
PyErr_SetString(PyExc_TypeError,
"data type not understood");
}
return NPY_FAIL;
}
retval = PyArray_DescrConverter(obj2, at);
Py_DECREF(obj2);
return retval;
}
if (PyBytes_Check(obj)) {
char *type = NULL;
Py_ssize_t len = 0;
/* Check for a string typecode. */
if (PyBytes_AsStringAndSize(obj, &type, &len) < 0) {
goto error;
}
/* Empty string is invalid */
if (len == 0) {
goto fail;
}
/* check for commas present or first (or second) element a digit */
if (_check_for_commastring(type, len)) {
*at = _convert_from_commastring(obj, 0);
return (*at) ? NPY_SUCCEED : NPY_FAIL;
}
/* Process the endian character. '|' is replaced by '='*/
switch (type[0]) {
case '>':
case '<':
case '=':
endian = type[0];
++type;
--len;
break;
case '|':
endian = '=';
++type;
--len;
break;
}
/* Just an endian character is invalid */
if (len == 0) {
goto fail;
}
/* Check for datetime format */
if (is_datetime_typestr(type, len)) {
*at = parse_dtype_from_datetime_typestr(type, len);
if (*at == NULL) {
return NPY_FAIL;
}
/* *at has byte order '=' at this point */
if (!PyArray_ISNBO(endian)) {
(*at)->byteorder = endian;
}
return NPY_SUCCEED;
}
/* A typecode like 'd' */
if (len == 1) {
check_num = type[0];
}
/* A kind + size like 'f8' */
else {
char *typeend = NULL;
int kind;
/* Parse the integer, make sure it's the rest of the string */
elsize = (int)strtol(type + 1, &typeend, 10);
if (typeend - type == len) {
kind = type[0];
switch (kind) {
case NPY_STRINGLTR:
case NPY_STRINGLTR2:
check_num = NPY_STRING;
break;
/*
* When specifying length of UNICODE
* the number of characters is given to match
* the STRING interface. Each character can be
* more than one byte and itemsize must be
* the number of bytes.
*/
case NPY_UNICODELTR:
check_num = NPY_UNICODE;
elsize <<= 2;
break;
case NPY_VOIDLTR:
check_num = NPY_VOID;
break;
default:
if (elsize == 0) {
check_num = NPY_NOTYPE+10;
}
/* Support for generic processing c8, i4, f8, etc...*/
else {
check_num = PyArray_TypestrConvert(elsize, kind);
if (check_num == NPY_NOTYPE) {
check_num += 10;
}
elsize = 0;
}
}
}
}
}
else if (PyTuple_Check(obj)) {
/* or a tuple */
*at = _convert_from_tuple(obj, 0);
if (*at == NULL){
if (PyErr_Occurred()) {
return NPY_FAIL;
}
goto fail;
}
return NPY_SUCCEED;
}
else if (PyList_Check(obj)) {
/* or a list */
*at = _convert_from_array_descr(obj,0);
if (*at == NULL) {
if (PyErr_Occurred()) {
return NPY_FAIL;
}
goto fail;
}
return NPY_SUCCEED;
}
else if (PyDict_Check(obj) || PyDictProxy_Check(obj)) {
/* or a dictionary */
*at = _convert_from_dict(obj,0);
if (*at == NULL) {
if (PyErr_Occurred()) {
return NPY_FAIL;
}
goto fail;
}
return NPY_SUCCEED;
}
else if (PyArray_Check(obj)) {
goto fail;
}
else {
*at = _arraydescr_from_dtype_attr(obj);
if (*at) {
return NPY_SUCCEED;
}
if (PyErr_Occurred()) {
return NPY_FAIL;
}
/*
* Note: this comes after _arraydescr_from_dtype_attr because the ctypes
* type might override the dtype if numpy does not otherwise
* support it.
*/
if (npy_ctypes_check(Py_TYPE(obj))) {
*at = _arraydescr_from_ctypes_type(Py_TYPE(obj));
return *at ? NPY_SUCCEED : NPY_FAIL;
}
goto fail;
}
if (PyErr_Occurred()) {
goto fail;
}
finish:
if ((check_num == NPY_NOTYPE + 10) ||
(*at = PyArray_DescrFromType(check_num)) == NULL) {
PyErr_Clear();
/* Now check to see if the object is registered in typeDict */
if (typeDict != NULL) {
item = PyDict_GetItem(typeDict, obj);
#if defined(NPY_PY3K)
if (!item && PyBytes_Check(obj)) {
PyObject *tmp;
tmp = PyUnicode_FromEncodedObject(obj, "ascii", "strict");
if (tmp != NULL) {
item = PyDict_GetItem(typeDict, tmp);
Py_DECREF(tmp);
}
}
#endif
if (item) {
/* Check for a deprecated Numeric-style typecode */
if (PyBytes_Check(obj)) {
char *type = NULL;
Py_ssize_t len = 0;
char *dep_tps[] = {"Bool", "Complex", "Float", "Int",
"Object0", "String0", "Timedelta64",
"Unicode0", "UInt", "Void0"};
int ndep_tps = sizeof(dep_tps) / sizeof(dep_tps[0]);
int i;
if (PyBytes_AsStringAndSize(obj, &type, &len) < 0) {
goto error;
}
for (i = 0; i < ndep_tps; ++i) {
char *dep_tp = dep_tps[i];
if (strncmp(type, dep_tp, strlen(dep_tp)) == 0) {
if (DEPRECATE("Numeric-style type codes are "
"deprecated and will result in "
"an error in the future.") < 0) {
goto fail;
}
}
}
}
return PyArray_DescrConverter(item, at);
}
}
goto fail;
}
if (PyDataType_ISUNSIZED(*at) && (*at)->elsize != elsize) {
PyArray_DESCR_REPLACE(*at);
if (*at == NULL) {
goto error;
}
(*at)->elsize = elsize;
}
if (endian != '=' && PyArray_ISNBO(endian)) {
endian = '=';
}
if (endian != '=' && (*at)->byteorder != '|'
&& (*at)->byteorder != endian) {
PyArray_DESCR_REPLACE(*at);
if (*at == NULL) {
goto error;
}
(*at)->byteorder = endian;
}
return NPY_SUCCEED;
fail:
if (PyBytes_Check(obj)) {
PyErr_Format(PyExc_TypeError,
"data type \"%s\" not understood", PyBytes_AS_STRING(obj));
}
else {
PyErr_SetString(PyExc_TypeError,
"data type not understood");
}
error:
*at = NULL;
return NPY_FAIL;
}
/** Array Descr Objects for dynamic types **/
/*
* There are some statically-defined PyArray_Descr objects corresponding
* to the basic built-in types.
* These can and should be DECREF'd and INCREF'd as appropriate, anyway.
* If a mistake is made in reference counting, deallocation on these
* builtins will be attempted leading to problems.
*
* This lets us deal with all PyArray_Descr objects using reference
* counting (regardless of whether they are statically or dynamically
* allocated).
*/
/*NUMPY_API
* base cannot be NULL
*/
NPY_NO_EXPORT PyArray_Descr *
PyArray_DescrNew(PyArray_Descr *base)
{
PyArray_Descr *newdescr = PyObject_New(PyArray_Descr, &PyArrayDescr_Type);
if (newdescr == NULL) {
return NULL;
}
/* Don't copy PyObject_HEAD part */
memcpy((char *)newdescr + sizeof(PyObject),
(char *)base + sizeof(PyObject),
sizeof(PyArray_Descr) - sizeof(PyObject));
/*
* The c_metadata has a by-value ownership model, need to clone it
* (basically a deep copy, but the auxdata clone function has some
* flexibility still) so the new PyArray_Descr object owns
* a copy of the data. Having both 'base' and 'newdescr' point to
* the same auxdata pointer would cause a double-free of memory.
*/
if (base->c_metadata != NULL) {
newdescr->c_metadata = NPY_AUXDATA_CLONE(base->c_metadata);
if (newdescr->c_metadata == NULL) {
PyErr_NoMemory();
/* TODO: This seems wrong, as the old fields get decref'd? */
Py_DECREF(newdescr);
return NULL;
}
}
if (newdescr->fields == Py_None) {
newdescr->fields = NULL;
}
Py_XINCREF(newdescr->fields);
Py_XINCREF(newdescr->names);
if (newdescr->subarray) {
newdescr->subarray = PyArray_malloc(sizeof(PyArray_ArrayDescr));
if (newdescr->subarray == NULL) {
Py_DECREF(newdescr);
return (PyArray_Descr *)PyErr_NoMemory();
}
memcpy(newdescr->subarray, base->subarray, sizeof(PyArray_ArrayDescr));
Py_INCREF(newdescr->subarray->shape);
Py_INCREF(newdescr->subarray->base);
}
Py_XINCREF(newdescr->typeobj);
Py_XINCREF(newdescr->metadata);
newdescr->hash = -1;
return newdescr;
}
/*
* should never be called for builtin-types unless
* there is a reference-count problem
*/
static void
arraydescr_dealloc(PyArray_Descr *self)
{
if (self->fields == Py_None) {
fprintf(stderr, "*** Reference count error detected: \n" \
"an attempt was made to deallocate %d (%c) ***\n",
self->type_num, self->type);
Py_INCREF(self);
Py_INCREF(self);
return;
}
_dealloc_cached_buffer_info((PyObject*)self);
Py_XDECREF(self->typeobj);
Py_XDECREF(self->names);
Py_XDECREF(self->fields);
if (self->subarray) {
Py_XDECREF(self->subarray->shape);
Py_DECREF(self->subarray->base);
PyArray_free(self->subarray);
}
Py_XDECREF(self->metadata);
NPY_AUXDATA_FREE(self->c_metadata);
self->c_metadata = NULL;
Py_TYPE(self)->tp_free((PyObject *)self);
}
/*
* we need to be careful about setting attributes because these
* objects are pointed to by arrays that depend on them for interpreting
* data. Currently no attributes of data-type objects can be set
* directly except names.
*/
static PyMemberDef arraydescr_members[] = {
{"type",
T_OBJECT, offsetof(PyArray_Descr, typeobj), READONLY, NULL},
{"kind",
T_CHAR, offsetof(PyArray_Descr, kind), READONLY, NULL},
{"char",
T_CHAR, offsetof(PyArray_Descr, type), READONLY, NULL},
{"num",
T_INT, offsetof(PyArray_Descr, type_num), READONLY, NULL},
{"byteorder",
T_CHAR, offsetof(PyArray_Descr, byteorder), READONLY, NULL},
{"itemsize",
T_INT, offsetof(PyArray_Descr, elsize), READONLY, NULL},
{"alignment",
T_INT, offsetof(PyArray_Descr, alignment), READONLY, NULL},
{"flags",
T_BYTE, offsetof(PyArray_Descr, flags), READONLY, NULL},
{NULL, 0, 0, 0, NULL},
};
static PyObject *
arraydescr_subdescr_get(PyArray_Descr *self)
{
if (!PyDataType_HASSUBARRAY(self)) {
Py_RETURN_NONE;
}
return Py_BuildValue("OO",
(PyObject *)self->subarray->base, self->subarray->shape);
}
NPY_NO_EXPORT PyObject *
arraydescr_protocol_typestr_get(PyArray_Descr *self)
{
char basic_ = self->kind;
char endian = self->byteorder;
int size = self->elsize;
PyObject *ret;
if (endian == '=') {
endian = '<';
if (!PyArray_IsNativeByteOrder(endian)) {
endian = '>';
}
}
if (self->type_num == NPY_UNICODE) {
size >>= 2;
}
if (self->type_num == NPY_OBJECT) {
ret = PyUString_FromFormat("%c%c", endian, basic_);
}
else {
ret = PyUString_FromFormat("%c%c%d", endian, basic_, size);
}
if (PyDataType_ISDATETIME(self)) {
PyArray_DatetimeMetaData *meta;
meta = get_datetime_metadata_from_dtype(self);
if (meta == NULL) {
Py_DECREF(ret);
return NULL;
}
ret = append_metastr_to_string(meta, 0, ret);
}
return ret;
}
static PyObject *
arraydescr_name_get(PyArray_Descr *self)
{
/* let python handle this */
PyObject *_numpy_dtype;
PyObject *res;
_numpy_dtype = PyImport_ImportModule("numpy.core._dtype");
if (_numpy_dtype == NULL) {
return NULL;
}
res = PyObject_CallMethod(_numpy_dtype, "_name_get", "O", self);
Py_DECREF(_numpy_dtype);
return res;
}
static PyObject *
arraydescr_base_get(PyArray_Descr *self)
{
if (!PyDataType_HASSUBARRAY(self)) {
Py_INCREF(self);
return (PyObject *)self;
}
Py_INCREF(self->subarray->base);
return (PyObject *)(self->subarray->base);
}
static PyObject *
arraydescr_shape_get(PyArray_Descr *self)
{
if (!PyDataType_HASSUBARRAY(self)) {
return PyTuple_New(0);
}
assert(PyTuple_Check(self->subarray->shape));
Py_INCREF(self->subarray->shape);
return self->subarray->shape;
}
static PyObject *
arraydescr_ndim_get(PyArray_Descr *self)
{
Py_ssize_t ndim;
if (!PyDataType_HASSUBARRAY(self)) {
return PyInt_FromLong(0);
}
/*
* PyTuple_Size has built in check
* for tuple argument
*/
ndim = PyTuple_Size(self->subarray->shape);
return PyInt_FromLong(ndim);
}
NPY_NO_EXPORT PyObject *
arraydescr_protocol_descr_get(PyArray_Descr *self)
{
PyObject *dobj, *res;
PyObject *_numpy_internal;
if (!PyDataType_HASFIELDS(self)) {
/* get default */
dobj = PyTuple_New(2);
if (dobj == NULL) {
return NULL;
}
PyTuple_SET_ITEM(dobj, 0, PyUString_FromString(""));
PyTuple_SET_ITEM(dobj, 1, arraydescr_protocol_typestr_get(self));
res = PyList_New(1);
if (res == NULL) {
Py_DECREF(dobj);
return NULL;
}
PyList_SET_ITEM(res, 0, dobj);
return res;
}
_numpy_internal = PyImport_ImportModule("numpy.core._internal");
if (_numpy_internal == NULL) {
return NULL;
}
res = PyObject_CallMethod(_numpy_internal, "_array_descr", "O", self);
Py_DECREF(_numpy_internal);
return res;
}
/*
* returns 1 for a builtin type
* and 2 for a user-defined data-type descriptor
* return 0 if neither (i.e. it's a copy of one)
*/
static PyObject *
arraydescr_isbuiltin_get(PyArray_Descr *self)
{
long val;
val = 0;
if (self->fields == Py_None) {
val = 1;
}
if (PyTypeNum_ISUSERDEF(self->type_num)) {
val = 2;
}
return PyInt_FromLong(val);
}
static int
_arraydescr_isnative(PyArray_Descr *self)
{
if (!PyDataType_HASFIELDS(self)) {
return PyArray_ISNBO(self->byteorder);
}
else {
PyObject *key, *value, *title = NULL;
PyArray_Descr *new;
int offset;
Py_ssize_t pos = 0;
while (PyDict_Next(self->fields, &pos, &key, &value)) {
if NPY_TITLE_KEY(key, value) {
continue;
}
if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset, &title)) {
return -1;
}
if (!_arraydescr_isnative(new)) {
return 0;
}
}
}
return 1;
}
/*
* return Py_True if this data-type descriptor
* has native byteorder if no fields are defined
*
* or if all sub-fields have native-byteorder if
* fields are defined
*/
static PyObject *
arraydescr_isnative_get(PyArray_Descr *self)
{
PyObject *ret;
int retval;
retval = _arraydescr_isnative(self);
if (retval == -1) {
return NULL;
}
ret = retval ? Py_True : Py_False;
Py_INCREF(ret);
return ret;
}
static PyObject *
arraydescr_isalignedstruct_get(PyArray_Descr *self)
{
PyObject *ret;
ret = (self->flags&NPY_ALIGNED_STRUCT) ? Py_True : Py_False;
Py_INCREF(ret);
return ret;
}
static PyObject *
arraydescr_fields_get(PyArray_Descr *self)
{
if (!PyDataType_HASFIELDS(self)) {
Py_RETURN_NONE;
}
return PyDictProxy_New(self->fields);
}
static PyObject *
arraydescr_metadata_get(PyArray_Descr *self)
{
if (self->metadata == NULL) {
Py_RETURN_NONE;
}
return PyDictProxy_New(self->metadata);
}
static PyObject *
arraydescr_hasobject_get(PyArray_Descr *self)
{
if (PyDataType_FLAGCHK(self, NPY_ITEM_HASOBJECT)) {
Py_RETURN_TRUE;
}
else {
Py_RETURN_FALSE;
}
}
static PyObject *
arraydescr_names_get(PyArray_Descr *self)
{
if (!PyDataType_HASFIELDS(self)) {
Py_RETURN_NONE;
}
Py_INCREF(self->names);
return self->names;
}
static int
arraydescr_names_set(PyArray_Descr *self, PyObject *val)
{
int N = 0;
int i;
PyObject *new_names;
PyObject *new_fields;
if (val == NULL) {
PyErr_SetString(PyExc_AttributeError,
"Cannot delete dtype names attribute");
return -1;
}
if (!PyDataType_HASFIELDS(self)) {
PyErr_SetString(PyExc_ValueError,
"there are no fields defined");
return -1;
}
/*
* FIXME
*
* This deprecation has been temporarily removed for the NumPy 1.7
* release. It should be re-added after the 1.7 branch is done,
* and a convenience API to replace the typical use-cases for
* mutable names should be implemented.
*
* if (DEPRECATE("Setting NumPy dtype names is deprecated, the dtype "
* "will become immutable in a future version") < 0) {
* return -1;
* }
*/
N = PyTuple_GET_SIZE(self->names);
if (!PySequence_Check(val) || PyObject_Size((PyObject *)val) != N) {
PyErr_Format(PyExc_ValueError,
"must replace all names at once with a sequence of length %d",
N);
return -1;
}
/* Make sure all entries are strings */
for (i = 0; i < N; i++) {
PyObject *item;
int valid = 1;
item = PySequence_GetItem(val, i);
valid = PyUString_Check(item);
Py_DECREF(item);
if (!valid) {
PyErr_Format(PyExc_ValueError,
"item #%d of names is of type %s and not string",
i, Py_TYPE(item)->tp_name);
return -1;
}
}
/* Invalidate cached hash value */
self->hash = -1;
/* Update dictionary keys in fields */
new_names = PySequence_Tuple(val);
new_fields = PyDict_New();
for (i = 0; i < N; i++) {
PyObject *key;
PyObject *item;
PyObject *new_key;
int ret;
key = PyTuple_GET_ITEM(self->names, i);
/* Borrowed references to item and new_key */
item = PyDict_GetItem(self->fields, key);
new_key = PyTuple_GET_ITEM(new_names, i);
/* Check for duplicates */
ret = PyDict_Contains(new_fields, new_key);
if (ret != 0) {
if (ret < 0) {
PyErr_Clear();
}
PyErr_SetString(PyExc_ValueError, "Duplicate field names given.");
Py_DECREF(new_names);
Py_DECREF(new_fields);
return -1;
}
PyDict_SetItem(new_fields, new_key, item);
}
/* Replace names */
Py_DECREF(self->names);
self->names = new_names;
/* Replace fields */
Py_DECREF(self->fields);
self->fields = new_fields;
return 0;
}
static PyGetSetDef arraydescr_getsets[] = {
{"subdtype",
(getter)arraydescr_subdescr_get,
NULL, NULL, NULL},
{"descr",
(getter)arraydescr_protocol_descr_get,
NULL, NULL, NULL},
{"str",
(getter)arraydescr_protocol_typestr_get,
NULL, NULL, NULL},
{"name",
(getter)arraydescr_name_get,
NULL, NULL, NULL},
{"base",
(getter)arraydescr_base_get,
NULL, NULL, NULL},
{"shape",
(getter)arraydescr_shape_get,
NULL, NULL, NULL},
{"ndim",
(getter)arraydescr_ndim_get,
NULL, NULL, NULL},
{"isbuiltin",
(getter)arraydescr_isbuiltin_get,
NULL, NULL, NULL},
{"isnative",
(getter)arraydescr_isnative_get,
NULL, NULL, NULL},
{"isalignedstruct",
(getter)arraydescr_isalignedstruct_get,
NULL, NULL, NULL},
{"fields",
(getter)arraydescr_fields_get,
NULL, NULL, NULL},
{"metadata",
(getter)arraydescr_metadata_get,
NULL, NULL, NULL},
{"names",
(getter)arraydescr_names_get,
(setter)arraydescr_names_set,
NULL, NULL},
{"hasobject",
(getter)arraydescr_hasobject_get,
NULL, NULL, NULL},
{NULL, NULL, NULL, NULL, NULL},
};
static PyObject *
arraydescr_new(PyTypeObject *NPY_UNUSED(subtype),
PyObject *args, PyObject *kwds)
{
PyObject *odescr, *metadata=NULL;
PyArray_Descr *descr, *conv;
npy_bool align = NPY_FALSE;
npy_bool copy = NPY_FALSE;
npy_bool copied = NPY_FALSE;
static char *kwlist[] = {"dtype", "align", "copy", "metadata", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&O&O!:dtype", kwlist,
&odescr,
PyArray_BoolConverter, &align,
PyArray_BoolConverter, &copy,
&PyDict_Type, &metadata)) {
return NULL;
}
if (align) {
if (!PyArray_DescrAlignConverter(odescr, &conv)) {
return NULL;
}
}
else if (!PyArray_DescrConverter(odescr, &conv)) {
return NULL;
}
/* Get a new copy of it unless it's already a copy */
if (copy && conv->fields == Py_None) {
descr = PyArray_DescrNew(conv);
Py_DECREF(conv);
conv = descr;
copied = NPY_TRUE;
}
if ((metadata != NULL)) {
/*
* We need to be sure to make a new copy of the data-type and any
* underlying dictionary
*/
if (!copied) {
copied = NPY_TRUE;
descr = PyArray_DescrNew(conv);
Py_DECREF(conv);
conv = descr;
}
if ((conv->metadata != NULL)) {
/*
* Make a copy of the metadata before merging with the
* input metadata so that this data-type descriptor has
* it's own copy
*/
/* Save a reference */
odescr = conv->metadata;
conv->metadata = PyDict_Copy(odescr);
/* Decrement the old reference */
Py_DECREF(odescr);
/*
* Update conv->metadata with anything new in metadata
* keyword, but do not over-write anything already there
*/
if (PyDict_Merge(conv->metadata, metadata, 0) != 0) {
Py_DECREF(conv);
return NULL;
}
}
else {
/* Make a copy of the input dictionary */
conv->metadata = PyDict_Copy(metadata);
}
}
return (PyObject *)conv;
}
/*
* Return a tuple of
* (cleaned metadata dictionary, tuple with (str, num))
*/
static PyObject *
_get_pickleabletype_from_datetime_metadata(PyArray_Descr *dtype)
{
PyObject *ret, *dt_tuple;
PyArray_DatetimeMetaData *meta;
/* Create the 2-item tuple to return */
ret = PyTuple_New(2);
if (ret == NULL) {
return NULL;
}
/* Store the metadata dictionary */
if (dtype->metadata != NULL) {
Py_INCREF(dtype->metadata);
PyTuple_SET_ITEM(ret, 0, dtype->metadata);
} else {
PyTuple_SET_ITEM(ret, 0, PyDict_New());
}
/* Convert the datetime metadata into a tuple */
meta = get_datetime_metadata_from_dtype(dtype);
if (meta == NULL) {
Py_DECREF(ret);
return NULL;
}
/* Use a 4-tuple that numpy 1.6 knows how to unpickle */
dt_tuple = PyTuple_New(4);
if (dt_tuple == NULL) {
Py_DECREF(ret);
return NULL;
}
PyTuple_SET_ITEM(dt_tuple, 0,
PyBytes_FromString(_datetime_strings[meta->base]));
PyTuple_SET_ITEM(dt_tuple, 1,
PyInt_FromLong(meta->num));
PyTuple_SET_ITEM(dt_tuple, 2,
PyInt_FromLong(1));
PyTuple_SET_ITEM(dt_tuple, 3,
PyInt_FromLong(1));
PyTuple_SET_ITEM(ret, 1, dt_tuple);
return ret;
}
/*
* return a tuple of (callable object, args, state).
*
* TODO: This method needs to change so that unpickling doesn't
* use __setstate__. This is required for the dtype
* to be an immutable object.
*/
static PyObject *
arraydescr_reduce(PyArray_Descr *self, PyObject *NPY_UNUSED(args))
{
/*
* version number of this pickle type. Increment if we need to
* change the format. Be sure to handle the old versions in
* arraydescr_setstate.
*/
const int version = 4;
PyObject *ret, *mod, *obj;
PyObject *state;
char endian;
int elsize, alignment;
ret = PyTuple_New(3);
if (ret == NULL) {
return NULL;
}
mod = PyImport_ImportModule("numpy.core._multiarray_umath");
if (mod == NULL) {
Py_DECREF(ret);
return NULL;
}
obj = PyObject_GetAttrString(mod, "dtype");
Py_DECREF(mod);
if (obj == NULL) {
Py_DECREF(ret);
return NULL;
}
PyTuple_SET_ITEM(ret, 0, obj);
if (PyTypeNum_ISUSERDEF(self->type_num)
|| ((self->type_num == NPY_VOID
&& self->typeobj != &PyVoidArrType_Type))) {
obj = (PyObject *)self->typeobj;
Py_INCREF(obj);
}
else {
elsize = self->elsize;
if (self->type_num == NPY_UNICODE) {
elsize >>= 2;
}
obj = PyUString_FromFormat("%c%d",self->kind, elsize);
}
PyTuple_SET_ITEM(ret, 1, Py_BuildValue("(Nii)", obj, 0, 1));
/*
* Now return the state which is at least byteorder,
* subarray, and fields
*/
endian = self->byteorder;
if (endian == '=') {
endian = '<';
if (!PyArray_IsNativeByteOrder(endian)) {
endian = '>';
}
}
if (PyDataType_ISDATETIME(self)) {
PyObject *newobj;
state = PyTuple_New(9);
PyTuple_SET_ITEM(state, 0, PyInt_FromLong(version));
/*
* newobj is a tuple of the Python metadata dictionary
* and tuple of date_time info (str, num)
*/
newobj = _get_pickleabletype_from_datetime_metadata(self);
if (newobj == NULL) {
Py_DECREF(state);
Py_DECREF(ret);
return NULL;
}
PyTuple_SET_ITEM(state, 8, newobj);
}
else if (self->metadata) {
state = PyTuple_New(9);
PyTuple_SET_ITEM(state, 0, PyInt_FromLong(version));
Py_INCREF(self->metadata);
PyTuple_SET_ITEM(state, 8, self->metadata);
}
else { /* Use version 3 pickle format */
state = PyTuple_New(8);
PyTuple_SET_ITEM(state, 0, PyInt_FromLong(3));
}
PyTuple_SET_ITEM(state, 1, PyUString_FromFormat("%c", endian));
PyTuple_SET_ITEM(state, 2, arraydescr_subdescr_get(self));
if (PyDataType_HASFIELDS(self)) {
Py_INCREF(self->names);
Py_INCREF(self->fields);
PyTuple_SET_ITEM(state, 3, self->names);
PyTuple_SET_ITEM(state, 4, self->fields);
}
else {
PyTuple_SET_ITEM(state, 3, Py_None);
PyTuple_SET_ITEM(state, 4, Py_None);
Py_INCREF(Py_None);
Py_INCREF(Py_None);
}
/* for extended types it also includes elsize and alignment */
if (PyTypeNum_ISEXTENDED(self->type_num)) {
elsize = self->elsize;
alignment = self->alignment;
}
else {
elsize = -1;
alignment = -1;
}
PyTuple_SET_ITEM(state, 5, PyInt_FromLong(elsize));
PyTuple_SET_ITEM(state, 6, PyInt_FromLong(alignment));
PyTuple_SET_ITEM(state, 7, PyInt_FromLong(self->flags));
PyTuple_SET_ITEM(ret, 2, state);
return ret;
}
/*
* returns NPY_OBJECT_DTYPE_FLAGS if this data-type has an object portion used
* when setting the state because hasobject is not stored.
*/
static char
_descr_find_object(PyArray_Descr *self)
{
if (self->flags
|| self->type_num == NPY_OBJECT
|| self->kind == 'O') {
return NPY_OBJECT_DTYPE_FLAGS;
}
if (PyDataType_HASFIELDS(self)) {
PyObject *key, *value, *title = NULL;
PyArray_Descr *new;
int offset;
Py_ssize_t pos = 0;
while (PyDict_Next(self->fields, &pos, &key, &value)) {
if NPY_TITLE_KEY(key, value) {
continue;
}
if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset, &title)) {
PyErr_Clear();
return 0;
}
if (_descr_find_object(new)) {
new->flags = NPY_OBJECT_DTYPE_FLAGS;
return NPY_OBJECT_DTYPE_FLAGS;
}
}
}
return 0;
}
/*
* state is at least byteorder, subarray, and fields but could include elsize
* and alignment for EXTENDED arrays
*/
static PyObject *
arraydescr_setstate(PyArray_Descr *self, PyObject *args)
{
int elsize = -1, alignment = -1;
int version = 4;
char endian;
PyObject *endian_obj;
PyObject *subarray, *fields, *names = NULL, *metadata=NULL;
int incref_names = 1;
int int_dtypeflags = 0;
char dtypeflags;
if (self->fields == Py_None) {
Py_RETURN_NONE;
}
if (PyTuple_GET_SIZE(args) != 1
|| !(PyTuple_Check(PyTuple_GET_ITEM(args, 0)))) {
PyErr_BadInternalCall();
return NULL;
}
switch (PyTuple_GET_SIZE(PyTuple_GET_ITEM(args,0))) {
case 9:
if (!PyArg_ParseTuple(args, "(iOOOOiiiO):__setstate__",
&version, &endian_obj,
&subarray, &names, &fields, &elsize,
&alignment, &int_dtypeflags, &metadata)) {
PyErr_Clear();
return NULL;
}
break;
case 8:
if (!PyArg_ParseTuple(args, "(iOOOOiii):__setstate__",
&version, &endian_obj,
&subarray, &names, &fields, &elsize,
&alignment, &int_dtypeflags)) {
return NULL;
}
break;
case 7:
if (!PyArg_ParseTuple(args, "(iOOOOii):__setstate__",
&version, &endian_obj,
&subarray, &names, &fields, &elsize,
&alignment)) {
return NULL;
}
break;
case 6:
if (!PyArg_ParseTuple(args, "(iOOOii):__setstate__",
&version,
&endian_obj, &subarray, &fields,
&elsize, &alignment)) {
return NULL;
}
break;
case 5:
version = 0;
if (!PyArg_ParseTuple(args, "(OOOii):__setstate__",
&endian_obj, &subarray, &fields, &elsize,
&alignment)) {
return NULL;
}
break;
default:
/* raise an error */
if (PyTuple_GET_SIZE(PyTuple_GET_ITEM(args,0)) > 5) {
version = PyInt_AsLong(PyTuple_GET_ITEM(args, 0));
}
else {
version = -1;
}
}
/*
* If we ever need another pickle format, increment the version
* number. But we should still be able to handle the old versions.
*/
if (version < 0 || version > 4) {
PyErr_Format(PyExc_ValueError,
"can't handle version %d of numpy.dtype pickle",
version);
return NULL;
}
/* Invalidate cached hash value */
self->hash = -1;
if (version == 1 || version == 0) {
if (fields != Py_None) {
PyObject *key, *list;
key = PyInt_FromLong(-1);
list = PyDict_GetItem(fields, key);
if (!list) {
return NULL;
}
Py_INCREF(list);
names = list;
PyDict_DelItem(fields, key);
incref_names = 0;
}
else {
names = Py_None;
}
}
/* Parse endian */
if (PyUnicode_Check(endian_obj) || PyBytes_Check(endian_obj)) {
PyObject *tmp = NULL;
char *str;
Py_ssize_t len;
if (PyUnicode_Check(endian_obj)) {
tmp = PyUnicode_AsASCIIString(endian_obj);
if (tmp == NULL) {
return NULL;
}
endian_obj = tmp;
}
if (PyBytes_AsStringAndSize(endian_obj, &str, &len) < 0) {
Py_XDECREF(tmp);
return NULL;
}
if (len != 1) {
PyErr_SetString(PyExc_ValueError,
"endian is not 1-char string in Numpy dtype unpickling");
Py_XDECREF(tmp);
return NULL;
}
endian = str[0];
Py_XDECREF(tmp);
}
else {
PyErr_SetString(PyExc_ValueError,
"endian is not a string in Numpy dtype unpickling");
return NULL;
}
if ((fields == Py_None && names != Py_None) ||
(names == Py_None && fields != Py_None)) {
PyErr_Format(PyExc_ValueError,
"inconsistent fields and names in Numpy dtype unpickling");
return NULL;
}
if (names != Py_None && !PyTuple_Check(names)) {
PyErr_Format(PyExc_ValueError,
"non-tuple names in Numpy dtype unpickling");
return NULL;
}
if (fields != Py_None && !PyDict_Check(fields)) {
PyErr_Format(PyExc_ValueError,
"non-dict fields in Numpy dtype unpickling");
return NULL;
}
if (endian != '|' && PyArray_IsNativeByteOrder(endian)) {
endian = '=';
}
self->byteorder = endian;
if (self->subarray) {
Py_XDECREF(self->subarray->base);
Py_XDECREF(self->subarray->shape);
PyArray_free(self->subarray);
}
self->subarray = NULL;
if (subarray != Py_None) {
PyObject *subarray_shape;
/*
* Ensure that subarray[0] is an ArrayDescr and
* that subarray_shape obtained from subarray[1] is a tuple of integers.
*/
if (!(PyTuple_Check(subarray) &&
PyTuple_Size(subarray) == 2 &&
PyArray_DescrCheck(PyTuple_GET_ITEM(subarray, 0)))) {
PyErr_Format(PyExc_ValueError,
"incorrect subarray in __setstate__");
return NULL;
}
subarray_shape = PyTuple_GET_ITEM(subarray, 1);
if (PyNumber_Check(subarray_shape)) {
PyObject *tmp;
#if defined(NPY_PY3K)
tmp = PyNumber_Long(subarray_shape);
#else
tmp = PyNumber_Int(subarray_shape);
#endif
if (tmp == NULL) {
return NULL;
}
subarray_shape = Py_BuildValue("(O)", tmp);
Py_DECREF(tmp);
if (subarray_shape == NULL) {
return NULL;
}
}
else if (_is_tuple_of_integers(subarray_shape)) {
Py_INCREF(subarray_shape);
}
else {
PyErr_Format(PyExc_ValueError,
"incorrect subarray shape in __setstate__");
return NULL;
}
self->subarray = PyArray_malloc(sizeof(PyArray_ArrayDescr));
if (!PyDataType_HASSUBARRAY(self)) {
return PyErr_NoMemory();
}
self->subarray->base = (PyArray_Descr *)PyTuple_GET_ITEM(subarray, 0);
Py_INCREF(self->subarray->base);
self->subarray->shape = subarray_shape;
}
if (fields != Py_None) {
/*
* Ensure names are of appropriate string type
*/
Py_ssize_t i;
int names_ok = 1;
PyObject *name;
for (i = 0; i < PyTuple_GET_SIZE(names); ++i) {
name = PyTuple_GET_ITEM(names, i);
if (!PyUString_Check(name)) {
names_ok = 0;
break;
}
}
if (names_ok) {
Py_XDECREF(self->fields);
self->fields = fields;
Py_INCREF(fields);
Py_XDECREF(self->names);
self->names = names;
if (incref_names) {
Py_INCREF(names);
}
}
else {
#if defined(NPY_PY3K)
/*
* To support pickle.load(f, encoding='bytes') for loading Py2
* generated pickles on Py3, we need to be more lenient and convert
* field names from byte strings to unicode.
*/
PyObject *tmp, *new_name, *field;
tmp = PyDict_New();
if (tmp == NULL) {
return NULL;
}
Py_XDECREF(self->fields);
self->fields = tmp;
tmp = PyTuple_New(PyTuple_GET_SIZE(names));
if (tmp == NULL) {
return NULL;
}
Py_XDECREF(self->names);
self->names = tmp;
for (i = 0; i < PyTuple_GET_SIZE(names); ++i) {
name = PyTuple_GET_ITEM(names, i);
field = PyDict_GetItem(fields, name);
if (!field) {
return NULL;
}
if (PyUnicode_Check(name)) {
new_name = name;
Py_INCREF(new_name);
}
else {
new_name = PyUnicode_FromEncodedObject(name, "ASCII", "strict");
if (new_name == NULL) {
return NULL;
}
}
PyTuple_SET_ITEM(self->names, i, new_name);
if (PyDict_SetItem(self->fields, new_name, field) != 0) {
return NULL;
}
}
#else
PyErr_Format(PyExc_ValueError,
"non-string names in Numpy dtype unpickling");
return NULL;
#endif
}
}
if (PyTypeNum_ISEXTENDED(self->type_num)) {
self->elsize = elsize;
self->alignment = alignment;
}
/*
* We use an integer converted to char for backward compatibility with
* pickled arrays. Pickled arrays created with previous versions encoded
* flags as an int even though it actually was a char in the PyArray_Descr
* structure
*/
dtypeflags = int_dtypeflags;
if (dtypeflags != int_dtypeflags) {
PyErr_Format(PyExc_ValueError,
"incorrect value for flags variable (overflow)");
return NULL;
}
else {
self->flags = dtypeflags;
}
if (version < 3) {
self->flags = _descr_find_object(self);
}
/*
* We have a borrowed reference to metadata so no need
* to alter reference count when throwing away Py_None.
*/
if (metadata == Py_None) {
metadata = NULL;
}
if (PyDataType_ISDATETIME(self) && (metadata != NULL)) {
PyObject *old_metadata, *errmsg;
PyArray_DatetimeMetaData temp_dt_data;
if ((! PyTuple_Check(metadata)) || (PyTuple_Size(metadata) != 2)) {
errmsg = PyUString_FromString("Invalid datetime dtype (metadata, c_metadata): ");
PyUString_ConcatAndDel(&errmsg, PyObject_Repr(metadata));
PyErr_SetObject(PyExc_ValueError, errmsg);
Py_DECREF(errmsg);
return NULL;
}
if (convert_datetime_metadata_tuple_to_datetime_metadata(
PyTuple_GET_ITEM(metadata, 1),
&temp_dt_data,
NPY_TRUE) < 0) {
return NULL;
}
old_metadata = self->metadata;
self->metadata = PyTuple_GET_ITEM(metadata, 0);
memcpy((char *) &((PyArray_DatetimeDTypeMetaData *)self->c_metadata)->meta,
(char *) &temp_dt_data,
sizeof(PyArray_DatetimeMetaData));
Py_XINCREF(self->metadata);
Py_XDECREF(old_metadata);
}
else {
PyObject *old_metadata = self->metadata;
self->metadata = metadata;
Py_XINCREF(self->metadata);
Py_XDECREF(old_metadata);
}
Py_RETURN_NONE;
}
/*NUMPY_API
*
* Get type-descriptor from an object forcing alignment if possible
* None goes to DEFAULT type.
*
* any object with the .fields attribute and/or .itemsize attribute (if the
*.fields attribute does not give the total size -- i.e. a partial record
* naming). If itemsize is given it must be >= size computed from fields
*
* The .fields attribute must return a convertible dictionary if present.
* Result inherits from NPY_VOID.
*/
NPY_NO_EXPORT int
PyArray_DescrAlignConverter(PyObject *obj, PyArray_Descr **at)
{
if (PyDict_Check(obj) || PyDictProxy_Check(obj)) {
*at = _convert_from_dict(obj, 1);
}
else if (PyBytes_Check(obj)) {
*at = _convert_from_commastring(obj, 1);
}
else if (PyUnicode_Check(obj)) {
PyObject *tmp;
tmp = PyUnicode_AsASCIIString(obj);
*at = _convert_from_commastring(tmp, 1);
Py_DECREF(tmp);
}
else if (PyTuple_Check(obj)) {
*at = _convert_from_tuple(obj, 1);
}
else if (PyList_Check(obj)) {
*at = _convert_from_array_descr(obj, 1);
}
else {
return PyArray_DescrConverter(obj, at);
}
if (*at == NULL) {
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_ValueError,
"data-type-descriptor not understood");
}
return NPY_FAIL;
}
return NPY_SUCCEED;
}
/*NUMPY_API
*
* Get type-descriptor from an object forcing alignment if possible
* None goes to NULL.
*/
NPY_NO_EXPORT int
PyArray_DescrAlignConverter2(PyObject *obj, PyArray_Descr **at)
{
if (PyDict_Check(obj) || PyDictProxy_Check(obj)) {
*at = _convert_from_dict(obj, 1);
}
else if (PyBytes_Check(obj)) {
*at = _convert_from_commastring(obj, 1);
}
else if (PyUnicode_Check(obj)) {
PyObject *tmp;
tmp = PyUnicode_AsASCIIString(obj);
*at = _convert_from_commastring(tmp, 1);
Py_DECREF(tmp);
}
else if (PyList_Check(obj)) {
*at = _convert_from_array_descr(obj, 1);
}
else {
return PyArray_DescrConverter2(obj, at);
}
if (*at == NULL) {
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_ValueError,
"data-type-descriptor not understood");
}
return NPY_FAIL;
}
return NPY_SUCCEED;
}
/*NUMPY_API
*
* returns a copy of the PyArray_Descr structure with the byteorder
* altered:
* no arguments: The byteorder is swapped (in all subfields as well)
* single argument: The byteorder is forced to the given state
* (in all subfields as well)
*
* Valid states: ('big', '>') or ('little' or '<')
* ('native', or '=')
*
* If a descr structure with | is encountered it's own
* byte-order is not changed but any fields are:
*
*
* Deep bytorder change of a data-type descriptor
* *** Leaves reference count of self unchanged --- does not DECREF self ***
*/
NPY_NO_EXPORT PyArray_Descr *
PyArray_DescrNewByteorder(PyArray_Descr *self, char newendian)
{
PyArray_Descr *new;
char endian;
new = PyArray_DescrNew(self);
endian = new->byteorder;
if (endian != NPY_IGNORE) {
if (newendian == NPY_SWAP) {
/* swap byteorder */
if (PyArray_ISNBO(endian)) {
endian = NPY_OPPBYTE;
}
else {
endian = NPY_NATBYTE;
}
new->byteorder = endian;
}
else if (newendian != NPY_IGNORE) {
new->byteorder = newendian;
}
}
if (PyDataType_HASFIELDS(new)) {
PyObject *newfields;
PyObject *key, *value;
PyObject *newvalue;
PyObject *old;
PyArray_Descr *newdescr;
Py_ssize_t pos = 0;
int len, i;
newfields = PyDict_New();
/* make new dictionary with replaced PyArray_Descr Objects */
while (PyDict_Next(self->fields, &pos, &key, &value)) {
if NPY_TITLE_KEY(key, value) {
continue;
}
if (!PyUString_Check(key) || !PyTuple_Check(value) ||
((len=PyTuple_GET_SIZE(value)) < 2)) {
continue;
}
old = PyTuple_GET_ITEM(value, 0);
if (!PyArray_DescrCheck(old)) {
continue;
}
newdescr = PyArray_DescrNewByteorder(
(PyArray_Descr *)old, newendian);
if (newdescr == NULL) {
Py_DECREF(newfields); Py_DECREF(new);
return NULL;
}
newvalue = PyTuple_New(len);
PyTuple_SET_ITEM(newvalue, 0, (PyObject *)newdescr);
for (i = 1; i < len; i++) {
old = PyTuple_GET_ITEM(value, i);
Py_INCREF(old);
PyTuple_SET_ITEM(newvalue, i, old);
}
PyDict_SetItem(newfields, key, newvalue);
Py_DECREF(newvalue);
}
Py_DECREF(new->fields);
new->fields = newfields;
}
if (PyDataType_HASSUBARRAY(new)) {
Py_DECREF(new->subarray->base);
new->subarray->base = PyArray_DescrNewByteorder(
self->subarray->base, newendian);
}
return new;
}
static PyObject *
arraydescr_newbyteorder(PyArray_Descr *self, PyObject *args)
{
char endian=NPY_SWAP;
if (!PyArg_ParseTuple(args, "|O&:newbyteorder", PyArray_ByteorderConverter,
&endian)) {
return NULL;
}
return (PyObject *)PyArray_DescrNewByteorder(self, endian);
}
static PyMethodDef arraydescr_methods[] = {
/* for pickling */
{"__reduce__",
(PyCFunction)arraydescr_reduce,
METH_VARARGS, NULL},
{"__setstate__",
(PyCFunction)arraydescr_setstate,
METH_VARARGS, NULL},
{"newbyteorder",
(PyCFunction)arraydescr_newbyteorder,
METH_VARARGS, NULL},
{NULL, NULL, 0, NULL} /* sentinel */
};
/*
* Checks whether the structured data type in 'dtype'
* has a simple layout, where all the fields are in order,
* and follow each other with no alignment padding.
*
* When this returns true, the dtype can be reconstructed
* from a list of the field names and dtypes with no additional
* dtype parameters.
*
* Returns 1 if it has a simple layout, 0 otherwise.
*/
NPY_NO_EXPORT int
is_dtype_struct_simple_unaligned_layout(PyArray_Descr *dtype)
{
PyObject *names, *fields, *key, *tup, *title;
Py_ssize_t i, names_size;
PyArray_Descr *fld_dtype;
int fld_offset;
npy_intp total_offset;
/* Get some properties from the dtype */
names = dtype->names;
names_size = PyTuple_GET_SIZE(names);
fields = dtype->fields;
/* Start at offset zero */
total_offset = 0;
for (i = 0; i < names_size; ++i) {
key = PyTuple_GET_ITEM(names, i);
if (key == NULL) {
return 0;
}
tup = PyDict_GetItem(fields, key);
if (tup == NULL) {
return 0;
}
if (!PyArg_ParseTuple(tup, "Oi|O", &fld_dtype, &fld_offset, &title)) {
PyErr_Clear();
return 0;
}
/* If this field doesn't follow the pattern, not a simple layout */
if (total_offset != fld_offset) {
return 0;
}
/* Get the next offset */
total_offset += fld_dtype->elsize;
}
/*
* If the itemsize doesn't match the final offset, it's
* not a simple layout.
*/
if (total_offset != dtype->elsize) {
return 0;
}
/* It's a simple layout, since all the above tests passed */
return 1;
}
/*
* The general dtype repr function.
*/
static PyObject *
arraydescr_repr(PyArray_Descr *dtype)
{
PyObject *_numpy_dtype;
PyObject *res;
_numpy_dtype = PyImport_ImportModule("numpy.core._dtype");
if (_numpy_dtype == NULL) {
return NULL;
}
res = PyObject_CallMethod(_numpy_dtype, "__repr__", "O", dtype);
Py_DECREF(_numpy_dtype);
return res;
}
/*
* The general dtype str function.
*/
static PyObject *
arraydescr_str(PyArray_Descr *dtype)
{
PyObject *_numpy_dtype;
PyObject *res;
_numpy_dtype = PyImport_ImportModule("numpy.core._dtype");
if (_numpy_dtype == NULL) {
return NULL;
}
res = PyObject_CallMethod(_numpy_dtype, "__str__", "O", dtype);
Py_DECREF(_numpy_dtype);
return res;
}
static PyObject *
arraydescr_richcompare(PyArray_Descr *self, PyObject *other, int cmp_op)
{
PyArray_Descr *new = NULL;
PyObject *result = Py_NotImplemented;
if (!PyArray_DescrCheck(other)) {
if (PyArray_DescrConverter(other, &new) == NPY_FAIL) {
return NULL;
}
}
else {
new = (PyArray_Descr *)other;
Py_INCREF(new);
}
switch (cmp_op) {
case Py_LT:
if (!PyArray_EquivTypes(self, new) && PyArray_CanCastTo(self, new)) {
result = Py_True;
}
else {
result = Py_False;
}
break;
case Py_LE:
if (PyArray_CanCastTo(self, new)) {
result = Py_True;
}
else {
result = Py_False;
}
break;
case Py_EQ:
if (PyArray_EquivTypes(self, new)) {
result = Py_True;
}
else {
result = Py_False;
}
break;
case Py_NE:
if (PyArray_EquivTypes(self, new))
result = Py_False;
else
result = Py_True;
break;
case Py_GT:
if (!PyArray_EquivTypes(self, new) && PyArray_CanCastTo(new, self)) {
result = Py_True;
}
else {
result = Py_False;
}
break;
case Py_GE:
if (PyArray_CanCastTo(new, self)) {
result = Py_True;
}
else {
result = Py_False;
}
break;
default:
result = Py_NotImplemented;
}
Py_XDECREF(new);
Py_INCREF(result);
return result;
}
static int
descr_nonzero(PyObject *self)
{
/* `bool(np.dtype(...)) == True` for all dtypes. Needed to override default
* nonzero implementation, which checks if `len(object) > 0`. */
return 1;
}
static PyNumberMethods descr_as_number = {
(binaryfunc)0, /* nb_add */
(binaryfunc)0, /* nb_subtract */
(binaryfunc)0, /* nb_multiply */
#if defined(NPY_PY3K)
#else
(binaryfunc)0, /* nb_divide */
#endif
(binaryfunc)0, /* nb_remainder */
(binaryfunc)0, /* nb_divmod */
(ternaryfunc)0, /* nb_power */
(unaryfunc)0, /* nb_negative */
(unaryfunc)0, /* nb_positive */
(unaryfunc)0, /* nb_absolute */
(inquiry)descr_nonzero, /* nb_nonzero */
};
/*************************************************************************
**************** Implement Mapping Protocol ***************************
*************************************************************************/
static Py_ssize_t
descr_length(PyObject *self0)
{
PyArray_Descr *self = (PyArray_Descr *)self0;
if (PyDataType_HASFIELDS(self)) {
return PyTuple_GET_SIZE(self->names);
}
else {
return 0;
}
}
static PyObject *
descr_repeat(PyObject *self, Py_ssize_t length)
{
PyObject *tup;
PyArray_Descr *new;
if (length < 0) {
return PyErr_Format(PyExc_ValueError,
"Array length must be >= 0, not %"NPY_INTP_FMT, (npy_intp)length);
}
tup = Py_BuildValue("O" NPY_SSIZE_T_PYFMT, self, length);
if (tup == NULL) {
return NULL;
}
PyArray_DescrConverter(tup, &new);
Py_DECREF(tup);
return (PyObject *)new;
}
static int
_check_has_fields(PyArray_Descr *self)
{
if (!PyDataType_HASFIELDS(self)) {
PyObject *astr = arraydescr_str(self);
if (astr == NULL) {
return -1;
}
#if defined(NPY_PY3K)
{
PyObject *bstr = PyUnicode_AsUnicodeEscapeString(astr);
Py_DECREF(astr);
astr = bstr;
}
#endif
PyErr_Format(PyExc_KeyError,
"There are no fields in dtype %s.", PyBytes_AsString(astr));
Py_DECREF(astr);
return -1;
}
else {
return 0;
}
}
static PyObject *
_subscript_by_name(PyArray_Descr *self, PyObject *op)
{
PyObject *obj = PyDict_GetItem(self->fields, op);
PyObject *descr;
PyObject *s;
if (obj == NULL) {
if (PyUnicode_Check(op)) {
s = PyUnicode_AsUnicodeEscapeString(op);
}
else {
s = op;
}
PyErr_Format(PyExc_KeyError,
"Field named \'%s\' not found.", PyBytes_AsString(s));
if (s != op) {
Py_DECREF(s);
}
return NULL;
}
descr = PyTuple_GET_ITEM(obj, 0);
Py_INCREF(descr);
return descr;
}
static PyObject *
_subscript_by_index(PyArray_Descr *self, Py_ssize_t i)
{
PyObject *name = PySequence_GetItem(self->names, i);
PyObject *ret;
if (name == NULL) {
PyErr_Format(PyExc_IndexError,
"Field index %zd out of range.", i);
return NULL;
}
ret = _subscript_by_name(self, name);
Py_DECREF(name);
return ret;
}
static PyObject *
descr_subscript(PyArray_Descr *self, PyObject *op)
{
if (_check_has_fields(self) < 0) {
return NULL;
}
if (PyBaseString_Check(op)) {
return _subscript_by_name(self, op);
}
else {
Py_ssize_t i = PyArray_PyIntAsIntp(op);
if (error_converting(i)) {
/* if converting to an int gives a type error, adjust the message */
PyObject *err = PyErr_Occurred();
if (PyErr_GivenExceptionMatches(err, PyExc_TypeError)) {
PyErr_SetString(PyExc_TypeError,
"Field key must be an integer, string, or unicode.");
}
return NULL;
}
return _subscript_by_index(self, i);
}
}
static PySequenceMethods descr_as_sequence = {
(lenfunc) descr_length, /* sq_length */
(binaryfunc) NULL, /* sq_concat */
(ssizeargfunc) descr_repeat, /* sq_repeat */
(ssizeargfunc) NULL, /* sq_item */
(ssizessizeargfunc) NULL, /* sq_slice */
(ssizeobjargproc) NULL, /* sq_ass_item */
(ssizessizeobjargproc) NULL, /* sq_ass_slice */
(objobjproc) NULL, /* sq_contains */
(binaryfunc) NULL, /* sq_inplace_concat */
(ssizeargfunc) NULL, /* sq_inplace_repeat */
};
static PyMappingMethods descr_as_mapping = {
descr_length, /* mp_length*/
(binaryfunc)descr_subscript, /* mp_subscript*/
(objobjargproc)NULL, /* mp_ass_subscript*/
};
/****************** End of Mapping Protocol ******************************/
NPY_NO_EXPORT PyTypeObject PyArrayDescr_Type = {
#if defined(NPY_PY3K)
PyVarObject_HEAD_INIT(NULL, 0)
#else
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
#endif
"numpy.dtype", /* tp_name */
sizeof(PyArray_Descr), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)arraydescr_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
#if defined(NPY_PY3K)
(void *)0, /* tp_reserved */
#else
0, /* tp_compare */
#endif
(reprfunc)arraydescr_repr, /* tp_repr */
&descr_as_number, /* tp_as_number */
&descr_as_sequence, /* tp_as_sequence */
&descr_as_mapping, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
(reprfunc)arraydescr_str, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
(richcmpfunc)arraydescr_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
arraydescr_methods, /* tp_methods */
arraydescr_members, /* tp_members */
arraydescr_getsets, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
arraydescr_new, /* tp_new */
0, /* tp_free */
0, /* tp_is_gc */
0, /* tp_bases */
0, /* tp_mro */
0, /* tp_cache */
0, /* tp_subclasses */
0, /* tp_weaklist */
0, /* tp_del */
0, /* tp_version_tag */
};