From 5b7c08c21449c407019f4484c91819a441e8bd9c Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Tue, 23 Aug 2016 18:15:35 +0100 Subject: Make Python builtin types hashable by default Default hash is the underlying C/C++ pointer. This matches up with testing for equivalence (Py_EQ in SwigPyObject_richcompare) which compares the pointers. --- Examples/test-suite/python/python_builtin_runme.py | 11 ++++++ Examples/test-suite/python_builtin.i | 14 ++++++-- Lib/python/builtin.swg | 40 +++++++++++++++------- Lib/python/pyhead.swg | 1 + Source/Modules/python.cxx | 3 +- 5 files changed, 53 insertions(+), 16 deletions(-) diff --git a/Examples/test-suite/python/python_builtin_runme.py b/Examples/test-suite/python/python_builtin_runme.py index c41dfc0b1..70990cbfd 100644 --- a/Examples/test-suite/python/python_builtin_runme.py +++ b/Examples/test-suite/python/python_builtin_runme.py @@ -1,6 +1,17 @@ from python_builtin import * if is_python_builtin(): + # Test 0 for default tp_hash + vs = ValueStruct(1234) + h = hash(vs) + d = dict() + d[h] = "hi" + if h not in d: + raise RuntimeError("h should be in d") + h2 = hash(ValueStruct.inout(vs)) + if h != h2: + raise RuntimeError("default tp_hash not working") + # Test 1 for tp_hash if hash(SimpleValue(222)) != 222: raise RuntimeError("tp_hash not working") diff --git a/Examples/test-suite/python_builtin.i b/Examples/test-suite/python_builtin.i index ac1ad9c2d..e2c453d54 100644 --- a/Examples/test-suite/python_builtin.i +++ b/Examples/test-suite/python_builtin.i @@ -10,6 +10,17 @@ bool is_python_builtin() { return false; } #endif %} +// Test 0 for default tp_hash +%inline %{ +struct ValueStruct { + int value; + ValueStruct(int value) : value(value) {} + static ValueStruct *inout(ValueStruct *v) { + return v; + } +}; +%} + // Test 1 for tp_hash #if defined(SWIGPYTHON_BUILTIN) %feature("python:tp_hash") SimpleValue "SimpleValueHashFunction" @@ -19,9 +30,6 @@ bool is_python_builtin() { return false; } struct SimpleValue { int value; SimpleValue(int value) : value(value) {} - static SimpleValue *inout(SimpleValue *sv) { - return sv; - } }; %} diff --git a/Lib/python/builtin.swg b/Lib/python/builtin.swg index 3dd5bbf95..1244dbbcc 100644 --- a/Lib/python/builtin.swg +++ b/Lib/python/builtin.swg @@ -187,19 +187,14 @@ wrapper##_closure(PyObject *a) { \ } #define SWIGPY_HASHFUNC_CLOSURE(wrapper) \ -SWIGINTERN long \ +SWIGINTERN Py_hash_t \ wrapper##_closure(PyObject *a) { \ PyObject *pyresult; \ - long result; \ + Py_hash_t result; \ pyresult = wrapper(a, NULL); \ if (!pyresult) \ return -1; \ - if (!PyLong_Check(pyresult)) \ - PyErr_Format(PyExc_TypeError, "Wrong type for hash function");\ - else \ - result = PyLong_AsLong(pyresult); \ - if (PyErr_Occurred()) \ - result = -1; \ + result = SWIG_PyNumber_AsPyHash(pyresult); \ Py_DECREF(pyresult); \ return result; \ } @@ -227,14 +222,35 @@ SwigPyBuiltin_BadInit(PyObject *self, PyObject *SWIGUNUSEDPARM(args), PyObject * } SWIGINTERN void -SwigPyBuiltin_BadDealloc(PyObject *pyobj) { - SwigPyObject *sobj; - sobj = (SwigPyObject *)pyobj; +SwigPyBuiltin_BadDealloc(PyObject *obj) { + SwigPyObject *sobj = (SwigPyObject *)obj; if (sobj->own) { - PyErr_Format(PyExc_TypeError, "Swig detected a memory leak in type '%.300s': no callable destructor found.", pyobj->ob_type->tp_name); + PyErr_Format(PyExc_TypeError, "Swig detected a memory leak in type '%.300s': no callable destructor found.", obj->ob_type->tp_name); } } +SWIGINTERN Py_hash_t +SwigPyObject_hash(PyObject *obj) { + SwigPyObject *sobj = (SwigPyObject *)obj; + void *ptr = sobj->ptr; + return (Py_hash_t)ptr; +} + +SWIGINTERN Py_hash_t +SWIG_PyNumber_AsPyHash(PyObject *obj) { + Py_hash_t result = -1; +#if PY_VERSION_HEX < 0x03020000 + if (PyLong_Check(obj)) + result = PyLong_AsLong(obj); +#else + if (PyNumber_Check(obj)) + result = PyNumber_AsSsize_t(obj, NULL); +#endif + else + PyErr_Format(PyExc_TypeError, "Wrong type for hash function"); + return result; +} + typedef struct { PyCFunction get; PyCFunction set; diff --git a/Lib/python/pyhead.swg b/Lib/python/pyhead.swg index 63df684b6..55eb95a6d 100644 --- a/Lib/python/pyhead.swg +++ b/Lib/python/pyhead.swg @@ -211,4 +211,5 @@ typedef destructor freefunc; #if PY_VERSION_HEX < 0x03020000 #define PyDescr_TYPE(x) (((PyDescrObject *)(x))->d_type) #define PyDescr_NAME(x) (((PyDescrObject *)(x))->d_name) +#define Py_hash_t long #endif diff --git a/Source/Modules/python.cxx b/Source/Modules/python.cxx index d31554071..40d3d9a8a 100644 --- a/Source/Modules/python.cxx +++ b/Source/Modules/python.cxx @@ -4057,6 +4057,7 @@ public: static String *tp_basicsize = NewStringf("sizeof(SwigPyObject)"); static String *tp_dictoffset_default = NewString("offsetof(SwigPyObject, dict)"); static String *tp_new = NewString("PyType_GenericNew"); + static String *tp_hash = NewString("SwigPyObject_hash"); String *tp_as_number = NewStringf("&%s_type.as_number", templ); String *tp_as_sequence = NewStringf("&%s_type.as_sequence", templ); String *tp_as_mapping = NewStringf("&%s_type.as_mapping", templ); @@ -4088,7 +4089,7 @@ public: printSlot(f, getSlot(n, "feature:python:tp_as_number", tp_as_number), "tp_as_number"); printSlot(f, getSlot(n, "feature:python:tp_as_sequence", tp_as_sequence), "tp_as_sequence"); printSlot(f, getSlot(n, "feature:python:tp_as_mapping", tp_as_mapping), "tp_as_mapping"); - printSlot(f, getSlot(n, "feature:python:tp_hash"), "tp_hash", "hashfunc"); + printSlot(f, getSlot(n, "feature:python:tp_hash", tp_hash), "tp_hash", "hashfunc"); printSlot(f, getSlot(n, "feature:python:tp_call"), "tp_call", "ternaryfunc"); printSlot(f, getSlot(n, "feature:python:tp_str"), "tp_str", "reprfunc"); printSlot(f, getSlot(n, "feature:python:tp_getattro"), "tp_getattro", "getattrofunc"); -- cgit v1.2.1