Skip to content

Commit 8acaa67

Browse files
committed
Add sq_contains, test and documentation.
1 parent 1b25e8e commit 8acaa67

3 files changed

Lines changed: 138 additions & 2 deletions

File tree

doc/sphinx/source/new_types.rst

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,8 @@ The Sequence Function Table
562562
- ``int (*objobjproc)(PyObject*, PyObject*)``
563563
- Returns non-zero if the sequence contains the given object.
564564
Used by `PySequence_Contains()`_.
565-
This slot may be left to NULL, in this case PySequence_Contains() simply traverses the sequence until it finds a match.
565+
This slot may be left to NULL, in this case PySequence_Contains() simply traverses the sequence until it
566+
finds a match.
566567
* - `sq_inplace_concat`_
567568
- `binaryfunc`_
568569
- ``PyObject *(*binaryfunc)(PyObject*, PyObject*)``
@@ -1157,6 +1158,91 @@ Deleting a value with an out of range index:
11571158
assert err.value.args[0] == expected
11581159
11591160
1161+
-------------------
1162+
``sq_ass_contains``
1163+
-------------------
1164+
1165+
.. list-table:: Sequence Methods: ``sq_ass_contains``
1166+
:widths: 20 80
1167+
:header-rows: 0
1168+
1169+
* - Member
1170+
- `sq_ass_contains`_
1171+
* - Function type
1172+
- `objobjproc`_
1173+
* - Function signature
1174+
- ``int (*objobjproc)(PyObject*, PyObject*)``
1175+
* - Description
1176+
- Returns non-zero if the sequence contains the given object.
1177+
If an item in o is equal to value, return 1, otherwise return 0. On error, return -1.
1178+
This is equivalent to the Python expression ``value in o``.
1179+
Used by `PySequence_Contains()`_.
1180+
This slot may be left to NULL, in this case `PySequence_Contains()`_ simply traverses the sequence until it
1181+
finds a match.
1182+
1183+
Implementation
1184+
--------------
1185+
1186+
In ``src/cpy/Object/cSeqObject.c``:
1187+
1188+
.. code-block:: c
1189+
1190+
/** If an item in self is equal to value, return 1, otherwise return 0.
1191+
* On error, return -1. */
1192+
static int
1193+
SequenceLongObject_sq_contains(PyObject *self, PyObject *value) {
1194+
fprintf(
1195+
stdout, "%s()#%d: self=%p value=%p\n",
1196+
__FUNCTION__, __LINE__, (void *) self, (void *) value
1197+
);
1198+
if (!PyLong_Check(value)) {
1199+
/* Alternates: Could raise TypeError or return -1.
1200+
* Here we act benignly! */
1201+
return 0;
1202+
}
1203+
long c_value = PyLong_AsLong(value);
1204+
/* For convenience. */
1205+
SequenceLongObject *self_as_slo = (SequenceLongObject *) self;
1206+
for (Py_ssize_t i = 0; i < SequenceLongObject_sq_length(self); ++i) {
1207+
if (self_as_slo->array_long[i] == c_value) {
1208+
return 1;
1209+
}
1210+
}
1211+
return 0;
1212+
}
1213+
1214+
.. note::
1215+
1216+
Whilst ``SequenceLongObject_sq_contains()`` returns 0 or 1 Python code such as ``value in obj`` converts this to
1217+
``False`` and ``True`` respectively
1218+
1219+
Tests
1220+
--------------
1221+
1222+
Tests are in ``tests/unit/test_c_seqobject.py``:
1223+
1224+
.. code-block:: python
1225+
1226+
from cPyExtPatt import cSeqObject
1227+
1228+
@pytest.mark.parametrize(
1229+
'initial_sequence, value, expected',
1230+
(
1231+
(
1232+
[7, ], 0, False,
1233+
),
1234+
(
1235+
[7, ], 7, True,
1236+
),
1237+
(
1238+
[1, 4, 7, ], 7, True,
1239+
),
1240+
)
1241+
)
1242+
def test_SequenceLongObject_contains(initial_sequence, value, expected):
1243+
obj = cSeqObject.SequenceLongObject(initial_sequence)
1244+
result = value in obj
1245+
assert result == expected
11601246
11611247
11621248

src/cpy/Object/cSeqObject.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,13 +305,41 @@ SequenceLongObject_sq_ass_item(PyObject *self, Py_ssize_t index, PyObject *value
305305
return 0;
306306
}
307307

308+
/**
309+
* If an item in self is equal to value, return 1, otherwise return 0. On error, return -1.
310+
* @param self
311+
* @param value
312+
* @return
313+
*/
314+
static int
315+
SequenceLongObject_sq_contains(PyObject *self, PyObject *value) {
316+
fprintf(
317+
stdout, "%s()#%d: self=%p value=%p\n",
318+
__FUNCTION__, __LINE__, (void *) self, (void *) value
319+
);
320+
if (!PyLong_Check(value)) {
321+
/* Alternates: Could raise TypeError or return -1.
322+
* Here we act benignly! */
323+
return 0;
324+
}
325+
long c_value = PyLong_AsLong(value);
326+
/* For convenience. */
327+
SequenceLongObject *self_as_slo = (SequenceLongObject *) self;
328+
for (Py_ssize_t i = 0; i < SequenceLongObject_sq_length(self); ++i) {
329+
if (self_as_slo->array_long[i] == c_value) {
330+
return 1;
331+
}
332+
}
333+
return 0;
334+
}
335+
308336
static PySequenceMethods SequenceLongObject_sequence_methods = {
309337
.sq_length = (lenfunc)SequenceLongObject_sq_length,
310338
.sq_concat = (binaryfunc)SequenceLongObject_sq_concat,
311339
.sq_repeat = (ssizeargfunc)SequenceLongObject_sq_repeat,
312340
.sq_item = (ssizeargfunc)SequenceLongObject_sq_item,
313341
.sq_ass_item = (ssizeobjargproc)SequenceLongObject_sq_ass_item,
314-
.sq_contains = (objobjproc)NULL,
342+
.sq_contains = (objobjproc)SequenceLongObject_sq_contains,
315343
.sq_inplace_concat = (binaryfunc)NULL,
316344
.sq_inplace_repeat = (ssizeargfunc)NULL,
317345
};

tests/unit/test_c_seqobject.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ def test_SequenceLongObject_dir_pre_311():
1616
assert result == [
1717
'__add__',
1818
'__class__',
19+
'__contains__',
1920
'__delattr__',
2021
'__delitem__',
2122
'__dir__',
@@ -53,6 +54,7 @@ def test_SequenceLongObject_dir_311_plus():
5354
assert result == [
5455
'__add__',
5556
'__class__',
57+
'__contains__',
5658
'__delattr__',
5759
'__delitem__',
5860
'__dir__',
@@ -287,6 +289,26 @@ def test_SequenceLongObject_delitem_raises(initial_sequence, index, expected):
287289
del obj[index]
288290
assert err.value.args[0] == expected
289291

292+
293+
@pytest.mark.parametrize(
294+
'initial_sequence, value, expected',
295+
(
296+
(
297+
[7, ], 0, False,
298+
),
299+
(
300+
[7, ], 7, True,
301+
),
302+
(
303+
[1, 4, 7, ], 7, True,
304+
),
305+
)
306+
)
307+
def test_SequenceLongObject_contains(initial_sequence, value, expected):
308+
obj = cSeqObject.SequenceLongObject(initial_sequence)
309+
result = value in obj
310+
assert result == expected
311+
290312
# @pytest.mark.parametrize(
291313
# 'initial_sequence, index, value, expected',
292314
# (

0 commit comments

Comments
 (0)