root/releases/pkgcore/0.2.1/src/lists.c @ marienz%2540gentoo.org-20061129021619-h92kexkktibvv60m

Revision marienz%2540gentoo.org-20061129021619-h92kexkktibvv60m, 16.6 KB (checked in by Marien Zwart <marienz@…>, 2 years ago)

Improve error handling in cpython module initialization.

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