Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions Lib/test/test_capi/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,11 +415,6 @@ def __setitem__(self, index, value):
with self.assertRaises(TypeError):
_testcapi.sequence_set_slice(None, 1, 3, 'xy')

mapping = {1: 'a', 2: 'b', 3: 'c'}
with self.assertRaises(TypeError):
_testcapi.sequence_set_slice(mapping, 1, 3, 'xy')
self.assertEqual(mapping, {1: 'a', 2: 'b', 3: 'c'})

def test_sequence_del_slice(self):
# Correct case:
data = [1, 2, 3, 4, 5]
Expand Down Expand Up @@ -455,7 +450,7 @@ def __delitem__(self, index):
_testcapi.sequence_del_slice(None, 1, 3)

mapping = {1: 'a', 2: 'b', 3: 'c'}
with self.assertRaises(TypeError):
with self.assertRaises(KeyError):
_testcapi.sequence_del_slice(mapping, 1, 3)
self.assertEqual(mapping, {1: 'a', 2: 'b', 3: 'c'})

Expand Down
6 changes: 2 additions & 4 deletions Lib/test/test_slice.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,8 @@ def test_repr(self):
self.assertEqual(repr(slice(1, 2, 3)), "slice(1, 2, 3)")

def test_hash(self):
# Verify clearing of SF bug #800796
self.assertRaises(TypeError, hash, slice(5))
with self.assertRaises(TypeError):
slice(5).__hash__()
self.assertEqual(hash(slice(5)), slice(5).__hash__())
self.assertNotEqual(slice(5), slice(6))

def test_cmp(self):
s1 = slice(1, 2, 3)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Make the slice object hashable.
38 changes: 37 additions & 1 deletion Objects/sliceobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,42 @@ slice_traverse(PySliceObject *v, visitproc visit, void *arg)
return 0;
}

/* code based on tuplehash() of Objects/tupleobject.c */
#if SIZEOF_PY_UHASH_T > 4
#define _PyHASH_XXPRIME_1 ((Py_uhash_t)11400714785074694791ULL)
#define _PyHASH_XXPRIME_2 ((Py_uhash_t)14029467366897019727ULL)
#define _PyHASH_XXPRIME_5 ((Py_uhash_t)2870177450012600261ULL)
#define _PyHASH_XXROTATE(x) ((x << 31) | (x >> 33)) /* Rotate left 31 bits */
#else
#define _PyHASH_XXPRIME_1 ((Py_uhash_t)2654435761UL)
#define _PyHASH_XXPRIME_2 ((Py_uhash_t)2246822519UL)
#define _PyHASH_XXPRIME_5 ((Py_uhash_t)374761393UL)
#define _PyHASH_XXROTATE(x) ((x << 13) | (x >> 19)) /* Rotate left 13 bits */
#endif

static Py_hash_t
slicehash(PySliceObject *v)
{
Py_uhash_t acc = _PyHASH_XXPRIME_5;
#define _PyHASH_SLICE_PART(com) { \
Py_uhash_t lane = PyObject_Hash(v->com); \
if(lane == (Py_uhash_t)-1) { \
return -1; \
} \
acc += lane * _PyHASH_XXPRIME_2; \
acc = _PyHASH_XXROTATE(acc); \
acc *= _PyHASH_XXPRIME_1; \
}
_PyHASH_SLICE_PART(start);
_PyHASH_SLICE_PART(stop);
_PyHASH_SLICE_PART(step);
#undef _PyHASH_SLICE_PART
if(acc == (Py_uhash_t)-1) {
return 1546275796;
}
return acc;
}

PyTypeObject PySlice_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"slice", /* Name of this type */
Expand All @@ -642,7 +678,7 @@ PyTypeObject PySlice_Type = {
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
PyObject_HashNotImplemented, /* tp_hash */
(hashfunc)slicehash, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
Expand Down
2 changes: 1 addition & 1 deletion Objects/tupleobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ tuplerepr(PyTupleObject *v)

/* Hash for tuples. This is a slightly simplified version of the xxHash
non-cryptographic hash:
- we do not use any parallellism, there is only 1 accumulator.
- we do not use any parallelism, there is only 1 accumulator.
- we drop the final mixing since this is just a permutation of the
output space: it does not help against collisions.
- at the end, we mangle the length with a single constant.
Expand Down