root/releases/pkgcore/0.2.3/src/lists.c @ ferringb%2540gmail.com-20070212032432-g6y1o22gt9bu2x2n

Revision ferringb%2540gmail.com-20070212032432-g6y1o22gt9bu2x2n, 16.9 KB (checked in by Brian Harring <ferringb@…>, 2 years ago)

quick tweak to bypass PyObject?_IsTrue for most sane returns.

Line 
1/*
2 * Copyright: 2006 Marien Zwart <marienz@gentoo.org>
3 * License: GPL2
4 *
5 * C version of some of pkgcore (for extra speed).
6 */
7
8/* This does not really do anything since we do not use the "#"
9 * specifier in a PyArg_Parse or similar call, but hey, not using it
10 * means we are Py_ssize_t-clean too!
11 */
12
13#define PY_SSIZE_T_CLEAN
14
15#include "Python.h"
16#include "py24-compatibility.h"
17
18
19/* Helper functions */
20
21static PyObject *
22build_initial_iterables(PyObject *l) {
23    PyObject *result, *iter = PyObject_GetIter(l);
24    if (!iter)
25        return NULL;
26
27    result = PyList_New(1);
28    if (!result) {
29        Py_DECREF(iter);
30        return NULL;
31    }
32    PyList_SET_ITEM(result, 0, iter);
33    return result;
34}
35
36/* iflatten_func: recursively flatten an iterable with a func as filter. */
37
38typedef struct {
39    PyObject_HEAD
40    PyObject *skip_func;
41    PyObject *iterables;
42    char in_iternext;
43} pkgcore_iflatten_func;
44
45static void
46pkgcore_iflatten_func_dealloc(pkgcore_iflatten_func *self)
47{
48    Py_CLEAR(self->skip_func);
49    Py_CLEAR(self->iterables);
50    self->ob_type->tp_free((PyObject*) self);
51}
52
53static PyObject *
54pkgcore_iflatten_func_new(PyTypeObject *type,
55                              PyObject *args, PyObject *kwargs)
56{
57    pkgcore_iflatten_func *self;
58    PyObject *l=NULL, *skip_func=NULL, *tmp;
59    int res;
60
61    if (kwargs && PyDict_Size(kwargs)) {
62        PyErr_SetString(PyExc_TypeError,
63                        "iflatten_func takes no keyword arguments");
64        return NULL;
65    }
66    if (!PyArg_UnpackTuple(args, "iflatten_func", 2, 2, &l, &skip_func)) {
67        return NULL;
68    }
69
70    /* Check if we got a single argument that should be skipped. */
71    tmp = PyObject_CallFunctionObjArgs(skip_func, l, NULL);
72    if (!tmp) {
73        return NULL;
74    }
75    // Py_(True|False) are singletons, thus we're trying to bypass
76    // the PyObject_IsTrue triggering __nonzero__ protocol.
77    if(tmp == Py_True) {
78        res = 1;
79    } else if (tmp == Py_False) {
80        res = 0;
81    } else {
82        res = PyObject_IsTrue(tmp);
83        if(res == -1) {
84            Py_DECREF(tmp);
85            return NULL;
86        }
87    }
88    Py_DECREF(tmp);
89    if (res) {
90        PyObject *tuple = PyTuple_Pack(1, l);
91        if (!tuple) {
92            return NULL;
93        }
94        PyObject *iter = PyObject_GetIter(tuple);
95        Py_DECREF(tuple);
96        return iter;
97    }
98
99    self = (pkgcore_iflatten_func *)type->tp_alloc(type, 0);
100    if (!self)
101        return NULL;
102
103    self->in_iternext = 0;
104
105    if (!(self->iterables = build_initial_iterables(l))) {
106        Py_DECREF(self);
107        return NULL;
108    }
109
110    Py_INCREF(skip_func);
111    self->skip_func = skip_func;
112
113    return (PyObject *)self;
114}
115
116static PyObject *
117pkgcore_iflatten_func_iternext(pkgcore_iflatten_func *self) {
118    PyObject *tail, *result, *tmp;
119    int res;
120    Py_ssize_t n;
121
122    if (self->in_iternext) {
123        /* We do not allow this because it means our list could be
124         * manipulated while we are running. Exception raised matches
125         * what a generator raises if you try the same thing.
126         */
127        PyErr_SetString(PyExc_ValueError,
128                        "Recursive calls to iflatten_func.next are illegal");
129        return NULL;
130    }
131    self->in_iternext = 1;
132
133    /* Look at the final iterator on our stack: */
134    while ((n = PyList_GET_SIZE(self->iterables))) {
135        tail = PyList_GET_ITEM(self->iterables, n - 1);
136
137        /* See if it has any results left: */
138
139        /* (This reference is borrowed from the list, but since we
140           disallow recursive calls in here it should be safe to not
141           increment it). */
142
143        result = PyIter_Next(tail);
144        if (result) {
145
146            /* See if we need to iterate over this new result: */
147
148            tmp = PyObject_CallFunctionObjArgs(self->skip_func, result, NULL);
149            if (!tmp) {
150                Py_DECREF(result);
151                self->in_iternext = 0;
152                return NULL;
153            }
154            res = PyObject_IsTrue(tmp);
155            Py_DECREF(tmp);
156            if (res == -1) {
157                Py_DECREF(result);
158                result = NULL;
159            } else if (!res) {
160                /* False from our skip func. */
161                /* If it is an iterator add it to our chain, else return it. */
162                tmp = PyObject_GetIter(result);
163                if (tmp) {
164                    /* Iterable, append to our stack and continue. */
165                    Py_DECREF(result);
166                    result = NULL;
167                    res = PyList_Append(self->iterables, tmp);
168                    Py_DECREF(tmp);
169                    if (res != -1) {
170                        continue;
171                    }
172                    /* Fall through and propagate the error. */
173                } else {
174                    /* If we get here PyObject_GetIter raised an exception.
175                     * If it was TypeError we have a non-iterator we can
176                     * just return, else we propagate the error.
177                     */
178                    if (PyErr_ExceptionMatches(PyExc_TypeError)) {
179                        PyErr_Clear();
180                    } else {
181                        Py_DECREF(result);
182                        result = NULL;
183                    }
184                }
185            }
186        } else {
187            /* PyIter_Next did not return an item. If this was not
188             * because of an error we should pop the exhausted
189             * iterable off and continue. */
190            if (!PyErr_Occurred() &&
191                PySequence_DelItem(self->iterables, n - 1) != -1) {
192                continue;
193            }
194        }
195        self->in_iternext = 0;
196        return result;
197    }
198
199    /* We ran out of iterables entirely, so we are done */
200    self->in_iternext = 0;
201    return NULL;
202}
203
204PyDoc_STRVAR(
205    pkgcore_iflatten_func_documentation,
206    "iflatten_func(iters, func): collapse [(1),2] into [1,2]\n"
207    "\n"
208    "func is called with one argument and should return true if this \n"
209    "should not be iterated over.\n"
210    );
211
212static PyTypeObject pkgcore_iflatten_func_type = {
213    PyObject_HEAD_INIT(NULL)
214    0,                                               /* ob_size*/
215    "pkgcore.util._lists.iflatten_func",             /* tp_name*/
216    sizeof(pkgcore_iflatten_func),                   /* tp_basicsize*/
217    0,                                               /* tp_itemsize*/
218    (destructor)pkgcore_iflatten_func_dealloc,       /* tp_dealloc*/
219    0,                                               /* tp_print*/
220    0,                                               /* tp_getattr*/
221    0,                                               /* tp_setattr*/
222    0,                                               /* tp_compare*/
223    0,                                               /* tp_repr*/
224    0,                                               /* tp_as_number*/
225    0,                                               /* tp_as_sequence*/
226    0,                                               /* tp_as_mapping*/
227    0,                                               /* tp_hash */
228    (ternaryfunc)0,                                  /* tp_call*/
229    (reprfunc)0,                                     /* tp_str*/
230    0,                                               /* tp_getattro*/
231    0,                                               /* tp_setattro*/
232    0,                                               /* tp_as_buffer*/
233    Py_TPFLAGS_DEFAULT,                              /* tp_flags*/
234    pkgcore_iflatten_func_documentation,             /* tp_doc */
235    (traverseproc)0,                                 /* tp_traverse */
236    (inquiry)0,                                      /* tp_clear */
237    (richcmpfunc)0,                                  /* tp_richcompare */
238    0,                                               /* tp_weaklistoffset */
239    (getiterfunc)PyObject_SelfIter,                  /* tp_iter */
240    (iternextfunc)pkgcore_iflatten_func_iternext,    /* tp_iternext */
241    0,                                               /* tp_methods */
242    0,                                               /* tp_members */
243    0,                                               /* tp_getset */
244    0,                                               /* tp_base */
245    0,                                               /* tp_dict */
246    0,                                               /* tp_descr_get */
247    0,                                               /* tp_descr_set */
248    0,                                               /* tp_dictoffset */
249    (initproc)0,                                     /* tp_init */
250    0,                                               /* tp_alloc */
251    pkgcore_iflatten_func_new,                       /* tp_new */
252};
253
254/* iflatten_instance: recursively flatten an iterable
255   except for some instances */
256
257typedef struct {
258    PyObject_HEAD
259    PyObject *skip_flattening;
260    PyObject *iterables;
261    char in_iternext;
262} pkgcore_iflatten_instance;
263
264static void
265pkgcore_iflatten_instance_dealloc(pkgcore_iflatten_instance *self)
266{
267    Py_CLEAR(self->skip_flattening);
268    Py_CLEAR(self->iterables);
269    self->ob_type->tp_free((PyObject*) self);
270}
271
272static PyObject *
273pkgcore_iflatten_instance_new(PyTypeObject *type,
274                                  PyObject *args, PyObject *kwargs)
275{
276    pkgcore_iflatten_instance *self;
277    PyObject *l=NULL, *skip_flattening=(PyObject*)&PyBaseString_Type;
278    int res;
279
280    if (kwargs && PyDict_Size(kwargs)) {
281        PyErr_SetString(PyExc_TypeError,
282                        "iflatten_instance takes no keyword arguments");
283        return NULL;
284    }
285    if (!PyArg_UnpackTuple(args, "iflatten_instance", 1, 2,
286                           &l, &skip_flattening)) {
287        return NULL;
288    }
289
290    /* Check if we got a single argument that should be skipped. */
291    res = PyObject_IsInstance(l, skip_flattening);
292    if (res == -1) {
293        return NULL;
294    } else if (res) {
295        PyObject *tuple = PyTuple_Pack(1, l);
296        if (!tuple) {
297            return NULL;
298        }
299        PyObject *iter = PyObject_GetIter(tuple);
300        Py_DECREF(tuple);
301        return iter;
302    }
303
304    self = (pkgcore_iflatten_instance *)type->tp_alloc(type, 0);
305    if (!self)
306        return NULL;
307
308    self->in_iternext = 0;
309
310    if (!(self->iterables = build_initial_iterables(l))) {
311        Py_DECREF(self);
312        return NULL;
313    }
314
315    Py_INCREF(skip_flattening);
316    self->skip_flattening = skip_flattening;
317
318    return (PyObject *)self;
319}
320
321static PyObject *
322pkgcore_iflatten_instance_iternext(pkgcore_iflatten_instance *self) {
323    PyObject *tail, *result, *iter;
324    int n, res;
325
326    if (self->in_iternext) {
327        /* We do not allow this because it means our list could be
328         * manipulated while we are running. Exception raised matches
329         * what a generator raises if you try the same thing.
330         */
331        PyErr_SetString(
332            PyExc_ValueError,
333            "Recursive calls to iflatten_instance.next are illegal");
334        return NULL;
335    }
336    self->in_iternext = 1;
337
338    /* Look at the final iterator on our stack: */
339
340    while ((n = PyList_GET_SIZE(self->iterables))) {
341        tail = PyList_GET_ITEM(self->iterables, n - 1);
342
343        /* See if it has any results left: */
344        /* (This reference is borrowed from the list, but since we
345           disallow recursive calls in here it should be safe to not
346           increment it). */
347
348        result = PyIter_Next(tail);
349        if (result) {
350            /* See if we need to iterate over this new result: */
351
352            res = PyObject_IsInstance(result, self->skip_flattening);
353            if (res == -1) {
354                Py_DECREF(result);
355                result = NULL;
356            } else if (!res) {
357                /* Not in skip_flattening. */
358                /* If it is an iterator add it to our chain, else return it. */
359                iter = PyObject_GetIter(result);
360                if (iter) {
361                    /* Iterable, append to our stack and continue. */
362                    Py_DECREF(result);
363                    result = NULL;
364                    res = PyList_Append(self->iterables, iter);
365                    Py_DECREF(iter);
366                    if (res != -1) {
367                        continue;
368                    }
369                    /* Fall through and propagate the error. */
370                } else {
371                    /* If we get here PyObject_GetIter raised an exception.
372                     * If it was TypeError we have a non-iterator we can
373                     * just return, else we propagate the error.
374                     */
375                    if (PyErr_ExceptionMatches(PyExc_TypeError)) {
376                        PyErr_Clear();
377                    } else {
378                        Py_DECREF(result);
379                        result = NULL;
380                    }
381                }
382            }
383        } else {
384            /* PyIter_Next did not return an item. If this was not
385             * because of an error we should pop the exhausted
386             * iterable off and continue. */
387            if (!PyErr_Occurred() &&
388                PySequence_DelItem(self->iterables, n - 1) != -1) {
389                continue;
390            }
391        }
392        self->in_iternext = 0;
393        return result;
394    }
395
396    /* We ran out of iterables entirely, so we are done */
397    self->in_iternext = 0;
398    return NULL;
399}
400
401PyDoc_STRVAR(
402    pkgcore_iflatten_instance_documentation,
403    "iflatten_func(iters, skip_flattening=basestring)\n"
404    "\n"
405    "collapse [(1),2] into [1,2]\n"
406    "skip_flattening is a list of classes to not descend through\n"
407    );
408
409static PyTypeObject pkgcore_iflatten_instance_type = {
410    PyObject_HEAD_INIT(NULL)
411    0,                                               /* ob_size*/
412    "pkgcore.util._lists.iflatten_instance",         /* tp_name*/
413    sizeof(pkgcore_iflatten_instance),               /* tp_basicsize*/
414    0,                                               /* tp_itemsize*/
415    (destructor)pkgcore_iflatten_instance_dealloc,   /* tp_dealloc*/
416    0,                                               /* tp_print*/
417    0,                                               /* tp_getattr*/
418    0,                                               /* tp_setattr*/
419    0,                                               /* tp_compare*/
420    0,                                               /* tp_repr*/
421    0,                                               /* tp_as_number*/
422    0,                                               /* tp_as_sequence*/
423    0,                                               /* tp_as_mapping*/
424    0,                                               /* tp_hash */
425    (ternaryfunc)0,                                  /* tp_call*/
426    (reprfunc)0,                                     /* tp_str*/
427    0,                                               /* tp_getattro*/
428    0,                                               /* tp_setattro*/
429    0,                                               /* tp_as_buffer*/
430    Py_TPFLAGS_DEFAULT,                              /* tp_flags*/
431    pkgcore_iflatten_instance_documentation,         /* tp_doc */
432    (traverseproc)0,                                 /* tp_traverse */
433    (inquiry)0,                                      /* tp_clear */
434    (richcmpfunc)0,                                  /* tp_richcompare */
435    0,                                               /* tp_weaklistoffset */
436    (getiterfunc)PyObject_SelfIter,                  /* tp_iter */
437    (iternextfunc)pkgcore_iflatten_instance_iternext, /* tp_iternext */
438    0,                                               /* tp_methods */
439    0,                                               /* tp_members */
440    0,                                               /* tp_getset */
441    0,                                               /* tp_base */
442    0,                                               /* tp_dict */
443    0,                                               /* tp_descr_get */
444    0,                                               /* tp_descr_set */
445    0,                                               /* tp_dictoffset */
446    (initproc)0,                                     /* tp_init */
447    0,                                               /* tp_alloc */
448    pkgcore_iflatten_instance_new,                   /* tp_new */
449};
450
451
452/* Initialization function for the module */
453
454PyDoc_STRVAR(
455    pkgcore_lists_documentation,
456    "C reimplementation of some of pkgcore.util.lists.");
457
458PyMODINIT_FUNC
459init_lists()
460{
461    /* Create the module and add the functions */
462    PyObject *m = Py_InitModule3("_lists", NULL, pkgcore_lists_documentation);
463    if (!m)
464        return;
465
466    if (PyType_Ready(&pkgcore_iflatten_func_type) < 0)
467        return;
468
469    if (PyType_Ready(&pkgcore_iflatten_instance_type) < 0)
470        return;
471
472    Py_INCREF(&pkgcore_iflatten_func_type);
473    if (PyModule_AddObject(
474            m, "iflatten_func", (PyObject *)&pkgcore_iflatten_func_type) == -1)
475        return;
476
477    Py_INCREF(&pkgcore_iflatten_instance_type);
478    if (PyModule_AddObject(
479            m, "iflatten_instance",
480            (PyObject *)&pkgcore_iflatten_instance_type) == -1)
481        return;
482}
Note: See TracBrowser for help on using the browser.