Skip to content

Commit fe83c85

Browse files
committed
Add itertools.serialize (name tbd)
1 parent a4be3bc commit fe83c85

File tree

4 files changed

+158
-2
lines changed

4 files changed

+158
-2
lines changed

Doc/library/itertools.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ Iterator Arguments Results
5656
:func:`groupby` iterable[, key] sub-iterators grouped by value of key(v) ``groupby(['A','B','DEF'], len) → (1, A B) (3, DEF)``
5757
:func:`islice` seq, [start,] stop [, step] elements from seq[start:stop:step] ``islice('ABCDEFG', 2, None) → C D E F G``
5858
:func:`pairwise` iterable (p[0], p[1]), (p[1], p[2]) ``pairwise('ABCDEFG') → AB BC CD DE EF FG``
59+
:func:`serialize` iterable p0, p1, p2, ... ``serialize([1,4,6]) → 1 4 6``
5960
:func:`starmap` func, seq func(\*seq[0]), func(\*seq[1]), ... ``starmap(pow, [(2,5), (3,2), (10,3)]) → 32 9 1000``
6061
:func:`takewhile` predicate, seq seq[0], seq[1], until predicate fails ``takewhile(lambda x: x<5, [1,4,6,3,8]) → 1 4``
6162
:func:`tee` it, n it1, it2, ... itn splits one iterator into n ``tee('ABC', 2) → A B C, A B C``
@@ -648,6 +649,19 @@ loops that truncate the stream.
648649
>>> list(map(pow, range(10), repeat(2)))
649650
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
650651

652+
.. function:: serialize(iterable)
653+
654+
Make an iterator thread-safe. [TBD]
655+
656+
Roughly equivalent to::
657+
658+
class serialize(Iterator):
659+
def __init__(self, it):
660+
self._it = iter(it)
661+
self._lock = Lock()
662+
def __next__(self):
663+
with self._lock:
664+
return next(self._it)
651665

652666
.. function:: starmap(function, iterable)
653667

Lib/test/test_itertools.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2331,6 +2331,19 @@ def test_tee(self):
23312331
self.assertRaises(TypeError, tee, N(s))
23322332
self.assertRaises(ZeroDivisionError, list, tee(E(s))[0])
23332333

2334+
def test_serialize(self):
2335+
for s in ("123", "", range(1000), ('do', 1.2), range(2000,2200,5)):
2336+
for g in (G, I, Ig, S, L, R):
2337+
seq = list(g(s))
2338+
expected = seq
2339+
actual = list(serialize(g(s)))
2340+
self.assertEqual(actual, expected)
2341+
self.assertRaises(TypeError, serialize, X(s))
2342+
self.assertRaises(TypeError, serialize, N(s))
2343+
self.assertRaises(ZeroDivisionError, list, serialize(E(s)))
2344+
for arg in [1, True, sys]:
2345+
self.assertRaises(TypeError, serialize, arg)
2346+
23342347
class LengthTransparency(unittest.TestCase):
23352348

23362349
def test_repeat(self):

Modules/clinic/itertoolsmodule.c.h

Lines changed: 31 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/itertoolsmodule.c

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ typedef struct {
3232
PyTypeObject *permutations_type;
3333
PyTypeObject *product_type;
3434
PyTypeObject *repeat_type;
35+
PyTypeObject *serialize_type;
3536
PyTypeObject *starmap_type;
3637
PyTypeObject *takewhile_type;
3738
PyTypeObject *tee_type;
@@ -85,8 +86,9 @@ class itertools.compress "compressobject *" "clinic_state()->compress_type"
8586
class itertools.filterfalse "filterfalseobject *" "clinic_state()->filterfalse_type"
8687
class itertools.count "countobject *" "clinic_state()->count_type"
8788
class itertools.pairwise "pairwiseobject *" "clinic_state()->pairwise_type"
89+
class itertools.serialize "serializeobject *" "clinic_state()->serialize_type"
8890
[clinic start generated code]*/
89-
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=aa48fe4de9d4080f]*/
91+
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=1261e430ec3a27e1]*/
9092

9193
#define clinic_state() (find_state_by_type(type))
9294
#define clinic_state_by_cls() (get_module_state_by_cls(base_tp))
@@ -3697,6 +3699,100 @@ static PyType_Spec repeat_spec = {
36973699
.slots = repeat_slots,
36983700
};
36993701

