Skip to content

Hash changed signature in Python 3.3. #3743

Closed

Description

@charris
ERROR: test_record_no_hash (test_multiarray.TestRecord)
----------------------------------------------------------------------
Traceback (most recent call last):
   File
"X:\Python33\lib\site-packages\numpy\core\tests\test_multiarray.py",
line 2464, in test_record_no_hash
     self.assertRaises(TypeError, hash, a[0])
   File "X:\Python33\lib\unittest\case.py", line 570, in assertRaises
     return context.handle('assertRaises', callableObj, args, kwargs)
   File "X:\Python33\lib\unittest\case.py", line 135, in handle
     callable_obj(*args, **kwargs)
   File "X:\Python33\lib\unittest\case.py", line 153, in __exit__
     self.obj_name))
TypeError: unhashable type: 'writeable void-scalar'

I [Christolph] could reproduce the weirdness outside the testsuite on 64 bit Python
3.2, 3.3 and 3.4. It looks like the interpreter gets corrupted.

It could be due to numpy 1.8 using a wrong signature for the hash
function [1] on win-amd64-py3.x. Since Python 3.2 the return value must
be of type Py_hash_t (defined as Py_ssize_t) [2], not C long as
for Python <= 3.1.

[1]
https://github.com/numpy/numpy/blob/master/numpy/core/src/multiarray/scalartypes.c.src#L3038
[2] http://docs.python.org/3.2/c-api/typeobj.html#PyTypeObject.tp_hash

Activity

  1. charris commented on Sep 14, 2013

    @charris
    MemberAuthor

    Fix also needs backport to 1.7.x.

  2. njsmith commented on Sep 14, 2013

    @njsmith
    Member

    Can we call it npy_hash_t? We've run into trouble before with our py
    version compatibility hacks colliding with other people's py version
    compatibility hacks. Otherwise looks good to me.
    On 14 Sep 2013 17:24, "Christoph Gohlke" [email protected] wrote:

    This patch from last night fixes the test error and all other tests also
    pass. The changes should only affect win-amd64-py3.x, where sizeof(Py_hash_t)
    != sizeof(long). I can work on a PR and backport later. Any comments in
    the meantime?

    diff --git a/numpy/core/include/numpy/npy_3kcompat.h b/numpy/core/include/numpy/npy_3kcompat.hindex 4b9d0ea..4686e29 100644--- a/numpy/core/include/numpy/npy_3kcompat.h+++ b/numpy/core/include/numpy/npy_3kcompat.h@@ -397,6 +397,16 @@ simple_capsule_dtor(void *ptr)

    #endif
    +/_+ * Py_hash_t is only defined on Python > 3.1+ */+#if PY_VERSION_HEX < 0x03020000+typedef long Py_hash_t;+#define NPY_SIZEOF_HASH_T NPY_SIZEOF_LONG+#else+#define NPY_SIZEOF_HASH_T NPY_SIZEOF_INTP+#endif+
    #ifdef __cplusplus
    }
    #endifdiff --git a/numpy/core/src/multiarray/hashdescr.c b/numpy/core/src/multiarray/hashdescr.cindex bff2664..b088852 100644--- a/numpy/core/src/multiarray/hashdescr.c+++ b/numpy/core/src/multiarray/hashdescr.c@@ -243,7 +243,7 @@ static int array_descr_walk(PyArray_Descr descr, PyObject l)
    /

    • Return 0 if successfull
      _/-static int _PyArray_DescrHashImp(PyArray_Descr *descr, long *hash)+static int PyArray_DescrHashImp(PyArray_Descr *descr, Py_hash_t *hash)
      {
      PyObject *l, *tl, *item;
      Py_ssize_t i;@@ -296,12 +296,12 @@ clean_l:
      return -1;
      }
      -NPY_NO_EXPORT long+NPY_NO_EXPORT Py_hash_t
      PyArray_DescrHash(PyObject
      odescr)
      {
      PyArray_Descr *descr;
      int st;- long hash;+ Py_hash_t hash;

      if (!PyArray_DescrCheck(odescr)) {
      PyErr_SetString(PyExc_ValueError,diff --git a/numpy/core/src/multiarray/hashdescr.h b/numpy/core/src/multiarray/hashdescr.hindex af0ec13..82fadbe 100644--- a/numpy/core/src/multiarray/hashdescr.h+++ b/numpy/core/src/multiarray/hashdescr.h@@ -1,7 +1,7 @@
      #ifndef NPY_HASHDESCR_H
      #define NPY_HASHDESCR_H
      -NPY_NO_EXPORT long+NPY_NO_EXPORT Py_hash_t
      PyArray_DescrHash(PyObject* odescr);

      #endifdiff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.srcindex f64b7e9..a56d1f1 100644--- a/numpy/core/src/multiarray/scalartypes.c.src+++ b/numpy/core/src/multiarray/scalartypes.c.src@@ -2864,10 +2864,10 @@ void_arrtype_new(PyTypeObject *type, PyObject *args, PyObject *NPY_UNUSED(kwds))

    • #lname = bool, ubyte, ushort#

    • #name = Bool,UByte, UShort#
      _/-static long+static Py_hash_t
      @Lname@_arrtype_hash(PyObject *obj)
      {- return (long)(((Py@name@ScalarObject *)obj)->obval);+ return (Py_hash_t)(((Py@name@ScalarObject )obj)->obval);
      }
      /_end repeat
      /
      @@ -2875,10 +2875,10 @@ static long

    • #lname = byte, short, uint, ulong#

    • #name = Byte, Short, UInt, ULong#
      _/-static long+static Py_hash_t
      @Lname@_arrtype_hash(PyObject *obj)
      {- long x = (long)(((Py@name@ScalarObject *)obj)->obval);+ Py_hash_t x = (Py_hash_t)(((Py@name@ScalarObject )obj)->obval);
      if (x == -1) {
      x = -2;
      }@@ -2887,10 +2887,10 @@ static long
      /_end repeat
      /

      #if (NPY_SIZEOF_INT != NPY_SIZEOF_LONG) || defined(NPY_PY3K)-static long+static Py_hash_t
      int_arrtype_hash(PyObject *obj)
      {- long x = (long)(((PyIntScalarObject *)obj)->obval);+ Py_hash_t x = (Py_hash_t)(((PyIntScalarObject *)obj)->obval);
      if (x == -1) {
      x = -2;
      }@@ -2903,16 +2903,16 @@ int_arrtype_hash(PyObject *obj)

    • #Char = ,U#

    • #ext = && (x >= LONG_MIN),#
      /-#if NPY_SIZEOF_LONG != NPY_SIZEOF_LONGLONG+#if NPY_SIZEOF_HASH_T != NPY_SIZEOF_LONGLONG
      /
      we assume NPY_SIZEOF_LONGLONG=2*NPY_SIZEOF_LONG */-static long+static Py_hash_t
      @char@longlong_arrtype_hash(PyObject *obj)
      {- long y;+ Py_hash_t y;
      npy_@char@longlong x = (((Py@Char@LongLongScalarObject *)obj)->obval);

      if ((x <= LONG_MAX)@ext@) {- y = (long) x;+ y = (Py_hash_t) x;
      }
      else {
      union Mask {@@ -2930,10 +2930,10 @@ static long
      }
      #else
      -static long+static Py_hash_t
      @char@longlong_arrtype_hash(PyObject *obj)
      {- long x = (long)(((Py@Char@LongLongScalarObject *)obj)->obval);+ Py_hash_t x = (Py_hash_t)(((Py@Char@LongLongScalarObject *)obj)->obval);
      if (x == -1) {
      x = -2;
      }@@ -2948,25 +2948,25 @@ static long

    • #lname = datetime, timedelta#

    • #name = Datetime, Timedelta#
      */-#if NPY_SIZEOF_LONG==NPY_SIZEOF_DATETIME-static long+#if NPY_SIZEOF_HASH_T==NPY_SIZEOF_DATETIME+static Py_hash_t
      @Lname@_arrtype_hash(PyObject *obj)
      {- long x = (long)(((Py@name@ScalarObject *)obj)->obval);+ Py_hash_t x = (Py_hash_t)(((Py@name@ScalarObject *)obj)->obval);
      if (x == -1) {
      x = -2;
      }
      return x;
      }
      #elif NPY_SIZEOF_LONGLONG==NPY_SIZEOF_DATETIME-static long+static Py_hash_t
      @Lname@_arrtype_hash(PyObject *obj)
      {- long y;+ Py_hash_t y;
      npy_longlong x = (((Py@name@ScalarObject *)obj)->obval);

      if ((x <= LONG_MAX)) {- y = (long) x;+ y = (Py_hash_t) x;
      }
      else {
      union Mask {@@ -2993,17 +2993,17 @@ static long

    • #lname = float, longdouble#

    • #name = Float, LongDouble#
      */-static long+static Py_hash_t
      @Lname@_arrtype_hash(PyObject *obj)
      {
      return _Py_HashDouble((double) ((Py@name@ScalarObject *)obj)->obval);
      }

      /* borrowed from complex_hash _/-static long+static Py_hash_t
      c@lname@_arrtype_hash(PyObject *obj)
      {- long hashreal, hashimag, combined;+ Py_hash_t hashreal, hashimag, combined;
      hashreal = _Py_HashDouble((double)
      (((PyC@name@ScalarObject *)obj)->obval).real);
      @@ -3023,13 +3023,13 @@ c@lname@_arrtype_hash(PyObject *obj)
      }
      /end repeat/
      -static long+static Py_hash_t
      half_arrtype_hash(PyObject *obj)
      {
      return Py_HashDouble(npy_half_to_double(((PyHalfScalarObject *)obj)->obval));
      }
      -static long+static Py_hash_t
      object_arrtype_hash(PyObject *obj)
      {
      return PyObject_Hash(((PyObjectScalarObject *)obj)->obval);@@ -3038,14 +3038,14 @@ object_arrtype_hash(PyObject *obj)
      /
      we used to just hash the pointer /
      /
      now use tuplehash algorithm using voidtype_item to get the object
      /-static long+static Py_hash_t
      void_arrtype_hash(PyObject *obj)
      {- register long x, y;+ register Py_hash_t x, y;
      register Py_ssize_t len, n;
      register PyVoidScalarObject *p;
      register PyObject *element;- long mult = 1000003L;+ Py_hash_t mult = 1000003L;
      x = 0x345678L;
      p = (PyVoidScalarObject *)obj;
      /
      Cannot hash mutable void scalars */

    —
    Reply to this email directly or view it on GitHubhttps://github.com//issues/3743#issuecomment-24450191
    .

  3. charris commented on Sep 14, 2013

    @charris
    MemberAuthor

    We should really get rid of the register modifier. Nathaniel's suggestion is good. Otherwise looks good to me. Let's hope there were no hash functions without hash in the name ;)

  4. added a commit that references this issue on Sep 14, 2013
  5. charris commented on Sep 15, 2013

    @charris
    MemberAuthor

    Closed by 4d53e8b.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions