summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharles Harris <charlesr.harris@gmail.com>2015-04-04 18:03:40 -0400
committerCharles Harris <charlesr.harris@gmail.com>2015-04-04 18:03:40 -0400
commite05b75880911172ac2ca4a589aeca28c0ac38729 (patch)
treecdad45dbf12ce88fbfdef9dbae343c425c56d01e
parentc3cd4bfa57992bb804f5efc41e93e5354d7dfef3 (diff)
parentcca2c1a4fecfa5533b5579204c5f28a12c5b078a (diff)
downloadnumpy-e05b75880911172ac2ca4a589aeca28c0ac38729.tar.gz
Merge pull request #5343 from pitrou/cache_dtype_hash
Fix #5339: cache dtype.__hash__.
-rw-r--r--doc/release/1.10.0-notes.rst3
-rw-r--r--numpy/core/include/numpy/ndarraytypes.h4
-rw-r--r--numpy/core/include/numpy/npy_3kcompat.h13
-rw-r--r--numpy/core/include/numpy/npy_common.h13
-rw-r--r--numpy/core/src/multiarray/arraytypes.c.src4
-rw-r--r--numpy/core/src/multiarray/descriptor.c5
-rw-r--r--numpy/core/src/multiarray/hashdescr.c11
-rw-r--r--numpy/core/tests/test_dtype.py15
8 files changed, 50 insertions, 18 deletions
diff --git a/doc/release/1.10.0-notes.rst b/doc/release/1.10.0-notes.rst
index d5cd99203..a7c0e2852 100644
--- a/doc/release/1.10.0-notes.rst
+++ b/doc/release/1.10.0-notes.rst
@@ -61,6 +61,9 @@ C API
The changes to *swapaxes* also apply to the *PyArray_SwapAxes* C function,
which now returns a view in all cases.
+The dtype structure (PyArray_Descr) has a new member at the end to cache
+its hash value. This shouldn't affect any well-written applications.
+
recarray field return types
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Previously the returned types for recarray fields accessed by attribute and by
diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h
index 78f79d5fe..edae27c72 100644
--- a/numpy/core/include/numpy/ndarraytypes.h
+++ b/numpy/core/include/numpy/ndarraytypes.h
@@ -619,6 +619,10 @@ typedef struct _PyArray_Descr {
* for NumPy 1.7.0.
*/
NpyAuxData *c_metadata;
+ /* Cached hash value (-1 if not yet computed).
+ * This was added for NumPy 2.0.0.
+ */
+ npy_hash_t hash;
} PyArray_Descr;
typedef struct _arr_descr {
diff --git a/numpy/core/include/numpy/npy_3kcompat.h b/numpy/core/include/numpy/npy_3kcompat.h
index 8a9109c5c..ef5b5694c 100644
--- a/numpy/core/include/numpy/npy_3kcompat.h
+++ b/numpy/core/include/numpy/npy_3kcompat.h
@@ -486,19 +486,6 @@ NpyCapsule_Check(PyObject *ptr)
#endif
-/*
- * Hash value compatibility.
- * As of Python 3.2 hash values are of type Py_hash_t.
- * Previous versions use C long.
- */
-#if PY_VERSION_HEX < 0x03020000
-typedef long npy_hash_t;
-#define NPY_SIZEOF_HASH_T NPY_SIZEOF_LONG
-#else
-typedef Py_hash_t npy_hash_t;
-#define NPY_SIZEOF_HASH_T NPY_SIZEOF_INTP
-#endif
-
#ifdef __cplusplus
}
#endif
diff --git a/numpy/core/include/numpy/npy_common.h b/numpy/core/include/numpy/npy_common.h
index 92b03d20c..eff5dd339 100644
--- a/numpy/core/include/numpy/npy_common.h
+++ b/numpy/core/include/numpy/npy_common.h
@@ -317,6 +317,19 @@ typedef float npy_float;
typedef double npy_double;
/*
+ * Hash value compatibility.
+ * As of Python 3.2 hash values are of type Py_hash_t.
+ * Previous versions use C long.
+ */
+#if PY_VERSION_HEX < 0x03020000
+typedef long npy_hash_t;
+#define NPY_SIZEOF_HASH_T NPY_SIZEOF_LONG
+#else
+typedef Py_hash_t npy_hash_t;
+#define NPY_SIZEOF_HASH_T NPY_SIZEOF_INTP
+#endif
+
+/*
* Disabling C99 complex usage: a lot of C code in numpy/scipy rely on being
* able to do .real/.imag. Will have to convert code first.
*/
diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src
index 05b843fc4..8287c2268 100644
--- a/numpy/core/src/multiarray/arraytypes.c.src
+++ b/numpy/core/src/multiarray/arraytypes.c.src
@@ -4031,6 +4031,8 @@ static PyArray_Descr @from@_Descr = {
NULL,
/* c_metadata */
NULL,
+ /* hash */
+ -1,
};
/**end repeat**/
@@ -4172,6 +4174,8 @@ NPY_NO_EXPORT PyArray_Descr @from@_Descr = {
NULL,
/* c_metadata */
NULL,
+ /* hash */
+ -1,
};
/**end repeat**/
diff --git a/numpy/core/src/multiarray/descriptor.c b/numpy/core/src/multiarray/descriptor.c
index 0993190b7..bbcd5da36 100644
--- a/numpy/core/src/multiarray/descriptor.c
+++ b/numpy/core/src/multiarray/descriptor.c
@@ -1591,6 +1591,7 @@ PyArray_DescrNew(PyArray_Descr *base)
}
Py_XINCREF(newdescr->typeobj);
Py_XINCREF(newdescr->metadata);
+ newdescr->hash = -1;
return newdescr;
}
@@ -1994,6 +1995,8 @@ arraydescr_names_set(PyArray_Descr *self, PyObject *val)
return -1;
}
}
+ /* Invalidate cached hash value */
+ self->hash = -1;
/* Update dictionary keys in fields */
new_names = PySequence_Tuple(val);
new_fields = PyDict_New();
@@ -2443,6 +2446,8 @@ arraydescr_setstate(PyArray_Descr *self, PyObject *args)
version);
return NULL;
}
+ /* Invalidate cached hash value */
+ self->hash = -1;
if (version == 1 || version == 0) {
if (fields != Py_None) {
diff --git a/numpy/core/src/multiarray/hashdescr.c b/numpy/core/src/multiarray/hashdescr.c
index 29d69fddb..fa5fb95cd 100644
--- a/numpy/core/src/multiarray/hashdescr.c
+++ b/numpy/core/src/multiarray/hashdescr.c
@@ -301,7 +301,6 @@ PyArray_DescrHash(PyObject* odescr)
{
PyArray_Descr *descr;
int st;
- npy_hash_t hash;
if (!PyArray_DescrCheck(odescr)) {
PyErr_SetString(PyExc_ValueError,
@@ -310,10 +309,12 @@ PyArray_DescrHash(PyObject* odescr)
}
descr = (PyArray_Descr*)odescr;
- st = _PyArray_DescrHashImp(descr, &hash);
- if (st) {
- return -1;
+ if (descr->hash == -1) {
+ st = _PyArray_DescrHashImp(descr, &descr->hash);
+ if (st) {
+ return -1;
+ }
}
- return hash;
+ return descr->hash;
}
diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py
index 3a255b038..852660432 100644
--- a/numpy/core/tests/test_dtype.py
+++ b/numpy/core/tests/test_dtype.py
@@ -125,6 +125,21 @@ class TestRecord(TestCase):
'titles': ['RRed pixel', 'Blue pixel']})
assert_dtype_not_equal(a, b)
+ def test_mutate(self):
+ # Mutating a dtype should reset the cached hash value
+ a = np.dtype([('yo', np.int)])
+ b = np.dtype([('yo', np.int)])
+ c = np.dtype([('ye', np.int)])
+ assert_dtype_equal(a, b)
+ assert_dtype_not_equal(a, c)
+ a.names = ['ye']
+ assert_dtype_equal(a, c)
+ assert_dtype_not_equal(a, b)
+ state = b.__reduce__()[2]
+ a.__setstate__(state)
+ assert_dtype_equal(a, b)
+ assert_dtype_not_equal(a, c)
+
def test_not_lists(self):
"""Test if an appropriate exception is raised when passing bad values to
the dtype constructor.