3702+
/* serialize object **************************************************************/
3703+
3704+
typedef struct {
3705+
PyObject_HEAD
3706+
PyObject *it;
3707+
} serializeobject;
3708+
3709+
#define serializeobject_CAST(op) ((serializeobject *)(op))
3710+
3711+
/*[clinic input]
3712+
@classmethod
3713+
itertools.serialize.__new__
3714+
iterable: object
3715+
/
3716+
Make an iterator thread-safe [tbd]
3717+
3718+
[clinic start generated code]*/
3719+
3720+
static PyObject *
3721+
itertools_serialize_impl(PyTypeObject *type, PyObject *iterable)
3722+
/*[clinic end generated code: output=abd19e483759f1b7 input=0099ab7fd57cdc9f]*/
3723+
{
3724+
/* Get iterator. */
3725+
PyObject *it = PyObject_GetIter(iterable);
3726+
if (it == NULL)
3727+
return NULL;
3728+
3729+
serializeobject *lz = (serializeobject *)type->tp_alloc(type, 0);
3730+
lz->it = it;
3731+
3732+
return (PyObject *)lz;
3733+
}
3734+
3735+
static void
3736+
serialize_dealloc(PyObject *op)
3737+
{
3738+
serializeobject *lz = serializeobject_CAST(op);
3739+
PyTypeObject *tp = Py_TYPE(lz);
3740+
PyObject_GC_UnTrack(lz);
3741+
Py_XDECREF(lz->it);
3742+
tp->tp_free(lz);
3743+
Py_DECREF(tp);
3744+
}
3745+
3746+
static int
3747+
serialize_traverse(PyObject *op, visitproc visit, void *arg)
3748+
{
3749+
serializeobject *lz = serializeobject_CAST(op);
3750+
Py_VISIT(Py_TYPE(lz));
3751+
Py_VISIT(lz->it);
3752+
return 0;
3753+
}
3754+
3755+
static PyObject *
3756+
serialize_next(PyObject *op)
3757+
{
3758+
serializeobject *lz = serializeobject_CAST(op);
3759+
PyObject *result = NULL;
3760+
3761+
Py_BEGIN_CRITICAL_SECTION(op); // or lock on op->it ?
3762+
PyObject *it = lz->it;
3763+
if (it != NULL) {
3764+
result = PyIter_Next(lz->it);
3765+
if (result == NULL) {
3766+
/* Note: StopIteration is already cleared by PyIter_Next() */
3767+
if (PyErr_Occurred())
3768+
return NULL;
3769+
Py_CLEAR(lz->it);
3770+
}
3771+
}
3772+
Py_END_CRITICAL_SECTION();
3773+
return result;
3774+
}
3775+
3776+
static PyType_Slot serialize_slots[] = {
3777+
{Py_tp_dealloc, serialize_dealloc},
3778+
{Py_tp_getattro, PyObject_GenericGetAttr},
3779+
{Py_tp_doc, (void *)itertools_serialize__doc__},
3780+
{Py_tp_traverse, serialize_traverse},
3781+
{Py_tp_iter, PyObject_SelfIter},
3782+
{Py_tp_iternext, serialize_next},
3783+
{Py_tp_new, itertools_serialize},
3784+
{Py_tp_free, PyObject_GC_Del},
3785+
{0, NULL},
3786+
};
3787+
3788+
static PyType_Spec serialize_spec = {
3789+
.name = "itertools.serialize",
3790+
.basicsize = sizeof(serializeobject),
3791+
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE |
3792+
Py_TPFLAGS_IMMUTABLETYPE),
3793+
.slots = serialize_slots,
3794+
};
3795+
37003796

37013797
/* ziplongest object *********************************************************/
37023798

@@ -3963,6 +4059,7 @@ itertoolsmodule_traverse(PyObject *mod, visitproc visit, void *arg)
39634059
Py_VISIT(state->permutations_type);
39644060
Py_VISIT(state->product_type);
39654061
Py_VISIT(state->repeat_type);
4062+
Py_VISIT(state->serialize_type);
39664063
Py_VISIT(state->starmap_type);
39674064
Py_VISIT(state->takewhile_type);
39684065
Py_VISIT(state->tee_type);
@@ -3992,6 +4089,7 @@ itertoolsmodule_clear(PyObject *mod)
39924089
Py_CLEAR(state->permutations_type);
39934090
Py_CLEAR(state->product_type);
39944091
Py_CLEAR(state->repeat_type);
4092+
Py_CLEAR(state->serialize_type);
39954093
Py_CLEAR(state->starmap_type);
39964094
Py_CLEAR(state->takewhile_type);
39974095
Py_CLEAR(state->tee_type);
@@ -4038,6 +4136,7 @@ itertoolsmodule_exec(PyObject *mod)
40384136
ADD_TYPE(mod, state->permutations_type, &permutations_spec);
40394137
ADD_TYPE(mod, state->product_type, &product_spec);
40404138
ADD_TYPE(mod, state->repeat_type, &repeat_spec);
4139+
ADD_TYPE(mod, state->serialize_type, &serialize_spec);
40414140
ADD_TYPE(mod, state->starmap_type, &starmap_spec);
40424141
ADD_TYPE(mod, state->takewhile_type, &takewhile_spec);
40434142
ADD_TYPE(mod, state->tee_type, &tee_spec);

0 commit comments

Comments
 (0)