Skip to content

Commit e8a2c03

Browse files
committed
Sequence methods, add repeat().
1 parent e774c9b commit e8a2c03

File tree

3 files changed

+198
-4
lines changed

3 files changed

+198
-4
lines changed

doc/sphinx/source/new_types.rst

Lines changed: 119 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ Tests are in ``tests/unit/test_c_seqobject.py``:
633633
- ``PyObject *(*binaryfunc)(PyObject*, PyObject*)``
634634
* - Description
635635
- Takes two sequences and returns a new third one with the first and second concatenated.
636-
This is used by the ``+`` operator.
636+
This is used by the ``+`` Python operator and the `PySequence_Concat()`_ C API.
637637

638638
Implementation
639639
--------------
@@ -690,7 +690,6 @@ In ``src/cpy/Object/cSeqObject.c``:
690690
return ret;
691691
}
692692
693-
694693
Tests
695694
--------------
696695

@@ -708,9 +707,126 @@ Tests are in ``tests/unit/test_c_seqobject.py``:
708707
assert len(obj) == 6
709708
assert list(obj) == [7, 4, 1, ] + [70, 40, 100, ]
710709
710+
---------------
711+
``sq_repeat``
712+
---------------
713+
714+
.. list-table:: Sequence Methods: ``sq_concat``
715+
:widths: 20 80
716+
:header-rows: 0
717+
718+
* - Member
719+
- `sq_repeat`_
720+
* - Function type
721+
- `ssizeargfunc`_
722+
* - Function signature
723+
- ``PyObject *(*ssizeargfunc)(PyObject*, Py_ssize_t)``
724+
* - Description
725+
- Returns a new sequence with the old one repeated the given number of times times.
726+
This is used by the ``*`` Python operator and the `PySequence_Repeat()`_ C API.
727+
728+
Implementation
729+
--------------
730+
731+
The implementation is fairly straightforward in ``src/cpy/Object/cSeqObject.c``.
732+
Note that ``count`` can be zero or negative:
733+
734+
.. code-block:: c
735+
736+
/** Return a new sequence which contains the old one repeated count times. */
737+
static PyObject *
738+
SequenceLongObject_sq_repeat(PyObject *self, Py_ssize_t count) {
739+
PyObject *ret = SequenceLongObject_new(&SequenceLongObjectType, NULL, NULL);
740+
if (!ret) {
741+
assert(PyErr_Occurred());
742+
return NULL;
743+
}
744+
assert(ret != self);
745+
if (((SequenceLongObject *) self)->size > 0 && count > 0) {
746+
/* For convenience. */
747+
SequenceLongObject *self_as_slo = (SequenceLongObject *) self;
748+
SequenceLongObject *ret_as_slo = (SequenceLongObject *) ret;
749+
ret_as_slo->size = self_as_slo->size * count;
750+
assert(ret_as_slo->size > 0);
751+
ret_as_slo->array_long = malloc(ret_as_slo->size * sizeof(long));
752+
if (!ret_as_slo->array_long) {
753+
PyErr_Format(PyExc_MemoryError, "%s(): Can not create new object.", __FUNCTION__);
754+
Py_DECREF(ret);
755+
return NULL;
756+
}
757+
Py_ssize_t ret_index = 0;
758+
for (Py_ssize_t i = 0; i < count; ++i) {
759+
for (Py_ssize_t j = 0; j < self_as_slo->size; ++j) {
760+
ret_as_slo->array_long[ret_index] = self_as_slo->array_long[j];
761+
++ret_index;
762+
}
763+
}
764+
} else {
765+
/* Empty sequence. */
766+
}
767+
return ret;
768+
}
769+
770+
Tests
771+
--------------
772+
773+
Tests are in ``tests/unit/test_c_seqobject.py``:
774+
775+
.. code-block:: python
776+
777+
@pytest.mark.parametrize(
778+
'initial_sequence, count, expected',
779+
(
780+
(
781+
[], 1, [],
782+
),
783+
(
784+
[7, 4, 1, ], 0, [],
785+
),
786+
(
787+
[7, 4, 1, ], -1, [],
788+
),
789+
(
790+
[7, 4, 1, ], 1, [7, 4, 1, ],
791+
),
792+
(
793+
[7, 4, 1, ], 2, [7, 4, 1, 7, 4, 1, ],
794+
),
795+
(
796+
[7, 4, 1, ], 3, [7, 4, 1, 7, 4, 1, 7, 4, 1, ],
797+
),
798+
)
799+
)
800+
def test_SequenceLongObject_repeat(initial_sequence, count, expected):
801+
obj_a = cSeqObject.SequenceLongObject(initial_sequence)
802+
obj = obj_a * count
803+
print()
804+
assert id(obj_a) != id(obj)
805+
assert list(obj) == expected
806+
assert list(obj) == (list(obj_a) * count)
807+
808+
809+
810+
811+
812+
813+
814+
815+
816+
817+
818+
819+
820+
821+
822+
823+
824+
825+
826+
711827
712828
713-
TOOD:
829+
TODO:
714830

715831
====================================
716832
TODOs:

src/cpy/Object/cSeqObject.c

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,50 @@ SequenceLongObject_sq_concat(PyObject *self, PyObject *other) {
145145
return ret;
146146
}
147147

148+
/**
149+
* Return a new sequence which contains the old one repeated count times.
150+
* @param self
151+
* @param count
152+
* @return
153+
*/
154+
static PyObject *
155+
SequenceLongObject_sq_repeat(PyObject *self, Py_ssize_t count) {
156+
PyObject *ret = SequenceLongObject_new(&SequenceLongObjectType, NULL, NULL);
157+
if (!ret) {
158+
assert(PyErr_Occurred());
159+
return NULL;
160+
}
161+
assert(ret != self);
162+
if (((SequenceLongObject *) self)->size > 0 && count > 0) {
163+
/* For convenience. */
164+
SequenceLongObject *self_as_slo = (SequenceLongObject *) self;
165+
SequenceLongObject *ret_as_slo = (SequenceLongObject *) ret;
166+
ret_as_slo->size = self_as_slo->size * count;
167+
assert(ret_as_slo->size > 0);
168+
ret_as_slo->array_long = malloc(ret_as_slo->size * sizeof(long));
169+
if (!ret_as_slo->array_long) {
170+
PyErr_Format(PyExc_MemoryError, "%s(): Can not create new object.", __FUNCTION__);
171+
Py_DECREF(ret);
172+
return NULL;
173+
}
174+
Py_ssize_t ret_index = 0;
175+
for (Py_ssize_t i = 0; i < count; ++i) {
176+
// fprintf(stdout, "%s(): Setting %p Count %zd\n", __FUNCTION__, (void *) ret, i);
177+
for (Py_ssize_t j = 0; j < self_as_slo->size; ++j) {
178+
// fprintf(
179+
// stdout, "%s(): Setting %p [%zd] to %zd\n",
180+
// __FUNCTION__, (void *) ret, ret_index, self_as_slo->array_long[j]
181+
// );
182+
ret_as_slo->array_long[ret_index] = self_as_slo->array_long[j];
183+
++ret_index;
184+
}
185+
}
186+
} else {
187+
/* Empty sequence. */
188+
}
189+
return ret;
190+
}
191+
148192
static PyObject *
149193
SequenceLongObject_sq_item(PyObject *self, Py_ssize_t index) {
150194
// fprintf(stdout, "%s(): index=%zd\n", __FUNCTION__, index);
@@ -168,7 +212,7 @@ SequenceLongObject_sq_item(PyObject *self, Py_ssize_t index) {
168212
PySequenceMethods SequenceLongObject_sequence_methods = {
169213
.sq_length = &SequenceLongObject_sq_length,
170214
.sq_concat = &SequenceLongObject_sq_concat,
171-
.sq_repeat = NULL,
215+
.sq_repeat = &SequenceLongObject_sq_repeat,
172216
.sq_item = &SequenceLongObject_sq_item,
173217
.sq_ass_item = NULL,
174218
.sq_contains = NULL,

tests/unit/test_c_seqobject.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@ def test_SequenceLongObject_dir():
3131
'__le__',
3232
'__len__',
3333
'__lt__',
34+
'__mul__',
3435
'__ne__',
3536
'__new__',
3637
'__reduce__',
3738
'__reduce_ex__',
3839
'__repr__',
40+
'__rmul__',
3941
'__setattr__',
4042
'__sizeof__',
4143
'__str__',
@@ -61,6 +63,38 @@ def test_SequenceLongObject_concat():
6163
assert len(obj) == 6
6264
assert list(obj) == [7, 4, 1, ] + [70, 40, 100, ]
6365

66+
67+
@pytest.mark.parametrize(
68+
'initial_sequence, count, expected',
69+
(
70+
(
71+
[], 1, [],
72+
),
73+
(
74+
[7, 4, 1, ], 0, [],
75+
),
76+
(
77+
[7, 4, 1, ], -1, [],
78+
),
79+
(
80+
[7, 4, 1, ], 1, [7, 4, 1, ],
81+
),
82+
(
83+
[7, 4, 1, ], 2, [7, 4, 1, 7, 4, 1, ],
84+
),
85+
(
86+
[7, 4, 1, ], 3, [7, 4, 1, 7, 4, 1, 7, 4, 1, ],
87+
),
88+
)
89+
)
90+
def test_SequenceLongObject_repeat(initial_sequence, count, expected):
91+
obj_a = cSeqObject.SequenceLongObject(initial_sequence)
92+
obj = obj_a * count
93+
print()
94+
assert id(obj_a) != id(obj)
95+
assert list(obj) == expected
96+
assert list(obj) == (list(obj_a) * count)
97+
6498
# @pytest.mark.skipif(not (sys.version_info.minor < 7), reason='Python < 3.7')
6599
# def test_str_dir_pre_37():
66100
# s = cObject.Str()

0 commit comments

Comments
 (0)