diff options
Diffstat (limited to 'numpy/core')
-rw-r--r-- | numpy/core/src/multiarray/_datetime.h | 39 | ||||
-rw-r--r-- | numpy/core/src/multiarray/datetime.c | 338 | ||||
-rw-r--r-- | numpy/core/src/multiarray/scalartypes.c.src | 81 |
3 files changed, 338 insertions, 120 deletions
diff --git a/numpy/core/src/multiarray/_datetime.h b/numpy/core/src/multiarray/_datetime.h index 48903ebcf..4c28c7840 100644 --- a/numpy/core/src/multiarray/_datetime.h +++ b/numpy/core/src/multiarray/_datetime.h @@ -50,6 +50,16 @@ convert_datetimestruct_to_datetime(PyArray_DatetimeMetaData *meta, npy_datetime *out); /* + * Parses the metadata string into the metadata C structure. + * + * Returns 0 on success, -1 on failure. + */ +NPY_NO_EXPORT int +parse_datetime_metadata_from_metastr(char *metastr, Py_ssize_t len, + PyArray_DatetimeMetaData *out_meta); + + +/* * This function returns a reference to a capsule * which contains the datetime metadata parsed from a metadata * string. 'metastr' should be NULL-terminated, and len should @@ -141,6 +151,15 @@ NPY_NO_EXPORT PyObject * convert_datetime_metadata_to_tuple(PyArray_DatetimeMetaData *meta); /* + * Converts a metadata tuple into a datetime metadata C struct. + * + * Returns 0 on success, -1 on failure. + */ +NPY_NO_EXPORT int +convert_datetime_metadata_tuple_to_datetime_metadata(PyObject *tuple, + PyArray_DatetimeMetaData *out_meta); + +/* * Given a tuple representing datetime metadata, * returns a capsule datetime metadata object. */ @@ -148,6 +167,16 @@ NPY_NO_EXPORT PyObject * convert_datetime_metadata_tuple_to_metacobj(PyObject *tuple); /* + * Converts an input object into datetime metadata. The input + * may be either a string or a tuple. + * + * Returns 0 on success, -1 on failure. + */ +NPY_NO_EXPORT int +convert_pyobject_to_datetime_metadata(PyObject *obj, + PyArray_DatetimeMetaData *out_meta); + +/* * 'ret' is a PyUString containing the datetime string, and this * function appends the metadata string to it. * @@ -193,6 +222,16 @@ convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj, npy_datetime *out); /* + * Converts a PyObject * into a timedelta, in any of the forms supported + * + * Returns -1 on error, 0 on success. + */ +NPY_NO_EXPORT int +convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj, + npy_timedelta *out); + + +/* * Converts a datetime into a PyObject *. * * For days or coarser, returns a datetime.date. diff --git a/numpy/core/src/multiarray/datetime.c b/numpy/core/src/multiarray/datetime.c index 40c696593..9dd1e0a38 100644 --- a/numpy/core/src/multiarray/datetime.c +++ b/numpy/core/src/multiarray/datetime.c @@ -1064,95 +1064,86 @@ get_datetime_metadata_from_dtype(PyArray_Descr *dtype) return meta; } -NPY_NO_EXPORT PyObject * -parse_datetime_metacobj_from_metastr(char *metastr, Py_ssize_t len) +/* + * Parses the metadata string into the metadata C structure. + * + * Returns 0 on success, -1 on failure. + */ +NPY_NO_EXPORT int +parse_datetime_metadata_from_metastr(char *metastr, Py_ssize_t len, + PyArray_DatetimeMetaData *out_meta) { - PyArray_DatetimeMetaData *dt_data; char *substr = metastr, *substrend = NULL; int den = 1; - dt_data = PyArray_malloc(sizeof(PyArray_DatetimeMetaData)); - if (dt_data == NULL) { - return PyErr_NoMemory(); + /* The metadata string must start with a '[' */ + if (len < 3 || *substr++ != '[') { + goto bad_input; } - /* If there's no metastr, use the default */ - if (len == 0) { - dt_data->num = 1; - dt_data->base = NPY_DATETIME_DEFAULTUNIT; - dt_data->events = 1; + /* First comes an optional integer multiplier */ + out_meta->num = (int)strtol(substr, &substrend, 10); + if (substr == substrend) { + out_meta->num = 1; } - else { - - /* The metadata string must start with a '[' */ - if (len < 3 || *substr++ != '[') { - goto bad_input; - } - - /* First comes an optional integer multiplier */ - dt_data->num = (int)strtol(substr, &substrend, 10); - if (substr == substrend) { - dt_data->num = 1; - } - substr = substrend; + substr = substrend; - /* Next comes the unit itself, followed by either '/' or ']' */ - substrend = substr; - while (*substrend != '\0' && *substrend != '/' && *substrend != ']') { - ++substrend; - } - if (*substrend == '\0') { - goto bad_input; - } - dt_data->base = parse_datetime_unit_from_string(substr, - substrend-substr, metastr); - if (dt_data->base == -1) { - goto error; - } - substr = substrend; + /* Next comes the unit itself, followed by either '/' or ']' */ + substrend = substr; + while (*substrend != '\0' && *substrend != '/' && *substrend != ']') { + ++substrend; + } + if (*substrend == '\0') { + goto bad_input; + } + out_meta->base = parse_datetime_unit_from_string(substr, + substrend-substr, metastr); + if (out_meta->base == -1) { + return -1; + } + substr = substrend; - /* Next comes an optional integer denominator */ - if (*substr == '/') { - substr++; - den = (int)strtol(substr, &substrend, 10); - /* If the '/' exists, there must be a number followed by ']' */ - if (substr == substrend || *substrend != ']') { - goto bad_input; - } - substr = substrend + 1; - } - else if (*substr == ']') { - substr++; - } - else { + /* Next comes an optional integer denominator */ + if (*substr == '/') { + substr++; + den = (int)strtol(substr, &substrend, 10); + /* If the '/' exists, there must be a number followed by ']' */ + if (substr == substrend || *substrend != ']') { goto bad_input; } + substr = substrend + 1; + } + else if (*substr == ']') { + substr++; + } + else { + goto bad_input; + } - /* Finally comes an optional number of events */ - if (substr[0] == '/' && substr[1] == '/') { - substr += 2; + /* Finally comes an optional number of events */ + if (substr[0] == '/' && substr[1] == '/') { + substr += 2; - dt_data->events = (int)strtol(substr, &substrend, 10); - if (substr == substrend || *substrend != '\0') { - goto bad_input; - } - } - else if (*substr != '\0') { + out_meta->events = (int)strtol(substr, &substrend, 10); + if (substr == substrend || *substrend != '\0') { goto bad_input; } - else { - dt_data->events = 1; - } + } + else if (*substr != '\0') { + goto bad_input; + } + else { + out_meta->events = 1; + } - if (den != 1) { - if (convert_datetime_divisor_to_multiple( - dt_data, den, metastr) < 0) { - goto error; - } + if (den != 1) { + if (convert_datetime_divisor_to_multiple( + out_meta, den, metastr) < 0) { + return -1; } } - return NpyCapsule_FromVoidPtr((void *)dt_data, simple_capsule_dtor); + return 0; bad_input: if (substr != metastr) { @@ -1165,9 +1156,34 @@ bad_input: "Invalid datetime metadata string \"%s\"", metastr); } -error: - PyArray_free(dt_data); - return NULL; + + return -1; +} + +NPY_NO_EXPORT PyObject * +parse_datetime_metacobj_from_metastr(char *metastr, Py_ssize_t len) +{ + PyArray_DatetimeMetaData *dt_data; + + dt_data = PyArray_malloc(sizeof(PyArray_DatetimeMetaData)); + if (dt_data == NULL) { + return PyErr_NoMemory(); + } + + /* If there's no metastr, use the default */ + if (len == 0) { + dt_data->num = 1; + dt_data->base = NPY_DATETIME_DEFAULTUNIT; + dt_data->events = 1; + } + else { + if (parse_datetime_metadata_from_metastr(metastr, len, dt_data) < 0) { + PyArray_free(dt_data); + return NULL; + } + } + + return NpyCapsule_FromVoidPtr((void *)dt_data, simple_capsule_dtor); } /* @@ -1960,8 +1976,9 @@ parse_datetime_unit_from_string(char *str, Py_ssize_t len, char *metastr) /* If nothing matched, it's an error */ if (metastr == NULL) { - PyErr_SetString(PyExc_TypeError, - "Invalid datetime unit in metadata"); + PyErr_Format(PyExc_TypeError, + "Invalid datetime unit \"%s\" in metadata", + str); } else { PyErr_Format(PyExc_TypeError, @@ -1992,10 +2009,15 @@ convert_datetime_metadata_to_tuple(PyArray_DatetimeMetaData *meta) return dt_tuple; } -NPY_NO_EXPORT PyObject * -convert_datetime_metadata_tuple_to_metacobj(PyObject *tuple) +/* + * Converts a metadata tuple into a datetime metadata C struct. + * + * Returns 0 on success, -1 on failure. + */ +NPY_NO_EXPORT int +convert_datetime_metadata_tuple_to_datetime_metadata(PyObject *tuple, + PyArray_DatetimeMetaData *out_meta) { - PyArray_DatetimeMetaData *dt_data; char *basestr = NULL; Py_ssize_t len = 0, tuple_size; int den = 1; @@ -2003,76 +2025,147 @@ convert_datetime_metadata_tuple_to_metacobj(PyObject *tuple) if (!PyTuple_Check(tuple)) { PyObject_Print(tuple, stderr, 0); PyErr_SetString(PyExc_TypeError, - "Require tuple for tuple->metacobj conversion"); - return NULL; + "Require tuple for tuple to NumPy datetime " + "metadata conversion"); + return -1; } tuple_size = PyTuple_GET_SIZE(tuple); if (tuple_size < 3 || tuple_size > 4) { PyErr_SetString(PyExc_TypeError, "Require tuple of size 3 or 4 for " - "tuple->metacobj conversion"); - return NULL; + "tuple to NumPy datetime metadata conversion"); + return -1; } if (PyBytes_AsStringAndSize(PyTuple_GET_ITEM(tuple, 0), &basestr, &len) < 0) { - return NULL; + return -1; } - dt_data = PyArray_malloc(sizeof(PyArray_DatetimeMetaData)); - dt_data->base = parse_datetime_unit_from_string(basestr, len, NULL); - if (dt_data->base == -1) { - PyArray_free(dt_data); - return NULL; + out_meta->base = parse_datetime_unit_from_string(basestr, len, NULL); + if (out_meta->base == -1) { + return -1; } /* Convert the values to longs */ - dt_data->num = PyInt_AsLong(PyTuple_GET_ITEM(tuple, 1)); - if (dt_data->num == -1 && PyErr_Occurred()) { - PyArray_free(dt_data); - return NULL; + out_meta->num = PyInt_AsLong(PyTuple_GET_ITEM(tuple, 1)); + if (out_meta->num == -1 && PyErr_Occurred()) { + return -1; } if (tuple_size == 3) { - dt_data->events = PyInt_AsLong(PyTuple_GET_ITEM(tuple, 2)); - if (dt_data->events == -1 && PyErr_Occurred()) { - PyArray_free(dt_data); - return NULL; + out_meta->events = PyInt_AsLong(PyTuple_GET_ITEM(tuple, 2)); + if (out_meta->events == -1 && PyErr_Occurred()) { + return -1; } } else { den = PyInt_AsLong(PyTuple_GET_ITEM(tuple, 2)); if (den == -1 && PyErr_Occurred()) { - PyArray_free(dt_data); - return NULL; + return -1; } - dt_data->events = PyInt_AsLong(PyTuple_GET_ITEM(tuple, 3)); - if (dt_data->events == -1 && PyErr_Occurred()) { - PyArray_free(dt_data); - return NULL; + out_meta->events = PyInt_AsLong(PyTuple_GET_ITEM(tuple, 3)); + if (out_meta->events == -1 && PyErr_Occurred()) { + return -1; } } - if (dt_data->num <= 0 || dt_data->events <= 0 || den <= 0) { + if (out_meta->num <= 0 || out_meta->events <= 0 || den <= 0) { PyErr_SetString(PyExc_TypeError, "Invalid tuple values for " - "tuple->metacobj conversion"); - PyArray_free(dt_data); - return NULL; + "tuple to NumPy datetime metadata conversion"); + return -1; } if (den != 1) { - if (convert_datetime_divisor_to_multiple(dt_data, den, NULL) < 0) { - PyArray_free(dt_data); - return NULL; + if (convert_datetime_divisor_to_multiple(out_meta, den, NULL) < 0) { + return -1; } } + return 0; +} + +/* + * Converts a metadata tuple into a datetime metadata capsule. + */ +NPY_NO_EXPORT PyObject * +convert_datetime_metadata_tuple_to_metacobj(PyObject *tuple) +{ + PyArray_DatetimeMetaData *dt_data; + + dt_data = PyArray_malloc(sizeof(PyArray_DatetimeMetaData)); + + if (convert_datetime_metadata_tuple_to_datetime_metadata( + tuple, dt_data) < 0) { + PyArray_free(dt_data); + return NULL; + } + return NpyCapsule_FromVoidPtr((void *)dt_data, simple_capsule_dtor); } /* + * Converts an input object into datetime metadata. The input + * may be either a string or a tuple. + * + * Returns 0 on success, -1 on failure. + */ +NPY_NO_EXPORT int +convert_pyobject_to_datetime_metadata(PyObject *obj, + PyArray_DatetimeMetaData *out_meta) +{ + PyObject *ascii = NULL; + char *str = NULL; + Py_ssize_t len = 0; + NPY_DATETIMEUNIT unit; + + if (PyTuple_Check(obj)) { + return convert_datetime_metadata_tuple_to_datetime_metadata(obj, + out_meta); + } + + /* Get an ASCII string */ + if (PyUnicode_Check(obj)) { + /* Allow unicode format strings: convert to bytes */ + ascii = PyUnicode_AsASCIIString(obj); + if (ascii == NULL) { + return -1; + } + } + else if (PyBytes_Check(obj)) { + ascii = obj; + Py_INCREF(ascii); + } + else { + PyErr_SetString(PyExc_TypeError, + "Invalid object for specifying NumPy datetime metadata"); + return -1; + } + + if (PyBytes_AsStringAndSize(ascii, &str, &len) < 0) { + return -1; + } + + /* First try for just the base unit */ + unit = parse_datetime_unit_from_string(str, len, NULL); + if (unit != -1) { + out_meta->num = 1; + out_meta->base = unit; + out_meta->events = 1; + + return 0; + } + /* If it failed, clear the error and use the main metastr parser */ + else { + PyErr_Clear(); + } + + return parse_datetime_metadata_from_metastr(str, len, out_meta); +} + +/* * 'ret' is a PyUString containing the datetime string, and this * function appends the metadata string to it. * @@ -2929,7 +3022,7 @@ convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj, return 0; } - /* TODO datetime64 scalars require conversion + /* TODO else if (PyArray_IsScalar(op, Datetime)) { } */ @@ -2957,6 +3050,31 @@ convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj, } /* + * Converts a PyObject * into a timedelta, in any of the forms supported + * + * Returns -1 on error, 0 on success. + */ +NPY_NO_EXPORT int +convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj, + npy_timedelta *out) +{ + /* Do no conversion on raw integers */ + if (PyInt_Check(obj)) { + *out = PyInt_AS_LONG(obj); + return 0; + } + else if (PyLong_Check(obj)) { + *out = PyLong_AsLongLong(obj); + return 0; + } + /* TODO: Finish this function */ + + PyErr_SetString(PyExc_ValueError, + "Could not convert object to NumPy timedelta"); + return -1; +} + +/* * Converts a datetime into a PyObject *. * * Not-a-time is returned as the string "NaT". diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src index 34962b744..b753609ba 100644 --- a/numpy/core/src/multiarray/scalartypes.c.src +++ b/numpy/core/src/multiarray/scalartypes.c.src @@ -21,6 +21,7 @@ #include "numpyos.h" #include "common.h" #include "scalartypes.h" +#include "_datetime.h" NPY_NO_EXPORT PyBoolScalarObject _PyArrayScalar_BoolValues[] = { {PyObject_HEAD_INIT(&PyBoolArrType_Type) 0}, @@ -2212,12 +2213,15 @@ object_arrtype_dealloc(PyObject *v) /**begin repeat * #name = byte, short, int, long, longlong, ubyte, ushort, uint, ulong, * ulonglong, half, float, double, longdouble, cfloat, cdouble, - * clongdouble, string, unicode, object, datetime, timedelta# + * clongdouble, string, unicode, object# + * #Name = Byte, Short, Int, Long, LongLong, UByte, UShort, UInt, ULong, + * ULongLong, Half, Float, Double, LongDouble, CFloat, CDouble, + * CLongDouble, String, Unicode, Object# * #TYPE = BYTE, SHORT, INT, LONG, LONGLONG, UBYTE, USHORT, UINT, ULONG, * ULONGLONG, HALF, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, - * CLONGDOUBLE, STRING, UNICODE, OBJECT, DATETIME, TIMEDELTA# - * #work = 0,0,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,z,z,0,0,0# - * #default = 0*17,1*2,2,0*2# + * CLONGDOUBLE, STRING, UNICODE, OBJECT# + * #work = 0,0,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,z,z,0# + * #default = 0*17,1*2,2# */ #define _NPY_UNUSED2_1 @@ -2248,18 +2252,22 @@ static PyObject * if (!PyArg_ParseTuple(args, "|O", &obj)) { return NULL; } - typecode = PyArray_DescrFromType(PyArray_@TYPE@); + typecode = PyArray_DescrFromType(NPY_@TYPE@); + if (typecode == NULL) { + return NULL; + } /* * typecode is new reference and stolen by * PyArray_FromAny but not PyArray_Scalar */ if (obj == NULL) { #if @default@ == 0 - char *mem = malloc(sizeof(npy_@name@)); - - memset(mem, 0, sizeof(npy_@name@)); - robj = PyArray_Scalar(mem, typecode, NULL); - free(mem); + robj = PyArray_Scalar(NULL, typecode, NULL); + if (robj == NULL) { + Py_DECREF(typecode); + return NULL; + } + memset(&((Py@Name@ScalarObject *)robj)->obval, 0, sizeof(npy_@name@)); #elif @default@ == 1 robj = PyArray_Scalar(NULL, typecode, NULL); #elif @default@ == 2 @@ -2339,6 +2347,59 @@ finish: #undef _WORK0 #undef _WORK +/**begin repeat + * #name = datetime, timedelta# + * #Name = Datetime, Timedelta# + * #TYPE = DATETIME, TIMEDELTA# + */ + +static PyObject * +@name@_arrtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *obj = NULL, *meta_obj = NULL; + Py@Name@ScalarObject *ret; + + if (!PyArg_ParseTuple(args, "|OO", &obj, &meta_obj)) { + return NULL; + } + + /* Allocate the return scalar */ + ret = (Py@Name@ScalarObject *)Py@Name@ArrType_Type.tp_alloc( + &Py@Name@ArrType_Type, 0); + if (ret == NULL) { + return NULL; + } + + /* Incorporate the metadata if its provided */ + if (meta_obj != NULL) { + /* Parse the provided metadata input */ + if (convert_pyobject_to_datetime_metadata(meta_obj, &ret->obmeta) + < 0) { + Py_DECREF(ret); + return NULL; + } + } + else { + ret->obmeta.base = NPY_DATETIME_DEFAULTUNIT; + ret->obmeta.num = 1; + ret->obmeta.events = 1; + } + + if (obj == NULL) { + ret->obval = 0; + } + else { + if (convert_pyobject_to_@name@(&ret->obmeta, obj, &ret->obval) + < 0) { + Py_DECREF(ret); + return NULL; + } + } + + return (PyObject *)ret; +} +/**end repeat**/ + /* bool->tp_new only returns Py_True or Py_False */ static PyObject * bool_arrtype_new(PyTypeObject *NPY_UNUSED(type), PyObject *args, PyObject *NPY_UNUSED(kwds)) |