summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wiebe <mwiebe@enthought.com>2011-05-20 15:41:27 -0500
committerMark Wiebe <mwiebe@enthought.com>2011-05-20 15:41:27 -0500
commit8727a806b34a412bce086df9288ee18cdafe365c (patch)
treeb9544eade4bf46ae2b720f727c44f9866815c9a1
parent6ab9c73290659a94b56a7127805bddd25d734901 (diff)
downloadnumpy-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.h9
-rw-r--r--numpy/core/numerictypes.py5
-rw-r--r--numpy/core/src/multiarray/_datetime.h4
-rw-r--r--numpy/core/src/multiarray/arraytypes.c.src1
-rw-r--r--numpy/core/src/multiarray/convert_datatype.c1
-rw-r--r--numpy/core/src/multiarray/datetime.c115
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c63
-rw-r--r--numpy/core/tests/test_datetime.py7
-rw-r--r--numpy/lib/tests/test_type_check.py8
-rw-r--r--numpy/lib/type_check.py46
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)