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 fdef5a2

Browse files
committed
WIP on sq_ass_item
1 parent 5978249 commit fdef5a2

File tree

3 files changed

+222
-1
lines changed

3 files changed

+222
-1
lines changed

‎doc/sphinx/source/new_types.rst

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,112 @@ Tests are in ``tests/unit/test_c_seqobject.py`` which includes failure modes:
913913
assert err.value.args[0] == expected
914914
915915
916+
---------------
917+
``sq_ass_item``
918+
---------------
919+
920+
`sq_ass_item`_ gives write and delete access to an indexed member.
921+
922+
.. list-table:: Sequence Methods: ``sq_ass_item``
923+
:widths: 20 80
924+
:header-rows: 0
925+
926+
* - Member
927+
- `sq_ass_item`_
928+
* - Function type
929+
- `ssizeobjargproc`_
930+
* - Function signature
931+
- ``int (*ssizeobjargproc)(PyObject*, Py_ssize_t, PyObject*)``
932+
* - Description
933+
- Sets the the n'th item in the sequence.
934+
If the value is NULL the item is deleted and the sequence concatenated (thus called by `PyObject_DelItem()`_).
935+
Negative indexes are handled appropriately.
936+
Used by `PyObject_SetItem()`_.
937+
938+
Implementation
939+
--------------
940+
941+
In ``src/cpy/Object/cSeqObject.c``:
942+
943+
.. code-block:: c
944+
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) {
948+
Py_ssize_t my_index = index;
949+
if (my_index < 0) {
950+
my_index += SequenceLongObject_sq_length(self);
951+
}
952+
// Corner case example: len(self) == 0 and index < 0
953+
if (my_index < 0 || my_index >= SequenceLongObject_sq_length(self)) {
954+
PyErr_Format(
955+
PyExc_IndexError,
956+
"Index %ld is out of range for length %ld",
957+
index,
958+
SequenceLongObject_sq_length(self)
959+
);
960+
return NULL;
961+
}
962+
return PyLong_FromLong(((SequenceLongObject *) self)->array_long[my_index]);
963+
}
964+
965+
Tests
966+
--------------
967+
968+
Tests are in ``tests/unit/test_c_seqobject.py`` which includes failure modes:
969+
970+
.. code-block:: python
971+
972+
from cPyExtPatt import cSeqObject
973+
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
1020+
1021+
9161022
9171023
9181024

‎src/cpy/Object/cSeqObject.c

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,13 +215,96 @@ SequenceLongObject_sq_item(PyObject *self, Py_ssize_t index) {
215215
}
216216
return PyLong_FromLong(((SequenceLongObject *) self)->array_long[my_index]);
217217
}
218+
static int
219+
SequenceLongObject_sq_ass_item(PyObject *self, Py_ssize_t index, PyObject *value) {
220+
Py_ssize_t my_index = index;
221+
if (my_index < 0) {
222+
my_index += SequenceLongObject_sq_length(self);
223+
}
224+
// Corner case example: len(self) == 0 and index < 0
225+
if (my_index < 0 || my_index >= SequenceLongObject_sq_length(self)) {
226+
PyErr_Format(
227+
PyExc_IndexError,
228+
"Index %ld is out of range for length %ld",
229+
index,
230+
SequenceLongObject_sq_length(self)
231+
);
232+
return -1;
233+
}
234+
if (value != NULL) {
235+
/* Just set the value. */
236+
if (!PyLong_Check(value)) {
237+
PyErr_Format(
238+
PyExc_TypeError,
239+
"sq_ass_item value needs to be an int, not type %s",
240+
Py_TYPE(value)->tp_name
241+
);
242+
return -1;
243+
}
244+
((SequenceLongObject *) self)->array_long[my_index] = PyLong_AsLong(value);
245+
} else {
246+
/* Delete the value. */
247+
/* For convenience. */
248+
SequenceLongObject *self_as_slo = (SequenceLongObject *) self;
249+
/* Special case: deleting the only item in the array. */
250+
if (self_as_slo->size == 1) {
251+
free(self_as_slo->array_long);
252+
self_as_slo->array_long = NULL;
253+
self_as_slo->size = 0;
254+
} else {
255+
/* Delete the value and re-compose the array. */
256+
long *new_array = malloc((self_as_slo->size - 1) * sizeof(long));
257+
if (!new_array) {
258+
PyErr_Format(
259+
PyExc_MemoryError,
260+
"sq_ass_item can not allocate new array. %s#%d",
261+
__FILE__, __LINE__
262+
);
263+
return -1;
264+
}
265+
/* memcpy across to the new array, firstly up to the index. */
266+
void *dest = NULL;
267+
void *src = NULL;
268+
size_t count = 0;
269+
270+
dest = new_array;
271+
src = self_as_slo->array_long;
272+
count = my_index * sizeof(long);
273+
if (memcpy(dest, src, count) != dest) {
274+
PyErr_Format(
275+
PyExc_MemoryError,
276+
"sq_ass_item can not memcpy into new array. %s#%d",
277+
__FILE__, __LINE__
278+
);
279+
return -1;
280+
}
281+
/* memcpy across to the new array, from the index to the end. */
282+
dest = new_array + count;
283+
src = self_as_slo->array_long + (count - sizeof(long));
284+
/* Example size=4, index=2 copy one value */
285+
count = (self_as_slo->size - my_index - 1) * sizeof(long);
286+
if (memcpy(dest, src, count) != dest) {
287+
PyErr_Format(
288+
PyExc_MemoryError,
289+
"sq_ass_item can not memcpy into new array. %s#%d",
290+
__FILE__, __LINE__
291+
);
292+
return -1;
293+
}
294+
free(self_as_slo->array_long);
295+
self_as_slo->array_long = new_array;
296+
--self_as_slo->size;
297+
}
298+
}
299+
return 0;
300+
}
218301

219302
PySequenceMethods SequenceLongObject_sequence_methods = {
220303
.sq_length = &SequenceLongObject_sq_length,
221304
.sq_concat = &SequenceLongObject_sq_concat,
222305
.sq_repeat = &SequenceLongObject_sq_repeat,
223306
.sq_item = &SequenceLongObject_sq_item,
224-
.sq_ass_item = NULL,
307+
.sq_ass_item = &SequenceLongObject_sq_ass_item,
225308
.sq_contains = NULL,
226309
.sq_inplace_concat = NULL,
227310
.sq_inplace_repeat = NULL,

‎tests/unit/test_c_seqobject.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ def test_SequenceLongObject_dir():
1616
'__add__',
1717
'__class__',
1818
'__delattr__',
19+
'__delitem__',
1920
'__dir__',
2021
'__doc__',
2122
'__eq__',
@@ -39,6 +40,7 @@ def test_SequenceLongObject_dir():
3940
'__repr__',
4041
'__rmul__',
4142
'__setattr__',
43+
'__setitem__',
4244
'__sizeof__',
4345
'__str__',
4446
'__subclasshook__',
@@ -144,6 +146,36 @@ def test_SequenceLongObject_item_raises(initial_sequence, index, expected):
144146
obj[index]
145147
assert err.value.args[0] == expected
146148

149+
150+
@pytest.mark.parametrize(
151+
'initial_sequence, index, value, expected',
152+
(
153+
(
154+
[7, 4, 1, ], 0, 14, [14, 4, 1, ],
155+
),
156+
(
157+
[7, 4, 1, ], -1, 14, [7, 4, 14, ],
158+
),
159+
(
160+
[7,], 0, None, [],
161+
),
162+
(
163+
[7,], -1, None, [],
164+
),
165+
(
166+
[7, 4, 1, ], 0, None, [4, 14, ],
167+
),
168+
)
169+
)
170+
def test_SequenceLongObject_setitem(initial_sequence, index, value, expected):
171+
obj = cSeqObject.SequenceLongObject(initial_sequence)
172+
if value is not None:
173+
obj[index] = value
174+
else:
175+
del obj[index]
176+
assert list(obj) == expected
177+
178+
147179
# @pytest.mark.skipif(not (sys.version_info.minor < 7), reason='Python < 3.7')
148180
# def test_str_dir_pre_37():
149181
# s = cObject.Str()

0 commit comments

Comments
(0)

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