From 61c8048bfde17519b10b6bd7be0a37650e454bcb Mon Sep 17 00:00:00 2001 From: furkanonder Date: Mon, 23 Jan 2023 23:03:14 +0300 Subject: [PATCH 01/10] fix the typo in the tuplehash func's comment --- Objects/tupleobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index e1b9953226c0d7..7d6d0e1bea249e 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -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. From ef22a9236526adc3e5bbd03f661814bb0cce61c0 Mon Sep 17 00:00:00 2001 From: furkanonder Date: Mon, 23 Jan 2023 23:04:16 +0300 Subject: [PATCH 02/10] make hashable the slice --- Lib/test/test_slice.py | 6 ++---- Objects/sliceobject.c | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_slice.py b/Lib/test/test_slice.py index 03fde3275e1475..70a4ad7896aa03 100644 --- a/Lib/test/test_slice.py +++ b/Lib/test/test_slice.py @@ -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) diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c index 5694bd9c661fa5..5d2e6ad522bcf2 100644 --- a/Objects/sliceobject.c +++ b/Objects/sliceobject.c @@ -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 */ @@ -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 */ From 886bffff8b3bbe7c7ba47c201d20507f5506907e Mon Sep 17 00:00:00 2001 From: furkanonder Date: Sun, 12 Feb 2023 01:45:13 +0300 Subject: [PATCH 03/10] fix failed slice's test cases --- Lib/test/test_capi/test_misc.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index dace37c362e569..5ee1f23ef477a1 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -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] @@ -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'}) From 093bf38474239adde1138fb90d6c7836cdfb80c3 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sat, 11 Feb 2023 23:14:08 +0000 Subject: [PATCH 04/10] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2023-02-11-23-14-06.gh-issue-84783._P5sMa.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-11-23-14-06.gh-issue-84783._P5sMa.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-11-23-14-06.gh-issue-84783._P5sMa.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-11-23-14-06.gh-issue-84783._P5sMa.rst new file mode 100644 index 00000000000000..e1c851a0825a7f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-11-23-14-06.gh-issue-84783._P5sMa.rst @@ -0,0 +1 @@ +Make the slice object hashable. From 159f3f2115cc88ba4ea7915c95b14ff839e8e4f6 Mon Sep 17 00:00:00 2001 From: furkanonder Date: Sun, 12 Feb 2023 02:32:59 +0300 Subject: [PATCH 05/10] Add version changed to slice's doc --- Doc/library/functions.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 658d6768457d16..8ec42cc9ba7873 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1635,6 +1635,8 @@ are always available. They are listed here in alphabetical order. example: ``a[start:stop:step]`` or ``a[start:stop, i]``. See :func:`itertools.islice` for an alternate version that returns an iterator. + .. versionchanged:: 3.10 + The slice object can be :term:`hashable`. .. function:: sorted(iterable, /, *, key=None, reverse=False) From 0c78cfc962e1d01ac2a01cb607b0775c914b6ed1 Mon Sep 17 00:00:00 2001 From: Owain Davies <116417456+OTheDev@users.noreply.github.com> Date: Sun, 12 Feb 2023 21:36:32 +0700 Subject: [PATCH 06/10] Update test_doctest.py --- Lib/test/test_doctest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index 65e215f1cdda4a..3491d4cdb1c18b 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -707,7 +707,7 @@ def non_Python_modules(): r""" >>> import builtins >>> tests = doctest.DocTestFinder().find(builtins) - >>> 825 < len(tests) < 845 # approximate number of objects with docstrings + >>> 830 < len(tests) < 850 # approximate number of objects with docstrings True >>> real_tests = [t for t in tests if len(t.examples) > 0] >>> len(real_tests) # objects that actually have doctests From cff08ddc407f6e948a1e2ed4fc41d80f73ba48c5 Mon Sep 17 00:00:00 2001 From: Furkan Onder Date: Sun, 12 Feb 2023 23:42:31 +0300 Subject: [PATCH 07/10] Update Doc/library/functions.rst Co-authored-by: Owain Davies <116417456+OTheDev@users.noreply.github.com> --- Doc/library/functions.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 8ec42cc9ba7873..b8aaa83f0c6427 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1635,7 +1635,9 @@ are always available. They are listed here in alphabetical order. example: ``a[start:stop:step]`` or ``a[start:stop, i]``. See :func:`itertools.islice` for an alternate version that returns an iterator. - .. versionchanged:: 3.10 + .. versionchanged:: 3.12 + Slice objects are now :term:`hashable` (provided :attr:`~slice.start`, + :attr:`~slice.stop`, and :attr:`~slice.step` are hashable). The slice object can be :term:`hashable`. .. function:: sorted(iterable, /, *, key=None, reverse=False) From f0302bf776ec804630c75f6daf50e888b863143b Mon Sep 17 00:00:00 2001 From: Furkan Onder Date: Sun, 12 Feb 2023 23:54:39 +0300 Subject: [PATCH 08/10] Update version changed to slice's doc --- Doc/library/functions.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index b8aaa83f0c6427..3ff28849025153 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1638,7 +1638,6 @@ are always available. They are listed here in alphabetical order. .. versionchanged:: 3.12 Slice objects are now :term:`hashable` (provided :attr:`~slice.start`, :attr:`~slice.stop`, and :attr:`~slice.step` are hashable). - The slice object can be :term:`hashable`. .. function:: sorted(iterable, /, *, key=None, reverse=False) From cbd15271020a756fcd0f321bfb8526f9fa9fcf56 Mon Sep 17 00:00:00 2001 From: furkanonder Date: Mon, 13 Feb 2023 01:58:44 +0300 Subject: [PATCH 09/10] Add more testcases for slice's hash --- Lib/test/test_slice.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Lib/test/test_slice.py b/Lib/test/test_slice.py index 70a4ad7896aa03..48d2305302df60 100644 --- a/Lib/test/test_slice.py +++ b/Lib/test/test_slice.py @@ -81,8 +81,17 @@ def test_repr(self): def test_hash(self): self.assertEqual(hash(slice(5)), slice(5).__hash__()) + self.assertEqual(hash(slice(1, 2)), slice(1, 2).__hash__()) + self.assertEqual(hash(slice(1, 2, 3)), slice(1, 2, 3).__hash__()) + self.assertEqual(hash((slice(4, 2), slice(2, 6))), (slice(4, 2), slice(2, 6)).__hash__()) self.assertNotEqual(slice(5), slice(6)) + with self.assertRaises(TypeError): + hash(slice(1, 2, [])) + + with self.assertRaises(TypeError): + hash(slice(4, {})) + def test_cmp(self): s1 = slice(1, 2, 3) s2 = slice(1, 2, 3) From 9f55e46dbe64036271338d7108ca76709fcbfc10 Mon Sep 17 00:00:00 2001 From: furkanonder Date: Mon, 13 Feb 2023 22:08:11 +0300 Subject: [PATCH 10/10] update testcases for slice's hash --- Lib/test/test_slice.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_slice.py b/Lib/test/test_slice.py index 48d2305302df60..c35a2293f790a2 100644 --- a/Lib/test/test_slice.py +++ b/Lib/test/test_slice.py @@ -83,7 +83,6 @@ def test_hash(self): self.assertEqual(hash(slice(5)), slice(5).__hash__()) self.assertEqual(hash(slice(1, 2)), slice(1, 2).__hash__()) self.assertEqual(hash(slice(1, 2, 3)), slice(1, 2, 3).__hash__()) - self.assertEqual(hash((slice(4, 2), slice(2, 6))), (slice(4, 2), slice(2, 6)).__hash__()) self.assertNotEqual(slice(5), slice(6)) with self.assertRaises(TypeError):