Author: Armin Rigo <[email protected]>
Branch: py3.6
Changeset: r97957:5b94ae9bcf6d
Date: 2019-11-05 17:12 +0100
http://bitbucket.org/pypy/pypy/changeset/5b94ae9bcf6d/

Log:    add the 3.6.1 new functions PySlice_Unpack() and
        PySlice_AdjustIndices()

diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -1528,6 +1528,7 @@
                          source_dir / "object.c",
                          source_dir / "typeobject.c",
                          source_dir / "tupleobject.c",
+                         source_dir / "sliceobject.c",
                          ]
 
 def build_eci(code, use_micronumpy=False, translating=False):
diff --git a/pypy/module/cpyext/include/sliceobject.h 
b/pypy/module/cpyext/include/sliceobject.h
--- a/pypy/module/cpyext/include/sliceobject.h
+++ b/pypy/module/cpyext/include/sliceobject.h
@@ -18,7 +18,10 @@
 } PySliceObject;
 
 #define PySlice_Check(op) ((op)->ob_type == &PySlice_Type)
-    
+
+PyAPI_FUNC(Py_ssize_t) PySlice_AdjustIndices(Py_ssize_t length,
+                         Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t step);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/pypy/module/cpyext/sliceobject.py 
b/pypy/module/cpyext/sliceobject.py
--- a/pypy/module/cpyext/sliceobject.py
+++ b/pypy/module/cpyext/sliceobject.py
@@ -1,3 +1,4 @@
+import sys
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import (
     cpython_api, cpython_struct, bootstrap_function, build_type_checkers,
@@ -5,7 +6,7 @@
 from pypy.module.cpyext.pyobject import (
     decref, PyObject, make_ref, make_typedescr)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
-from pypy.interpreter.error import OperationError
+from pypy.interpreter.error import oefmt
 from pypy.objspace.std.sliceobject import W_SliceObject
 
 # Slice objects directly expose their members as PyObject.
@@ -75,7 +76,7 @@
 
     Returns 0 on success and -1 on error with exception set."""
     if not isinstance(w_slice, W_SliceObject):
-        PyErr_BadInternalCall(space)
+        raise PyErr_BadInternalCall(space)
     start_p[0], stop_p[0], step_p[0], slicelength_p[0] = \
             w_slice.indices4(space, length)
     return 0
@@ -96,7 +97,37 @@
     incorporate the source of PySlice_GetIndicesEx(), suitably renamed,
     in the source of your extension."""
     if not isinstance(w_slice, W_SliceObject):
-        PyErr_BadInternalCall(space)
+        raise PyErr_BadInternalCall(space)
     start_p[0], stop_p[0], step_p[0] = \
             w_slice.indices3(space, length)
     return 0
+
+@cpython_api([PyObject, Py_ssize_tP, Py_ssize_tP, Py_ssize_tP],
+             rffi.INT_real, error=-1)
+def PySlice_Unpack(space, w_slice, start_p, stop_p, step_p):
+    if not isinstance(w_slice, W_SliceObject):
+        raise PyErr_BadInternalCall(space)
+
+    if space.is_none(w_slice.w_step):
+        step = 1
+    else:
+        step = W_SliceObject.eval_slice_index(space, w_slice.w_step)
+        if step == 0:
+            raise oefmt(space.w_ValueError, "slice step cannot be zero")
+        if step < -sys.maxint:
+            step = -sys.maxint
+    step_p[0] = step
+
+    if space.is_none(w_slice.w_start):
+        start = sys.maxint if step < 0 else 0
+    else:
+        start = W_SliceObject.eval_slice_index(space, w_slice.w_start)
+    start_p[0] = start
+
+    if space.is_none(w_slice.w_stop):
+        stop = -sys.maxint-1 if step < 0 else sys.maxint
+    else:
+        stop = W_SliceObject.eval_slice_index(space, w_slice.w_stop)
+    stop_p[0] = stop
+
+    return 0
diff --git a/pypy/module/cpyext/src/sliceobject.c 
b/pypy/module/cpyext/src/sliceobject.c
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/src/sliceobject.c
@@ -0,0 +1,43 @@
+#include "Python.h"
+
+Py_ssize_t
+PySlice_AdjustIndices(Py_ssize_t length,
+                      Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t step)
+{
+    /* this is harder to get right than you might think */
+
+    assert(step != 0);
+    assert(step >= -PY_SSIZE_T_MAX);
+
+    if (*start < 0) {
+        *start += length;
+        if (*start < 0) {
+            *start = (step < 0) ? -1 : 0;
+        }
+    }
+    else if (*start >= length) {
+        *start = (step < 0) ? length - 1 : length;
+    }
+
+    if (*stop < 0) {
+        *stop += length;
+        if (*stop < 0) {
+            *stop = (step < 0) ? -1 : 0;
+        }
+    }
+    else if (*stop >= length) {
+        *stop = (step < 0) ? length - 1 : length;
+    }
+
+    if (step < 0) {
+        if (*stop < *start) {
+            return (*start - *stop - 1) / (-step) + 1;
+        }
+    }
+    else {
+        if (*start < *stop) {
+            return (*stop - *start - 1) / step + 1;
+        }
+    }
+    return 0;
+}
diff --git a/pypy/module/cpyext/test/test_sliceobject.py 
b/pypy/module/cpyext/test/test_sliceobject.py
--- a/pypy/module/cpyext/test/test_sliceobject.py
+++ b/pypy/module/cpyext/test/test_sliceobject.py
@@ -1,3 +1,4 @@
+import sys
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.module.cpyext.test.test_api import BaseApiTest
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
@@ -84,3 +85,47 @@
             ])
         s = slice(10, 20, 30)
         assert module.check(s)
+
+    def test_Unpack(self):
+        from sys import maxsize as M
+        module = self.import_extension('foo', [
+            ("check", "METH_O",
+             """
+                 Py_ssize_t start, stop, step;
+                 if (PySlice_Unpack(args, &start, &stop, &step) != 0)
+                     return NULL;
+                 return Py_BuildValue("nnn", start, stop, step);
+             """),
+            ])
+        assert module.check(slice(10, 20, 1)) == (10, 20, 1)
+        assert module.check(slice(None, 20, 1)) == (0, 20, 1)
+        assert module.check(slice(10, None, 3)) == (10, M, 3)
+        assert module.check(slice(10, 20, None)) == (10, 20, 1)
+        assert module.check(slice(20, 5, 1)) == (20, 5, 1)
+        assert module.check(slice(None, None, None)) == (0, M, 1)
+
+        assert module.check(slice(20, 10, -1)) == (20, 10, -1)
+        assert module.check(slice(None, 20, -1)) == (M, 20, -1)
+        assert module.check(slice(10, None, -1)) == (10, -M-1, -1)
+
+        assert module.check(slice(M*2, M*3, 1)) == (M, M, 1)
+        assert module.check(slice(M*2, -123, 1)) == (M, -123, 1)
+        assert module.check(slice(-M*2, -M*3, 1)) == (-M-1, -M-1, 1)
+        assert module.check(slice(-M*2, 123, -2)) == (-M-1, 123, -2)
+
+        with raises(ValueError):
+            module.check(slice(2, 3, 0))
+        assert module.check(slice(2, 3, -M-1)) == (2, 3, -M)
+        assert module.check(slice(2, 3, -M-10)) == (2, 3, -M)
+        assert module.check(slice(2, 3, M+10)) == (2, 3, M)
+
+    def test_AdjustIndices(self):
+        module = self.import_extension('foo', [
+            ("check", "METH_NOARGS",
+             """
+                 Py_ssize_t start = -35, stop = 99999, step = 10, result;
+                 result = PySlice_AdjustIndices(100, &start, &stop, step);
+                 return Py_BuildValue("nnnn", result, start, stop, step);
+             """),
+            ])
+        assert module.check() == (4, 65, 100, 10)
diff --git a/pypy/objspace/std/sliceobject.py b/pypy/objspace/std/sliceobject.py
--- a/pypy/objspace/std/sliceobject.py
+++ b/pypy/objspace/std/sliceobject.py
@@ -172,6 +172,11 @@
         return app_indices(space, self.w_start, self.w_stop,
                            self.w_step, w_length)
 
+    @staticmethod
+    def eval_slice_index(space, w_int):
+        """Helper for cpyext"""
+        return _eval_slice_index(space, w_int)
+
 
 def slicewprop(name):
     def fget(space, w_obj):
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to