diff options
-rw-r--r-- | numpy/core/include/numpy/ndarraytypes.h | 9 | ||||
-rw-r--r-- | numpy/core/numerictypes.py | 5 | ||||
-rw-r--r-- | numpy/core/src/multiarray/_datetime.h | 4 | ||||
-rw-r--r-- | numpy/core/src/multiarray/arraytypes.c.src | 1 | ||||
-rw-r--r-- | numpy/core/src/multiarray/convert_datatype.c | 1 | ||||
-rw-r--r-- | numpy/core/src/multiarray/datetime.c | 115 | ||||
-rw-r--r-- | numpy/core/src/multiarray/multiarraymodule.c | 63 | ||||
-rw-r--r-- | numpy/core/tests/test_datetime.py | 7 | ||||
-rw-r--r-- | numpy/lib/tests/test_type_check.py | 8 | ||||
-rw-r--r-- | numpy/lib/type_check.py | 46 |
10 files changed, 126 insertions, 133 deletions
diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h index 2a21a779e..95bc78d47 100644 --- a/numpy/core/include/numpy/ndarraytypes.h +++ b/numpy/core/include/numpy/ndarraytypes.h @@ -676,10 +676,11 @@ typedef struct { typedef struct { NPY_DATETIMEUNIT base; int num; - int den; /* - * Converted to 1 on input for now -- an - * input-only mechanism - */ + /* + * 'den' is unused, kept here for ABI compatibility with 1.6. + * TODO: Remove for 2.0. + */ + int den; int events; } PyArray_DatetimeMetaData; diff --git a/numpy/core/numerictypes.py b/numpy/core/numerictypes.py index 08907541a..5ab874ccb 100644 --- a/numpy/core/numerictypes.py +++ b/numpy/core/numerictypes.py @@ -91,9 +91,10 @@ Exported symbols include: __all__ = ['sctypeDict', 'sctypeNA', 'typeDict', 'typeNA', 'sctypes', 'ScalarType', 'obj2sctype', 'cast', 'nbytes', 'sctype2char', 'maximum_sctype', 'issctype', 'typecodes', 'find_common_type', - 'issubdtype'] + 'issubdtype','datetime_data'] -from numpy.core.multiarray import typeinfo, ndarray, array, empty, dtype +from numpy.core.multiarray import typeinfo, ndarray, array, \ + empty, dtype, datetime_data import types as _types import sys diff --git a/numpy/core/src/multiarray/_datetime.h b/numpy/core/src/multiarray/_datetime.h index 331b02824..7f97fd416 100644 --- a/numpy/core/src/multiarray/_datetime.h +++ b/numpy/core/src/multiarray/_datetime.h @@ -58,7 +58,7 @@ parse_datetime_unit_from_string(char *str, Py_ssize_t len, char *metastr); */ NPY_NO_EXPORT int convert_datetime_divisor_to_multiple(PyArray_DatetimeMetaData *meta, - char *metastr); + int den, char *metastr); /* * Given an the CObject/Capsule datetime metadata object, @@ -68,7 +68,7 @@ NPY_NO_EXPORT PyObject * convert_datetime_metadata_to_tuple(PyArray_DatetimeMetaData *meta); /* - * Given a tuple representing datetime metadata tuple, + * Given a tuple representing datetime metadata, * returns a CObject/Capsule datetime metadata object. */ NPY_NO_EXPORT PyObject * diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src index fde95c4cb..6ca982345 100644 --- a/numpy/core/src/multiarray/arraytypes.c.src +++ b/numpy/core/src/multiarray/arraytypes.c.src @@ -3796,7 +3796,6 @@ _init_datetime_descr(PyArray_Descr *descr) dt_data = _pya_malloc(sizeof(PyArray_DatetimeMetaData)); dt_data->base = NPY_FR_us; dt_data->num = 1; - dt_data->den = 1; dt_data->events = 1; /* FIXME diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c index e20e8142d..b033e9587 100644 --- a/numpy/core/src/multiarray/convert_datatype.c +++ b/numpy/core/src/multiarray/convert_datatype.c @@ -738,7 +738,6 @@ PyArray_PromoteTypes(PyArray_Descr *type1, PyArray_Descr *type2) if (meta1->base == meta2->base && meta1->num == meta2->num && - meta1->den == meta2->den && meta1->events == meta2->events) { Py_INCREF(type1); return type1; diff --git a/numpy/core/src/multiarray/datetime.c b/numpy/core/src/multiarray/datetime.c index 8c35235c3..eb35ce88e 100644 --- a/numpy/core/src/multiarray/datetime.c +++ b/numpy/core/src/multiarray/datetime.c @@ -47,27 +47,6 @@ NPY_NO_EXPORT char *_datetime_strings[] = { NPY_STR_as }; -static NPY_DATETIMEUNIT _multiples_table[16][4] = { - {12, 52, 365}, /* NPY_FR_Y */ - {NPY_FR_M, NPY_FR_W, NPY_FR_D}, - {4, 30, 720}, /* NPY_FR_M */ - {NPY_FR_W, NPY_FR_D, NPY_FR_h}, - {5, 7, 168, 10080}, /* NPY_FR_W */ - {NPY_FR_B, NPY_FR_D, NPY_FR_h, NPY_FR_m}, - {24, 1440, 86400}, /* NPY_FR_B */ - {NPY_FR_h, NPY_FR_m, NPY_FR_s}, - {24, 1440, 86400}, /* NPY_FR_D */ - {NPY_FR_h, NPY_FR_m, NPY_FR_s}, - {60, 3600}, /* NPY_FR_h */ - {NPY_FR_m, NPY_FR_s}, - {60, 60000}, /* NPY_FR_m */ - {NPY_FR_s, NPY_FR_ms}, - {1000, 1000000}, /* >=NPY_FR_s */ - {0, 0} -}; - - - /* ==================================================== == Beginning of section borrowed from mx.DateTime == @@ -1006,6 +985,7 @@ parse_datetime_metacobj_from_metastr(char *metastr, Py_ssize_t len) { PyArray_DatetimeMetaData *dt_data; char *substr = metastr, *substrend = NULL; + int den = 1; dt_data = PyArray_malloc(sizeof(PyArray_DatetimeMetaData)); if (dt_data == NULL) { @@ -1016,7 +996,6 @@ parse_datetime_metacobj_from_metastr(char *metastr, Py_ssize_t len) if (len == 0) { dt_data->num = 1; dt_data->base = NPY_DATETIME_DEFAULTUNIT; - dt_data->den = 1; dt_data->events = 1; } else { @@ -1051,7 +1030,7 @@ parse_datetime_metacobj_from_metastr(char *metastr, Py_ssize_t len) /* Next comes an optional integer denominator */ if (*substr == '/') { substr++; - dt_data->den = (int)strtol(substr, &substrend, 10); + den = (int)strtol(substr, &substrend, 10); /* If the '/' exists, there must be a number followed by ']' */ if (substr == substrend || *substrend != ']') { goto bad_input; @@ -1059,7 +1038,6 @@ parse_datetime_metacobj_from_metastr(char *metastr, Py_ssize_t len) substr = substrend + 1; } else if (*substr == ']') { - dt_data->den = 1; substr++; } else { @@ -1082,8 +1060,9 @@ parse_datetime_metacobj_from_metastr(char *metastr, Py_ssize_t len) dt_data->events = 1; } - if (dt_data->den > 1) { - if (convert_datetime_divisor_to_multiple(dt_data, metastr) < 0) { + if (den != 1) { + if (convert_datetime_divisor_to_multiple( + dt_data, den, metastr) < 0) { goto error; } } @@ -1199,6 +1178,26 @@ parse_dtype_from_datetime_typestr(char *typestr, Py_ssize_t len) return dtype; } +static NPY_DATETIMEUNIT _multiples_table[16][4] = { + {12, 52, 365}, /* NPY_FR_Y */ + {NPY_FR_M, NPY_FR_W, NPY_FR_D}, + {4, 30, 720}, /* NPY_FR_M */ + {NPY_FR_W, NPY_FR_D, NPY_FR_h}, + {5, 7, 168, 10080}, /* NPY_FR_W */ + {NPY_FR_B, NPY_FR_D, NPY_FR_h, NPY_FR_m}, + {24, 1440, 86400}, /* NPY_FR_B */ + {NPY_FR_h, NPY_FR_m, NPY_FR_s}, + {24, 1440, 86400}, /* NPY_FR_D */ + {NPY_FR_h, NPY_FR_m, NPY_FR_s}, + {60, 3600}, /* NPY_FR_h */ + {NPY_FR_m, NPY_FR_s}, + {60, 60000}, /* NPY_FR_m */ + {NPY_FR_s, NPY_FR_ms}, + {1000, 1000000}, /* >=NPY_FR_s */ + {0, 0} +}; + + /* * Translate divisors into multiples of smaller units. @@ -1209,7 +1208,7 @@ parse_dtype_from_datetime_typestr(char *typestr, Py_ssize_t len) */ NPY_NO_EXPORT int convert_datetime_divisor_to_multiple(PyArray_DatetimeMetaData *meta, - char *metastr) + int den, char *metastr) { int i, num, ind; NPY_DATETIMEUNIT *totry; @@ -1242,8 +1241,8 @@ convert_datetime_divisor_to_multiple(PyArray_DatetimeMetaData *meta, } for (i = 0; i < num; i++) { - q = totry[i] / meta->den; - r = totry[i] % meta->den; + q = totry[i] / den; + r = totry[i] % den; if (r == 0) { break; } @@ -1252,17 +1251,16 @@ convert_datetime_divisor_to_multiple(PyArray_DatetimeMetaData *meta, if (metastr == NULL) { PyErr_Format(PyExc_ValueError, "divisor (%d) is not a multiple of a lower-unit " - "in datetime metadata", meta->den); + "in datetime metadata", den); } else { PyErr_Format(PyExc_ValueError, "divisor (%d) is not a multiple of a lower-unit " - "in datetime metadata \"%s\"", meta->den, metastr); + "in datetime metadata \"%s\"", den, metastr); } return -1; } meta->base = baseunit[i]; - meta->den = 1; meta->num *= q; return 0; @@ -1336,7 +1334,7 @@ convert_datetime_metadata_to_tuple(PyArray_DatetimeMetaData *meta) { PyObject *dt_tuple; - dt_tuple = PyTuple_New(4); + dt_tuple = PyTuple_New(3); if (dt_tuple == NULL) { return NULL; } @@ -1346,8 +1344,6 @@ convert_datetime_metadata_to_tuple(PyArray_DatetimeMetaData *meta) PyTuple_SET_ITEM(dt_tuple, 1, PyInt_FromLong(meta->num)); PyTuple_SET_ITEM(dt_tuple, 2, - PyInt_FromLong(meta->den)); - PyTuple_SET_ITEM(dt_tuple, 3, PyInt_FromLong(meta->events)); return dt_tuple; @@ -1358,7 +1354,22 @@ convert_datetime_metadata_tuple_to_metacobj(PyObject *tuple) { PyArray_DatetimeMetaData *dt_data; char *basestr = NULL; - Py_ssize_t len = 0; + Py_ssize_t len = 0, tuple_size; + int den = 1; + + if (!PyTuple_Check(tuple)) { + PyErr_SetString(PyExc_TypeError, + "Require tuple for tuple->metacobj conversion"); + return NULL; + } + + 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; + } if (PyBytes_AsStringAndSize(PyTuple_GET_ITEM(tuple, 0), &basestr, &len) < 0) { @@ -1372,13 +1383,26 @@ convert_datetime_metadata_tuple_to_metacobj(PyObject *tuple) return NULL; } - /* Assumes other objects are Python integers */ - dt_data->num = PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 1)); - dt_data->den = PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 2)); - dt_data->events = PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 3)); + /* Convert the values to longs */ + dt_data->num = PyInt_AsLong(PyTuple_GET_ITEM(tuple, 1)); + if (tuple_size == 3) { + dt_data->events = PyInt_AsLong(PyTuple_GET_ITEM(tuple, 2)); + } + else { + den = PyInt_AsLong(PyTuple_GET_ITEM(tuple, 2)); + dt_data->events = PyInt_AsLong(PyTuple_GET_ITEM(tuple, 3)); + } - if (dt_data->den > 1) { - if (convert_datetime_divisor_to_multiple(dt_data, NULL) < 0) { + if (dt_data->num <= 0 || dt_data->events <= 0 || den <= 0) { + PyErr_SetString(PyExc_TypeError, + "Invalid tuple values for " + "tuple->metacobj conversion"); + PyArray_free(dt_data); + return NULL; + } + + if (den != 1) { + if (convert_datetime_divisor_to_multiple(dt_data, den, NULL) < 0) { PyArray_free(dt_data); return NULL; } @@ -1398,7 +1422,7 @@ append_metastr_to_datetime_typestr(PyArray_Descr *self, PyObject *ret) { PyObject *tmp; PyObject *res; - int num, den, events; + int num, events; char *basestr; PyArray_DatetimeMetaData *dt_data; @@ -1409,7 +1433,6 @@ append_metastr_to_datetime_typestr(PyArray_Descr *self, PyObject *ret) } num = dt_data->num; - den = dt_data->den; events = dt_data->events; basestr = _datetime_strings[dt_data->base]; @@ -1419,10 +1442,6 @@ append_metastr_to_datetime_typestr(PyArray_Descr *self, PyObject *ret) else { tmp = PyUString_FromFormat("%d%s", num, basestr); } - if (den != 1) { - res = PyUString_FromFormat("/%d", den); - PyUString_ConcatAndDel(&tmp, res); - } res = PyUString_FromString("["); PyUString_ConcatAndDel(&res, tmp); diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c index f121f6147..e7e26e56e 100644 --- a/numpy/core/src/multiarray/multiarraymodule.c +++ b/numpy/core/src/multiarray/multiarraymodule.c @@ -44,6 +44,7 @@ NPY_NO_EXPORT int NPY_NUMUSERTYPES = 0; #include "numpymemoryview.h" #include "convert_datatype.h" #include "nditer_pywrap.h" +#include "_datetime.h" /* Only here for API compatibility */ NPY_NO_EXPORT PyTypeObject PyBigArray_Type; @@ -1401,35 +1402,31 @@ _equivalent_fields(PyObject *field1, PyObject *field2) { } /* - * compare the metadata for two date-times + * Compare the metadata for two date-times * return 1 if they are the same * or 0 if not */ static int -_equivalent_units(PyObject *meta1, PyObject *meta2) +_equivalent_datetime_units(PyArray_Descr *dtype1, PyArray_Descr *dtype2) { - PyObject *cobj1, *cobj2; PyArray_DatetimeMetaData *data1, *data2; - /* Same meta object */ - if (meta1 == meta2) { - return 1; + data1 = get_datetime_metadata_from_dtype(dtype1); + data2 = get_datetime_metadata_from_dtype(dtype1); + + /* If there's a metadata problem, it doesn't match */ + if (data1 == NULL || data2 == NULL) { + PyErr_Clear(); + return 0; } - cobj1 = PyDict_GetItemString(meta1, NPY_METADATA_DTSTR); - cobj2 = PyDict_GetItemString(meta2, NPY_METADATA_DTSTR); - if (cobj1 == cobj2) { + /* Same meta object */ + if (data1 == data2) { return 1; } -/* FIXME - * There is no err handling here. - */ - data1 = NpyCapsule_AsVoidPtr(cobj1); - data2 = NpyCapsule_AsVoidPtr(cobj2); return ((data1->base == data2->base) && (data1->num == data2->num) - && (data1->den == data2->den) && (data1->events == data2->events)); } @@ -1494,17 +1491,17 @@ PyArray_EquivTypes(PyArray_Descr *typ1, PyArray_Descr *typ2) return ((typenum1 == typenum2) && _equivalent_subarrays(typ1->subarray, typ2->subarray)); } - if (typenum1 == PyArray_VOID - || typenum2 == PyArray_VOID) { + if (typenum1 == NPY_VOID + || typenum2 == NPY_VOID) { return ((typenum1 == typenum2) && _equivalent_fields(typ1->fields, typ2->fields)); } - if (typenum1 == PyArray_DATETIME - || typenum1 == PyArray_DATETIME - || typenum2 == PyArray_TIMEDELTA - || typenum2 == PyArray_TIMEDELTA) { + if (typenum1 == NPY_DATETIME + || typenum1 == NPY_DATETIME + || typenum2 == NPY_TIMEDELTA + || typenum2 == NPY_TIMEDELTA) { return ((typenum1 == typenum2) - && _equivalent_units(typ1->metadata, typ2->metadata)); + && _equivalent_datetime_units(typ1, typ2)); } return typ1->kind == typ2->kind; } @@ -2844,6 +2841,25 @@ finish: return ret; } +static PyObject * +array_datetime_data(PyObject *NPY_UNUSED(dummy), PyObject *args) +{ + PyArray_Descr *dtype; + PyArray_DatetimeMetaData *meta; + + if(!PyArg_ParseTuple(args, "O&:datetime_data", + PyArray_DescrConverter, &dtype)) { + return NULL; + } + + meta = get_datetime_metadata_from_dtype(dtype); + if (meta == NULL) { + return NULL; + } + + return convert_datetime_metadata_to_tuple(meta); +} + #if !defined(NPY_PY3K) static PyObject * new_buffer(PyObject *NPY_UNUSED(dummy), PyObject *args) @@ -3464,6 +3480,9 @@ static struct PyMethodDef array_module_methods[] = { {"result_type", (PyCFunction)array_result_type, METH_VARARGS, NULL}, + {"datetime_data", + (PyCFunction)array_datetime_data, + METH_VARARGS, NULL}, #if !defined(NPY_PY3K) {"newbuffer", (PyCFunction)new_buffer, diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py index e8c1890e4..0728654ee 100644 --- a/numpy/core/tests/test_datetime.py +++ b/numpy/core/tests/test_datetime.py @@ -1,6 +1,7 @@ from os import path import numpy as np from numpy.testing import * +from numpy.compat import asbytes class TestDateTime(TestCase): def test_creation(self): @@ -92,5 +93,11 @@ class TestDateTime(TestCase): assert_equal(x[0].astype(np.int64), 322689600000000000) +class TestDateTimeData(TestCase): + + def test_basic(self): + a = np.array(['1980-03-23'], dtype=np.datetime64) + assert_equal(np.datetime_data(a.dtype), (asbytes('us'), 1, 1)) + if __name__ == "__main__": run_module_suite() diff --git a/numpy/lib/tests/test_type_check.py b/numpy/lib/tests/test_type_check.py index c8bc87c6e..0f8927614 100644 --- a/numpy/lib/tests/test_type_check.py +++ b/numpy/lib/tests/test_type_check.py @@ -382,13 +382,5 @@ class TestArrayConversion(TestCase): assert_equal(a.__class__,ndarray) assert_(issubdtype(a.dtype,float)) -class TestDateTimeData(object): - - @dec.skipif(not _HAS_CTYPE, "ctypes not available on this python installation") - def test_basic(self): - a = array(['1980-03-23'], dtype=datetime64) - assert_equal(datetime_data(a.dtype), (asbytes('us'), 1, 1, 1)) - - if __name__ == "__main__": run_module_suite() diff --git a/numpy/lib/type_check.py b/numpy/lib/type_check.py index 0ce851fe4..edea02b62 100644 --- a/numpy/lib/type_check.py +++ b/numpy/lib/type_check.py @@ -3,7 +3,7 @@ __all__ = ['iscomplexobj','isrealobj','imag','iscomplex', 'isreal','nan_to_num','real','real_if_close', 'typename','asfarray','mintypecode','asscalar', - 'common_type', 'datetime_data'] + 'common_type'] import numpy.core.numeric as _nx from numpy.core.numeric import asarray, asanyarray, array, isnan, \ @@ -601,47 +601,3 @@ def common_type(*arrays): else: return array_type[0][precision] -def datetime_data(dtype): - """Return (unit, numerator, denominator, events) from a datetime dtype - """ - try: - import ctypes - except ImportError: - raise RuntimeError("Cannot access date-time internals without ctypes installed") - - if dtype.kind not in ['m','M']: - raise ValueError("Not a date-time dtype") - - obj = dtype.metadata[METADATA_DTSTR] - class DATETIMEMETA(ctypes.Structure): - _fields_ = [('base', ctypes.c_int), - ('num', ctypes.c_int), - ('den', ctypes.c_int), - ('events', ctypes.c_int)] - - import sys - if sys.version_info[:2] >= (3, 0): - func = ctypes.pythonapi.PyCapsule_GetPointer - func.argtypes = [ctypes.py_object, ctypes.c_char_p] - func.restype = ctypes.c_void_p - result = func(ctypes.py_object(obj), ctypes.c_char_p(None)) - else: - func = ctypes.pythonapi.PyCObject_AsVoidPtr - func.argtypes = [ctypes.py_object] - func.restype = ctypes.c_void_p - result = func(ctypes.py_object(obj)) - result = ctypes.cast(ctypes.c_void_p(result), ctypes.POINTER(DATETIMEMETA)) - - struct = result[0] - base = struct.base - - # FIXME: This needs to be kept consistent with enum in ndarrayobject.h - from numpy.core.multiarray import DATETIMEUNITS - obj = ctypes.py_object(DATETIMEUNITS) - if sys.version_info[:2] >= (2,7): - result = func(obj, ctypes.c_char_p(None)) - else: - result = func(obj) - _unitnum2name = ctypes.cast(ctypes.c_void_p(result), ctypes.POINTER(ctypes.c_char_p)) - - return (_unitnum2name[base], struct.num, struct.den, struct.events) |