summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFurkan Onder <furkanonder@protonmail.com>2023-02-19 00:22:02 +0000
committerGitHub <noreply@github.com>2023-02-18 18:22:02 -0600
commit61f1e67c6fcbf80eb9be2b75f7d62954e28c89e6 (patch)
tree74e32f1ed356fa4cb6cf6013c2cf2aca10046ff3
parentgh-97930: Apply changes from importlib_resources 5.12. (GH-102010) (diff)
downloadcpython-61f1e67c6fcbf80eb9be2b75f7d62954e28c89e6.tar.gz
cpython-61f1e67c6fcbf80eb9be2b75f7d62954e28c89e6.tar.bz2
cpython-61f1e67c6fcbf80eb9be2b75f7d62954e28c89e6.zip
GH-84783: Make the slice object hashable (GH-101264)
-rw-r--r--Doc/library/functions.rst3
-rw-r--r--Lib/test/test_capi/test_misc.py7
-rw-r--r--Lib/test/test_doctest.py2
-rw-r--r--Lib/test/test_slice.py12
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2023-02-11-23-14-06.gh-issue-84783._P5sMa.rst1
-rw-r--r--Objects/sliceobject.c38
-rw-r--r--Objects/tupleobject.c2
7 files changed, 53 insertions, 12 deletions
diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst
index 658d6768457..3ff28849025 100644
--- a/Doc/library/functions.rst
+++ b/Doc/library/functions.rst
@@ -1635,6 +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.12
+ Slice objects are now :term:`hashable` (provided :attr:`~slice.start`,
+ :attr:`~slice.stop`, and :attr:`~slice.step` are hashable).
.. function:: sorted(iterable, /, *, key=None, reverse=False)
diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py
index f26b4723d1e..ad099c61463 100644
--- a/Lib/test/test_capi/test_misc.py
+++ b/Lib/test/test_capi/test_misc.py
@@ -419,11 +419,6 @@ class CAPITest(unittest.TestCase):
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]
@@ -459,7 +454,7 @@ class CAPITest(unittest.TestCase):
_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'})
diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py
index 65e215f1cdd..3491d4cdb1c 100644
--- a/Lib/test/test_doctest.py
+++ b/Lib/test/test_doctest.py
@@ -707,7 +707,7 @@ plain ol' Python and is guaranteed to be available.
>>> 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
diff --git a/Lib/test/test_slice.py b/Lib/test/test_slice.py
index 03fde3275e1..c35a2293f79 100644
--- a/Lib/test/test_slice.py
+++ b/Lib/test/test_slice.py
@@ -80,10 +80,16 @@ class SliceTest(unittest.TestCase):
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))
+ 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.assertNotEqual(slice(5), slice(6))
+
+ with self.assertRaises(TypeError):
+ hash(slice(1, 2, []))
+
with self.assertRaises(TypeError):
- slice(5).__hash__()
+ hash(slice(4, {}))
def test_cmp(self):
s1 = slice(1, 2, 3)
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 00000000000..e1c851a0825
--- /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.
diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c
index 5694bd9c661..5d2e6ad522b 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 */
diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c
index e1b9953226c..7d6d0e1bea2 100644
--- a/Objects/tupleobject.c
+++ b/Objects/tupleobject.c
@@ -288,7 +288,7 @@ error:
/* 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.