Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 018f351

Browse files
committed
Sequence methods, fixing sq_ass_item with some very weird index manipulation for negative indexes.
1 parent 3f13196 commit 018f351

File tree

3 files changed

+163
-109
lines changed

3 files changed

+163
-109
lines changed

‎doc/sphinx/source/new_types.rst

Lines changed: 61 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -942,24 +942,79 @@ In ``src/cpy/Object/cSeqObject.c``:
942942

943943
.. code-block:: c
944944
945-
/** Returns a new reference to an indexed item in a sequence. */
946-
static PyObject *
947-
SequenceLongObject_sq_item(PyObject *self, Py_ssize_t index) {
945+
static int
946+
SequenceLongObject_sq_ass_item(PyObject *self, Py_ssize_t index, PyObject *value) {
947+
fprintf(
948+
stdout, "%s()#%d: self=%p index=%zd value=%p\n",
949+
__FUNCTION__, __LINE__, (void *) self, index, (void *) value
950+
);
948951
Py_ssize_t my_index = index;
949952
if (my_index < 0) {
950953
my_index += SequenceLongObject_sq_length(self);
951954
}
952955
// Corner case example: len(self) == 0 and index < 0
956+
fprintf(
957+
stdout, "%s()#%d: len=%zd index=%zd my_index=%zd\n", __FUNCTION__, __LINE__,
958+
SequenceLongObject_sq_length(self), index, my_index
959+
);
953960
if (my_index < 0 || my_index >= SequenceLongObject_sq_length(self)) {
954961
PyErr_Format(
955962
PyExc_IndexError,
956963
"Index %ld is out of range for length %ld",
957964
index,
958965
SequenceLongObject_sq_length(self)
959966
);
960-
return NULL;
967+
return -1;
961968
}
962-
return PyLong_FromLong(((SequenceLongObject *) self)->array_long[my_index]);
969+
if (value != NULL) {
970+
/* Just set the value. */
971+
if (!PyLong_Check(value)) {
972+
PyErr_Format(
973+
PyExc_TypeError,
974+
"sq_ass_item value needs to be an int, not type %s",
975+
Py_TYPE(value)->tp_name
976+
);
977+
return -1;
978+
}
979+
((SequenceLongObject *) self)->array_long[my_index] = PyLong_AsLong(value);
980+
} else {
981+
/* Delete the value. */
982+
/* For convenience. */
983+
SequenceLongObject *self_as_slo = (SequenceLongObject *) self;
984+
/* Special case: deleting the only item in the array. */
985+
if (self_as_slo->size == 1) {
986+
fprintf(stdout, "%s()#%d: deleting empty index\n", __FUNCTION__, __LINE__);
987+
free(self_as_slo->array_long);
988+
self_as_slo->array_long = NULL;
989+
self_as_slo->size = 0;
990+
} else {
991+
/* Delete the value and re-compose the array. */
992+
fprintf(stdout, "%s()#%d: deleting index=%zd\n", __FUNCTION__, __LINE__, index);
993+
long *new_array = malloc((self_as_slo->size - 1) * sizeof(long));
994+
if (!new_array) {
995+
PyErr_Format(
996+
PyExc_MemoryError,
997+
"sq_ass_item can not allocate new array. %s#%d",
998+
__FILE__, __LINE__
999+
);
1000+
return -1;
1001+
}
1002+
/* Copy up to the index. */
1003+
Py_ssize_t index_new_array = 0;
1004+
for (Py_ssize_t i = 0; i < my_index; ++i, ++index_new_array) {
1005+
new_array[index_new_array] = self_as_slo->array_long[i];
1006+
}
1007+
/* Copy past the index. */
1008+
for (Py_ssize_t i = my_index + 1; i < self_as_slo->size; ++i, ++index_new_array) {
1009+
new_array[index_new_array] = self_as_slo->array_long[i];
1010+
}
1011+
1012+
free(self_as_slo->array_long);
1013+
self_as_slo->array_long = new_array;
1014+
--self_as_slo->size;
1015+
}
1016+
}
1017+
return 0;
9631018
}
9641019
9651020
Tests
@@ -971,52 +1026,7 @@ Tests are in ``tests/unit/test_c_seqobject.py`` which includes failure modes:
9711026
9721027
from cPyExtPatt import cSeqObject
9731028
974-
@pytest.mark.parametrize(
975-
'initial_sequence, index, expected',
976-
(
977-
(
978-
[7, 4, 1, ], 0, 7,
979-
),
980-
(
981-
[7, 4, 1, ], 1, 4,
982-
),
983-
(
984-
[7, 4, 1, ], 2, 1,
985-
),
986-
(
987-
[7, 4, 1, ], -1, 1,
988-
),
989-
(
990-
[7, 4, 1, ], -2, 4,
991-
),
992-
(
993-
[7, 4, 1, ], -3, 7,
994-
),
995-
)
996-
)
997-
def test_SequenceLongObject_item(initial_sequence, index, expected):
998-
obj = cSeqObject.SequenceLongObject(initial_sequence)
999-
assert obj[index] == expected
1000-
1001-
@pytest.mark.parametrize(
1002-
'initial_sequence, index, expected',
1003-
(
1004-
(
1005-
[], 0, 'Index 0 is out of range for length 0',
1006-
),
1007-
(
1008-
[], -1, 'Index -1 is out of range for length 0',
1009-
),
1010-
(
1011-
[1, ], 2, 'Index 2 is out of range for length 1',
1012-
),
1013-
)
1014-
)
1015-
def test_SequenceLongObject_item_raises(initial_sequence, index, expected):
1016-
obj = cSeqObject.SequenceLongObject(initial_sequence)
1017-
with pytest.raises(IndexError) as err:
1018-
obj[index]
1019-
assert err.value.args[0] == expected
1029+
pass
10201030
10211031
10221032

‎src/cpy/Object/cSeqObject.c

Lines changed: 30 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -222,11 +222,24 @@ SequenceLongObject_sq_ass_item(PyObject *self, Py_ssize_t index, PyObject *value
222222
stdout, "%s()#%d: self=%p index=%zd value=%p\n",
223223
__FUNCTION__, __LINE__, (void *) self, index, (void *) value
224224
);
225+
/* This is very weird. */
226+
if (index < 0) {
227+
fprintf(
228+
stdout, "%s()#%d: Fixing index index=%zd to %zd\n", __FUNCTION__, __LINE__,
229+
index, index - SequenceLongObject_sq_length(self)
230+
);
231+
index -= SequenceLongObject_sq_length(self);
232+
}
233+
/* Isn't it? */
225234
Py_ssize_t my_index = index;
226235
if (my_index < 0) {
227236
my_index += SequenceLongObject_sq_length(self);
228237
}
229238
// Corner case example: len(self) == 0 and index < 0
239+
fprintf(
240+
stdout, "%s()#%d: len=%zd index=%zd my_index=%zd\n", __FUNCTION__, __LINE__,
241+
SequenceLongObject_sq_length(self), index, my_index
242+
);
230243
if (my_index < 0 || my_index >= SequenceLongObject_sq_length(self)) {
231244
PyErr_Format(
232245
PyExc_IndexError,
@@ -269,38 +282,16 @@ SequenceLongObject_sq_ass_item(PyObject *self, Py_ssize_t index, PyObject *value
269282
);
270283
return -1;
271284
}
272-
/* memcpy parameters. */
273-
void *dest = NULL;
274-
void *src = NULL;
275-
size_t count = 0;
276-
277-
/* memcpy across to the new array, firstly up to the index. */
278-
dest = new_array;
279-
src = self_as_slo->array_long;
280-
count = my_index * sizeof(long);
281-
fprintf(stdout, "%s()#%d: First: dest=%p src=%p count=%zu\n", __FUNCTION__, __LINE__, dest, src, count);
282-
if (memcpy(dest, src, count) != dest) {
283-
PyErr_Format(
284-
PyExc_MemoryError,
285-
"sq_ass_item can not memcpy into new array. %s#%d",
286-
__FILE__, __LINE__
287-
);
288-
return -1;
285+
/* Copy up to the index. */
286+
Py_ssize_t index_new_array = 0;
287+
for (Py_ssize_t i = 0; i < my_index; ++i, ++index_new_array) {
288+
new_array[index_new_array] = self_as_slo->array_long[i];
289289
}
290-
/* memcpy from the index to the end. */
291-
dest = new_array + count;
292-
src = self_as_slo->array_long + count;
293-
/* Example size=4, index=2 copy one value */
294-
count = (self_as_slo->size - my_index - 1) * sizeof(long);
295-
fprintf(stdout, "%s()#%d: Next: dest=%p src=%p count=%zu\n", __FUNCTION__, __LINE__, dest, src, count);
296-
if (memcpy(dest, src, count) != dest) {
297-
PyErr_Format(
298-
PyExc_MemoryError,
299-
"sq_ass_item can not memcpy into new array. %s#%d",
300-
__FILE__, __LINE__
301-
);
302-
return -1;
290+
/* Copy past the index. */
291+
for (Py_ssize_t i = my_index + 1; i < self_as_slo->size; ++i, ++index_new_array) {
292+
new_array[index_new_array] = self_as_slo->array_long[i];
303293
}
294+
304295
free(self_as_slo->array_long);
305296
self_as_slo->array_long = new_array;
306297
--self_as_slo->size;
@@ -309,15 +300,15 @@ SequenceLongObject_sq_ass_item(PyObject *self, Py_ssize_t index, PyObject *value
309300
return 0;
310301
}
311302

312-
PySequenceMethods SequenceLongObject_sequence_methods = {
313-
.sq_length = &SequenceLongObject_sq_length,
314-
.sq_concat = &SequenceLongObject_sq_concat,
315-
.sq_repeat = &SequenceLongObject_sq_repeat,
316-
.sq_item = &SequenceLongObject_sq_item,
317-
.sq_ass_item = &SequenceLongObject_sq_ass_item,
318-
.sq_contains = NULL,
319-
.sq_inplace_concat = NULL,
320-
.sq_inplace_repeat = NULL,
303+
staticPySequenceMethods SequenceLongObject_sequence_methods = {
304+
.sq_length = (lenfunc)SequenceLongObject_sq_length,
305+
.sq_concat = (binaryfunc)SequenceLongObject_sq_concat,
306+
.sq_repeat = (ssizeargfunc)SequenceLongObject_sq_repeat,
307+
.sq_item = (ssizeargfunc)SequenceLongObject_sq_item,
308+
.sq_ass_item = (ssizeobjargproc)SequenceLongObject_sq_ass_item,
309+
.sq_contains = (objobjproc)NULL,
310+
.sq_inplace_concat = (binaryfunc)NULL,
311+
.sq_inplace_repeat = (ssizeargfunc)NULL,
321312
};
322313

323314
static PyObject *

‎tests/unit/test_c_seqobject.py

Lines changed: 72 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -156,47 +156,100 @@ def test_SequenceLongObject_item_raises(initial_sequence, index, expected):
156156
(
157157
[7, 4, 1, ], -1, 14, [7, 4, 14, ],
158158
),
159-
# (
160-
# [], 0, None, [],
161-
# ),
159+
)
160+
)
161+
def test_SequenceLongObject_setitem(initial_sequence, index, value, expected):
162+
obj = cSeqObject.SequenceLongObject(initial_sequence)
163+
obj[index] = value
164+
assert list(obj) == expected
165+
166+
167+
@pytest.mark.parametrize(
168+
'initial_sequence, index, expected',
169+
(
162170
(
163-
[7,], 0, None, [],
171+
[7,4, 1, ], 3, 'Index 3 is out of range for length 3',
164172
),
165173
(
166-
[7,], -1, None, [],
174+
[7,4, 1, ], -4, 'Index -4 is out of range for length 3',
167175
),
176+
)
177+
)
178+
def test_SequenceLongObject_setitem_raises(initial_sequence, index, expected):
179+
print()
180+
print(initial_sequence, index, expected)
181+
obj = cSeqObject.SequenceLongObject(initial_sequence)
182+
with pytest.raises(IndexError) as err:
183+
obj[index] = 100
184+
print(list(obj))
185+
assert err.value.args[0] == expected
186+
187+
188+
@pytest.mark.parametrize(
189+
'initial_sequence, index, expected',
190+
(
168191
(
169-
[7,4, 1, ], 1, None, [7, 1, ],
192+
[7,], 0, [],
170193
),
171194
(
172-
[7, 4, ], 0, None, [4, ],
195+
[7,], -1, [],
196+
),
197+
(
198+
[7, 4, 1, ], 1, [7, 1, ],
199+
),
200+
(
201+
[7, 4, ], 0, [4, ],
173202
),
174203
)
175204
)
176-
def test_SequenceLongObject_setitem(initial_sequence, index, value, expected):
205+
def test_SequenceLongObject_delitem(initial_sequence, index, expected):
177206
obj = cSeqObject.SequenceLongObject(initial_sequence)
178-
if value is not None:
179-
obj[index] = value
180-
else:
181-
del obj[index]
207+
del obj[index]
182208
assert list(obj) == expected
183209

184210

185211
@pytest.mark.parametrize(
186-
'initial_sequence, index, value, expected',
212+
'initial_sequence, index, expected',
187213
(
188214
(
189-
[7, 4, 1, ], 1, None, [7, 1, ],
215+
[], 0, 'Index 0 is out of range for length 0',
216+
),
217+
(
218+
[], -1, 'Index -1 is out of range for length 0',
219+
),
220+
(
221+
[7,], 1, 'Index 1 is out of range for length 1',
222+
),
223+
(
224+
[7,], -3, 'Index -3 is out of range for length 1',
190225
),
191226
)
192227
)
193-
def test_SequenceLongObject_setitem_debug(initial_sequence, index, value, expected):
228+
def test_SequenceLongObject_delitem_raises(initial_sequence, index, expected):
229+
print()
230+
print(initial_sequence, index, expected)
194231
obj = cSeqObject.SequenceLongObject(initial_sequence)
195-
if value is not None:
196-
obj[index] = value
197-
else:
232+
print(list(obj))
233+
with pytest.raises(IndexError) as err:
198234
del obj[index]
199-
assert list(obj) == expected
235+
assert err.value.args[0] == expected
236+
237+
238+
# @pytest.mark.parametrize(
239+
# 'initial_sequence, index, value, expected',
240+
# (
241+
# (
242+
# [7, 4, 1, ], 1, None, [7, 1, ],
243+
# ),
244+
# )
245+
# )
246+
# def test_SequenceLongObject_setitem_debug(initial_sequence, index, value, expected):
247+
# obj = cSeqObject.SequenceLongObject(initial_sequence)
248+
# if value is not None:
249+
# obj[index] = value
250+
# else:
251+
# del obj[index]
252+
# assert list(obj) == expected
200253

201254

202255
# @pytest.mark.skipif(not (sys.version_info.minor < 7), reason='Python < 3.7')

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /