diff options
author | Mark Wiebe <mwiebe@enthought.com> | 2011-05-20 15:41:27 -0500 |
---|---|---|
committer | Mark Wiebe <mwiebe@enthought.com> | 2011-05-20 15:41:27 -0500 |
commit | 8727a806b34a412bce086df9288ee18cdafe365c (patch) | |
tree | b9544eade4bf46ae2b720f727c44f9866815c9a1 | |
parent | 6ab9c73290659a94b56a7127805bddd25d734901 (diff) | |
download | numpy-8727a806b34a412bce086df9288ee18cdafe365c.tar.gz |
ENH: Remove 'den' datetime metadata, move datetime_data to a C function
The 'den' metadata was always 1, except during construction, so there
is no reason for it to exist. The variable is kept in the struct
for 1.6 ABI compatibility, however.
The datetime_data function used ctypes. Moving the function to C is
no more difficult, and a bit cleaner in my opinion.
-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) |