Commit caa2b8b5 authored by Stefan Behnel's avatar Stefan Behnel

implement correct slicing for setslice and delslice

--HG--
rename : tests/compile/delslice.pyx => tests/run/delslice.py
parent c7d063cc
......@@ -3670,21 +3670,8 @@ class SliceIndexNode(ExprNode):
elif self.type is py_object_type:
code.globalstate.use_utility_code(
UtilityCode.load_cached("GetObjectSlice", "ObjectHandling.c"))
has_c_start, c_start, py_start = False, '0', 'NULL'
if self.start:
has_c_start = not self.start.type.is_pyobject
if has_c_start:
c_start = self.start.result()
else:
py_start = '&%s' % self.start.py_result()
has_c_stop, c_stop, py_stop = False, '0', 'NULL'
if self.stop:
has_c_stop = not self.stop.type.is_pyobject
if has_c_stop:
c_stop = self.stop.result()
else:
py_stop = '&%s' % self.stop.py_result()
py_slice = self.slice and '&%s' % self.slice.py_result() or 'NULL'
(has_c_start, has_c_stop, c_start, c_stop,
py_start, py_stop, py_slice) = self.get_slice_config()
code.putln(
"%s = __Pyx_PySequence_GetObjectSlice(%s, %s, %s, %s, %s, %s, %d, %d); %s" % (
result,
......@@ -3717,12 +3704,17 @@ class SliceIndexNode(ExprNode):
def generate_assignment_code(self, rhs, code):
self.generate_subexpr_evaluation_code(code)
if self.type.is_pyobject:
code.globalstate.use_utility_code(
UtilityCode.load_cached("SetObjectSlice", "ObjectHandling.c"))
(has_c_start, has_c_stop, c_start, c_stop,
py_start, py_stop, py_slice) = self.get_slice_config()
code.put_error_if_neg(self.pos,
"__Pyx_PySequence_SetSlice(%s, %s, %s, %s)" % (
"__Pyx_PySequence_SetObjectSlice(%s, %s, %s, %s, %s, %s, %s, %d, %d)" % (
self.base.py_result(),
self.start_code(),
self.stop_code(),
rhs.py_result()))
rhs.py_result(),
c_start, c_stop,
py_start, py_stop, py_slice,
has_c_start, has_c_stop))
else:
start_offset = ''
if self.start:
......@@ -3754,14 +3746,38 @@ class SliceIndexNode(ExprNode):
"Deleting slices is only supported for Python types, not '%s'." % self.type)
return
self.generate_subexpr_evaluation_code(code)
code.globalstate.use_utility_code(
UtilityCode.load_cached("SetObjectSlice", "ObjectHandling.c"))
(has_c_start, has_c_stop, c_start, c_stop,
py_start, py_stop, py_slice) = self.get_slice_config()
code.put_error_if_neg(self.pos,
"__Pyx_PySequence_DelSlice(%s, %s, %s)" % (
"__Pyx_PySequence_DelObjectSlice(%s, %s, %s, %s, %s, %s, %d, %d)" % (
self.base.py_result(),
self.start_code(),
self.stop_code()))
c_start, c_stop,
py_start, py_stop, py_slice,
has_c_start, has_c_stop))
self.generate_subexpr_disposal_code(code)
self.free_subexpr_temps(code)
def get_slice_config(self):
has_c_start, c_start, py_start = False, '0', 'NULL'
if self.start:
has_c_start = not self.start.type.is_pyobject
if has_c_start:
c_start = self.start.result()
else:
py_start = '&%s' % self.start.py_result()
has_c_stop, c_stop, py_stop = False, '0', 'NULL'
if self.stop:
has_c_stop = not self.stop.type.is_pyobject
if has_c_stop:
c_stop = self.stop.result()
else:
py_stop = '&%s' % self.stop.py_result()
py_slice = self.slice and '&%s' % self.slice.py_result() or 'NULL'
return (has_c_start, has_c_stop, c_start, c_stop,
py_start, py_stop, py_slice)
def generate_slice_guard_code(self, code, target_size):
if not self.base.type.is_array:
return
......
......@@ -543,6 +543,109 @@ static CYTHON_INLINE PyObject* __Pyx_PySequence_GetObjectSlice(
return NULL;
}
/////////////// SetObjectSlice.proto ///////////////
#define __Pyx_PySequence_DelObjectSlice(obj, cstart, cstop, py_start, py_stop, py_slice, has_cstart, has_cstop) \
__Pyx_PySequence_SetObjectSlice(obj, (PyObject*)NULL, cstart, cstop, py_start, py_stop, py_slice, has_cstart, has_cstop)
// we pass pointer addresses to show the C compiler what is NULL and what isn't
static CYTHON_INLINE int __Pyx_PySequence_SetObjectSlice(
PyObject* obj, PyObject* value, Py_ssize_t cstart, Py_ssize_t cstop,
PyObject** py_start, PyObject** py_stop, PyObject** py_slice,
int has_cstart, int has_cstop);
/////////////// SetObjectSlice ///////////////
static CYTHON_INLINE int __Pyx_PySequence_SetObjectSlice(
PyObject* obj, PyObject* value, Py_ssize_t cstart, Py_ssize_t cstop,
PyObject** _py_start, PyObject** _py_stop, PyObject** _py_slice,
int has_cstart, int has_cstop) {
PyMappingMethods* mp;
#if PY_MAJOR_VERSION < 3
PySequenceMethods* ms = Py_TYPE(obj)->tp_as_sequence;
if (likely(ms && ms->sq_ass_slice)) {
if (!has_cstart) {
if (_py_start) {
cstart = __Pyx_PyIndex_AsSsize_t(*_py_start);
if ((cstart == (Py_ssize_t)-1) && PyErr_Occurred()) return -1;
} else
cstart = 0;
}
if (!has_cstop) {
if (_py_stop) {
cstop = __Pyx_PyIndex_AsSsize_t(*_py_stop);
if ((cstop == (Py_ssize_t)-1) && PyErr_Occurred()) return -1;
} else
cstop = PY_SSIZE_T_MAX;
}
if (unlikely((cstart < 0) | (cstop < 0)) && likely(ms->sq_length)) {
Py_ssize_t l = ms->sq_length(obj);
if (likely(l >= 0)) {
if (cstop < 0) {
cstop += l;
if (cstop < 0) cstop = 0;
}
if (cstart < 0) {
cstart += l;
if (cstart < 0) cstart = 0;
}
} else {
// if length > max(Py_ssize_t), maybe the object can wrap around itself?
if (PyErr_ExceptionMatches(PyExc_OverflowError))
PyErr_Clear();
else
return -1;
}
}
return ms->sq_ass_slice(obj, cstart, cstop, value);
}
#endif
mp = Py_TYPE(obj)->tp_as_mapping;
if (likely(mp && mp->mp_ass_subscript)) {
PyObject *py_slice, *py_start, *py_stop;
int result;
if (_py_slice) {
py_slice = *_py_slice;
} else {
PyObject* owned_start = NULL;
PyObject* owned_stop = NULL;
if (_py_start) {
py_start = *_py_start;
} else {
if (has_cstart) {
owned_start = py_start = PyInt_FromSsize_t(cstart);
if (unlikely(!py_start)) return -1;
} else
py_start = Py_None;
}
if (_py_stop) {
py_stop = *_py_stop;
} else {
if (has_cstop) {
owned_stop = py_stop = PyInt_FromSsize_t(cstop);
if (unlikely(!py_stop)) {
Py_XDECREF(owned_start);
return -1;
}
} else
py_stop = Py_None;
}
py_slice = PySlice_New(py_start, py_stop, Py_None);
Py_XDECREF(owned_start);
Py_XDECREF(owned_stop);
if (unlikely(!py_slice)) return -1;
}
result = mp->mp_ass_subscript(obj, py_slice, value);
if (!_py_slice) {
Py_DECREF(py_slice);
}
return result;
}
PyErr_Format(PyExc_TypeError,
"'%.200s' object is unsliceable", Py_TYPE(obj)->tp_name);
return -1;
}
/////////////// SliceTupleAndList.proto ///////////////
#if CYTHON_COMPILING_IN_CPYTHON
......
# mode: compile
cdef void spam():
cdef object x
del x[17:42]
spam()
# mode: run
# tag: del, slice
def del_constant_start_stop(x):
"""
>>> l = [1,2,3,4]
>>> del_constant_start_stop(l)
[1, 2]
>>> l = [1,2,3,4,5,6,7]
>>> del_constant_start_stop(l)
[1, 2, 7]
"""
del x[2:6]
return x
def del_start(x, start):
"""
>>> l = [1,2,3,4]
>>> del_start(l, 2)
[1, 2]
>>> l = [1,2,3,4,5,6,7]
>>> del_start(l, 20)
[1, 2, 3, 4, 5, 6, 7]
>>> del_start(l, 8)
[1, 2, 3, 4, 5, 6, 7]
>>> del_start(l, 4)
[1, 2, 3, 4]
>>> del_start(l, -2)
[1, 2]
>>> l
[1, 2]
>>> del_start(l, -2)
[]
>>> del_start(l, 2)
[]
>>> del_start(l, -2)
[]
>>> del_start(l, 20)
[]
>>> del_start([1,2,3,4], -20)
[]
>>> del_start([1,2,3,4], 0)
[]
"""
del x[start:]
return x
def del_stop(x, stop):
"""
>>> l = [1,2,3,4]
>>> del_stop(l, 2)
[3, 4]
>>> l = [1,2,3,4,5,6,7]
>>> del_stop(l, -20)
[1, 2, 3, 4, 5, 6, 7]
>>> del_stop(l, -8)
[1, 2, 3, 4, 5, 6, 7]
>>> del_stop(l, -4)
[4, 5, 6, 7]
>>> del_stop(l, -2)
[6, 7]
>>> l
[6, 7]
>>> del_stop(l, -2)
[6, 7]
>>> del_stop(l, 2)
[]
>>> del_stop(l, -2)
[]
>>> del_stop(l, 20)
[]
>>> del_stop([1,2,3,4], -20)
[1, 2, 3, 4]
>>> del_stop([1,2,3,4], 0)
[1, 2, 3, 4]
"""
del x[:stop]
return x
def del_start_stop(x, start, stop):
"""
>>> l = [1,2,3,4]
>>> del_start_stop(l, 0, 2)
[3, 4]
>>> l
[3, 4]
>>> l = [1,2,3,4,5,6,7]
>>> del_start_stop(l, -1, -20)
[1, 2, 3, 4, 5, 6, 7]
>>> del_start_stop(l, -20, -8)
[1, 2, 3, 4, 5, 6, 7]
>>> del_start_stop(l, -6, -4)
[1, 4, 5, 6, 7]
>>> del_start_stop(l, -20, -2)
[6, 7]
>>> l
[6, 7]
>>> del_start_stop(l, -2, 1)
[7]
>>> del_start_stop(l, -2, 3)
[]
>>> del_start_stop(l, 2, 4)
[]
>>> del_start_stop([1,2,3,4], 20, -20)
[1, 2, 3, 4]
>>> del_start_stop([1,2,3,4], 0, 0)
[1, 2, 3, 4]
"""
del x[start:stop]
return x
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment