@@ -434,7 +434,8 @@ Emulating Sequence Types
434
434
435
435
This section describes how to make an object act like a sequence using
436
436
`tp_as_sequence <https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_as_sequence >`_.
437
- See also `Sequence Object Structures <https://docs.python.org/3/c-api/typeobj.html#sequence-structs >`_
437
+ See also `Sequence Object Structures <https://docs.python.org/3/c-api/typeobj.html#sequence-structs >`_.
438
+ This allows your objects to have set and get methods like a list.
438
439
439
440
As an example here is an extension that can represent a sequence of longs in C with a CPython sequence interface.
440
441
The code is in ``src/cpy/Object/cSeqObject.c ``.
@@ -674,10 +675,13 @@ In ``src/cpy/Object/cSeqObject.c``:
674
675
}
675
676
/* For convenience. */
676
677
SequenceLongObject *ret_as_slo = (SequenceLongObject *) ret;
677
- ret_as_slo->size = ((SequenceLongObject *) self)->size + ((SequenceLongObject *) other)->size;
678
+ ret_as_slo->size = ((SequenceLongObject *) self)->size \
679
+ + ((SequenceLongObject *) other)->size;
678
680
ret_as_slo->array_long = malloc(ret_as_slo->size * sizeof(long));
679
681
if (!ret_as_slo->array_long) {
680
- PyErr_Format(PyExc_MemoryError, "%s(): Can not create new object.", __FUNCTION__);
682
+ PyErr_Format(
683
+ PyExc_MemoryError, "%s(): Can not create new object.", __FUNCTION__
684
+ );
681
685
Py_DECREF(ret);
682
686
return NULL;
683
687
}
@@ -763,7 +767,9 @@ Note that ``count`` can be zero or negative:
763
767
assert(ret_as_slo->size > 0);
764
768
ret_as_slo->array_long = malloc(ret_as_slo->size * sizeof(long));
765
769
if (!ret_as_slo->array_long) {
766
- PyErr_Format(PyExc_MemoryError, "%s(): Can not create new object.", __FUNCTION__);
770
+ PyErr_Format(
771
+ PyExc_MemoryError, "%s(): Can not create new object.", __FUNCTION__
772
+ );
767
773
Py_DECREF(ret);
768
774
return NULL;
769
775
}
@@ -790,24 +796,12 @@ Tests are in ``tests/unit/test_c_seqobject.py``:
790
796
@pytest.mark.parametrize (
791
797
' initial_sequence, count, expected' ,
792
798
(
793
- (
794
- [], 1 , [],
795
- ),
796
- (
797
- [7 , 4 , 1 , ], 0 , [],
798
- ),
799
- (
800
- [7 , 4 , 1 , ], - 1 , [],
801
- ),
802
- (
803
- [7 , 4 , 1 , ], 1 , [7 , 4 , 1 , ],
804
- ),
805
- (
806
- [7 , 4 , 1 , ], 2 , [7 , 4 , 1 , 7 , 4 , 1 , ],
807
- ),
808
- (
809
- [7 , 4 , 1 , ], 3 , [7 , 4 , 1 , 7 , 4 , 1 , 7 , 4 , 1 , ],
810
- ),
799
+ ([], 1 , [],),
800
+ ([7 , 4 , 1 , ], 0 , [],),
801
+ ([7 , 4 , 1 , ], - 1 , [],),
802
+ ([7 , 4 , 1 , ], 1 , [7 , 4 , 1 , ],),
803
+ ([7 , 4 , 1 , ], 2 , [7 , 4 , 1 , 7 , 4 , 1 , ],),
804
+ ([7 , 4 , 1 , ], 3 , [7 , 4 , 1 , 7 , 4 , 1 , 7 , 4 , 1 , ],),
811
805
)
812
806
)
813
807
def test_SequenceLongObject_repeat (initial_sequence , count , expected ):
@@ -877,7 +871,7 @@ In ``src/cpy/Object/cSeqObject.c``:
877
871
Tests
878
872
--------------
879
873
880
- Tests are in ``tests/unit/test_c_seqobject.py `` which includes failure modes :
874
+ Tests are in ``tests/unit/test_c_seqobject.py ``:
881
875
882
876
.. code-block :: python
883
877
@@ -886,42 +880,30 @@ Tests are in ``tests/unit/test_c_seqobject.py`` which includes failure modes:
886
880
@pytest.mark.parametrize (
887
881
' initial_sequence, index, expected' ,
888
882
(
889
- (
890
- [7 , 4 , 1 , ], 0 , 7 ,
891
- ),
892
- (
893
- [7 , 4 , 1 , ], 1 , 4 ,
894
- ),
895
- (
896
- [7 , 4 , 1 , ], 2 , 1 ,
897
- ),
898
- (
899
- [7 , 4 , 1 , ], - 1 , 1 ,
900
- ),
901
- (
902
- [7 , 4 , 1 , ], - 2 , 4 ,
903
- ),
904
- (
905
- [7 , 4 , 1 , ], - 3 , 7 ,
906
- ),
883
+ ([7 , 4 , 1 , ], 0 , 7 ,),
884
+ ([7 , 4 , 1 , ], 1 , 4 ,),
885
+ ([7 , 4 , 1 , ], 2 , 1 ,),
886
+ ([7 , 4 , 1 , ], - 1 , 1 ,),
887
+ ([7 , 4 , 1 , ], - 2 , 4 ,),
888
+ ([7 , 4 , 1 , ], - 3 , 7 ,),
907
889
)
908
890
)
909
891
def test_SequenceLongObject_item (initial_sequence , index , expected ):
910
892
obj = cSeqObject.SequenceLongObject(initial_sequence)
911
893
assert obj[index] == expected
912
894
895
+ And failure modes:
896
+
897
+ .. code-block :: python
898
+
899
+ from cPyExtPatt import cSeqObject
900
+
913
901
@pytest.mark.parametrize (
914
902
' initial_sequence, index, expected' ,
915
903
(
916
- (
917
- [], 0 , ' Index 0 is out of range for length 0' ,
918
- ),
919
- (
920
- [], - 1 , ' Index -1 is out of range for length 0' ,
921
- ),
922
- (
923
- [1 , ], 2 , ' Index 2 is out of range for length 1' ,
924
- ),
904
+ ([], 0 , ' Index 0 is out of range for length 0' ,),
905
+ ([], - 1 , ' Index -1 is out of range for length 0' ,),
906
+ ([1 , ], 2 , ' Index 2 is out of range for length 1' ,),
925
907
)
926
908
)
927
909
def test_SequenceLongObject_item_raises (initial_sequence , index , expected ):
@@ -954,6 +936,7 @@ Tests are in ``tests/unit/test_c_seqobject.py`` which includes failure modes:
954
936
- ``int (*ssizeobjargproc)(PyObject*, Py_ssize_t, PyObject*) ``
955
937
* - Description
956
938
- Sets the the n'th item in the sequence.
939
+ Returns 0 on success, -1 on failure.
957
940
If the value is NULL the item is deleted and the sequence concatenated (thus called by `PyObject_DelItem() `_).
958
941
Negative indexes are handled appropriately.
959
942
Used by `PyObject_SetItem() `_.
@@ -964,7 +947,7 @@ Implementation
964
947
.. warning ::
965
948
966
949
There is an undocumented feature when using `sq_ass_item `_ from `PyObject_SetItem() `_ and `PyObject_DelItem() `_
967
- when using negative indexes when the negative index is *out of range *.
950
+ when using negative indexes and when the index is *out of range *.
968
951
969
952
In that case, before the `sq_ass_item `_ function is called the index will have had the sequence length added to it.
970
953
@@ -973,7 +956,8 @@ Implementation
973
956
If the given index is -5 then the index that the `sq_ass_item `_ function receives is -2.
974
957
975
958
Thus the slightly odd code below to fix this problem.
976
- Failing to do this will mean out of range errors will not be detected by the `sq_ass_item `_ function.
959
+ Failing to do this will mean out of range errors will not be detected by the `sq_ass_item `_ function and any
960
+ error message will be wrong.
977
961
978
962
979
963
In ``src/cpy/Object/cSeqObject.c ``:
@@ -1038,7 +1022,10 @@ In ``src/cpy/Object/cSeqObject.c``:
1038
1022
new_array[index_new_array] = self_as_slo->array_long[i];
1039
1023
}
1040
1024
/* Copy past the index. */
1041
- for (Py_ssize_t i = my_index + 1; i < self_as_slo->size; ++i, ++index_new_array) {
1025
+ for (
1026
+ Py_ssize_t i = my_index + 1;
1027
+ i < self_as_slo->size;
1028
+ ++i, ++index_new_array) {
1042
1029
new_array[index_new_array] = self_as_slo->array_long[i];
1043
1030
}
1044
1031
@@ -1063,18 +1050,10 @@ First setting a value:
1063
1050
@pytest.mark.parametrize (
1064
1051
' initial_sequence, index, value, expected' ,
1065
1052
(
1066
- (
1067
- [7 , 4 , 1 , ], 0 , 14 , [14 , 4 , 1 , ],
1068
- ),
1069
- (
1070
- [7 , 4 , 1 , ], - 1 , 14 , [7 , 4 , 14 , ],
1071
- ),
1072
- (
1073
- [7 , 4 , 1 , ], - 2 , 14 , [7 , 14 , 1 , ],
1074
- ),
1075
- (
1076
- [7 , 4 , 1 , ], - 3 , 14 , [14 , 4 , 1 , ],
1077
- ),
1053
+ ([7 , 4 , 1 , ], 0 , 14 , [14 , 4 , 1 , ],),
1054
+ ([7 , 4 , 1 , ], - 1 , 14 , [7 , 4 , 14 , ],),
1055
+ ([7 , 4 , 1 , ], - 2 , 14 , [7 , 14 , 1 , ],),
1056
+ ([7 , 4 , 1 , ], - 3 , 14 , [14 , 4 , 1 , ],),
1078
1057
)
1079
1058
)
1080
1059
def test_SequenceLongObject_setitem (initial_sequence , index , value , expected ):
@@ -1092,21 +1071,14 @@ Setting a value with an out of range index:
1092
1071
@pytest.mark.parametrize (
1093
1072
' initial_sequence, index, expected' ,
1094
1073
(
1095
- (
1096
- [7 , 4 , 1 , ], 3 , ' Index 3 is out of range for length 3' ,
1097
- ),
1098
- (
1099
- [7 , 4 , 1 , ], - 4 , ' Index -4 is out of range for length 3' ,
1100
- ),
1074
+ ([7 , 4 , 1 , ], 3 , ' Index 3 is out of range for length 3' ,),
1075
+ ([7 , 4 , 1 , ], - 4 , ' Index -4 is out of range for length 3' ,),
1101
1076
)
1102
1077
)
1103
1078
def test_SequenceLongObject_setitem_raises (initial_sequence , index , expected ):
1104
- print ()
1105
- print (initial_sequence, index, expected)
1106
1079
obj = cSeqObject.SequenceLongObject(initial_sequence)
1107
1080
with pytest.raises(IndexError ) as err:
1108
1081
obj[index] = 100
1109
- print (list (obj))
1110
1082
assert err.value.args[0 ] == expected
1111
1083
1112
1084
@@ -1119,27 +1091,13 @@ Deleting a value:
1119
1091
@pytest.mark.parametrize (
1120
1092
' initial_sequence, index, expected' ,
1121
1093
(
1122
- (
1123
- [7 , ], 0 , [],
1124
- ),
1125
- (
1126
- [7 , ], - 1 , [],
1127
- ),
1128
- (
1129
- [7 , 4 , 1 , ], 1 , [7 , 1 , ],
1130
- ),
1131
- (
1132
- [7 , 4 , ], 0 , [4 , ],
1133
- ),
1134
- (
1135
- [7 , 4 , 1 , ], - 1 , [7 , 4 , ],
1136
- ),
1137
- (
1138
- [7 , 4 , 1 , ], - 2 , [7 , 1 , ],
1139
- ),
1140
- (
1141
- [7 , 4 , 1 , ], - 3 , [4 , 1 , ],
1142
- ),
1094
+ ([7 , ], 0 , [],),
1095
+ ([7 , ], - 1 , [],),
1096
+ ([7 , 4 , 1 , ], 1 , [7 , 1 , ],),
1097
+ ([7 , 4 , ], 0 , [4 , ],),
1098
+ ([7 , 4 , 1 , ], - 1 , [7 , 4 , ],),
1099
+ ([7 , 4 , 1 , ], - 2 , [7 , 1 , ],),
1100
+ ([7 , 4 , 1 , ], - 3 , [4 , 1 , ],),
1143
1101
)
1144
1102
)
1145
1103
def test_SequenceLongObject_delitem (initial_sequence , index , expected ):
@@ -1157,25 +1115,14 @@ Deleting a value with an out of range index:
1157
1115
@pytest.mark.parametrize (
1158
1116
' initial_sequence, index, expected' ,
1159
1117
(
1160
- (
1161
- [], 0 , ' Index 0 is out of range for length 0' ,
1162
- ),
1163
- (
1164
- [], - 1 , ' Index -1 is out of range for length 0' ,
1165
- ),
1166
- (
1167
- [7 , ], 1 , ' Index 1 is out of range for length 1' ,
1168
- ),
1169
- (
1170
- [7 , ], - 3 , ' Index -3 is out of range for length 1' ,
1171
- ),
1118
+ ([], 0 , ' Index 0 is out of range for length 0' ,),
1119
+ ([], - 1 , ' Index -1 is out of range for length 0' ,),
1120
+ ([7 , ], 1 , ' Index 1 is out of range for length 1' ,),
1121
+ ([7 , ], - 3 , ' Index -3 is out of range for length 1' ,),
1172
1122
)
1173
1123
)
1174
1124
def test_SequenceLongObject_delitem_raises (initial_sequence , index , expected ):
1175
- print ()
1176
- print (initial_sequence, index, expected)
1177
1125
obj = cSeqObject.SequenceLongObject(initial_sequence)
1178
- print (list (obj))
1179
1126
with pytest.raises(IndexError ) as err:
1180
1127
del obj[index]
1181
1128
assert err.value.args[0 ] == expected
@@ -1256,15 +1203,9 @@ Tests are in ``tests/unit/test_c_seqobject.py``:
1256
1203
@pytest.mark.parametrize (
1257
1204
' initial_sequence, value, expected' ,
1258
1205
(
1259
- (
1260
- [7 , ], 0 , False ,
1261
- ),
1262
- (
1263
- [7 , ], 7 , True ,
1264
- ),
1265
- (
1266
- [1 , 4 , 7 , ], 7 , True ,
1267
- ),
1206
+ ([7 , ], 0 , False ,),
1207
+ ([7 , ], 7 , True ,),
1208
+ ([1 , 4 , 7 , ], 7 , True ,),
1268
1209
)
1269
1210
)
1270
1211
def test_SequenceLongObject_contains (initial_sequence , value , expected ):
@@ -1361,24 +1302,12 @@ Tests are in ``tests/unit/test_c_seqobject.py``:
1361
1302
@pytest.mark.parametrize (
1362
1303
' initial_sequence, count, expected' ,
1363
1304
(
1364
- (
1365
- [], 1 , [],
1366
- ),
1367
- (
1368
- [7 , 4 , 1 , ], 0 , [],
1369
- ),
1370
- (
1371
- [7 , 4 , 1 , ], - 1 , [],
1372
- ),
1373
- (
1374
- [7 , 4 , 1 , ], 1 , [7 , 4 , 1 , ],
1375
- ),
1376
- (
1377
- [7 , 4 , 1 , ], 2 , [7 , 4 , 1 , 7 , 4 , 1 , ],
1378
- ),
1379
- (
1380
- [7 , 4 , 1 , ], 3 , [7 , 4 , 1 , 7 , 4 , 1 , 7 , 4 , 1 , ],
1381
- ),
1305
+ ([], 1 , [],),
1306
+ ([7 , 4 , 1 , ], 0 , [],),
1307
+ ([7 , 4 , 1 , ], - 1 , [],),
1308
+ ([7 , 4 , 1 , ], 1 , [7 , 4 , 1 , ],),
1309
+ ([7 , 4 , 1 , ], 2 , [7 , 4 , 1 , 7 , 4 , 1 , ],),
1310
+ ([7 , 4 , 1 , ], 3 , [7 , 4 , 1 , 7 , 4 , 1 , 7 , 4 , 1 , ],),
1382
1311
)
1383
1312
)
1384
1313
def test_SequenceLongObject_repeat_inplace (initial_sequence , count , expected ):
@@ -1407,8 +1336,8 @@ All these functions are gathered together in a `PySequenceMethods`_ table:
1407
1336
.sq_item = (ssizeargfunc)SequenceLongObject_sq_item,
1408
1337
.sq_ass_item = (ssizeobjargproc)SequenceLongObject_sq_ass_item,
1409
1338
.sq_contains = (objobjproc)SequenceLongObject_sq_contains,
1410
- .sq_inplace_concat = (binaryfunc)NULL,
1411
- .sq_inplace_repeat = (ssizeargfunc)NULL,
1339
+ .sq_inplace_concat = (binaryfunc)NULL, // Not implemented. See above.
1340
+ .sq_inplace_repeat = (ssizeargfunc)NULL, // Not implemented. See above.
1412
1341
};
1413
1342
1414
1343
And the ``SequenceLongObjectType `` type is declared with the ``tp_as_sequence `` field referring to this table:
0 commit comments