Python-2.7.3/Modules/_functoolsmodule.c

No issues found

  1 #include "Python.h"
  2 #include "structmember.h"
  3 
  4 /* _functools module written and maintained
  5    by Hye-Shik Chang <perky@FreeBSD.org>
  6    with adaptations by Raymond Hettinger <python@rcn.com>
  7    Copyright (c) 2004, 2005, 2006 Python Software Foundation.
  8    All rights reserved.
  9 */
 10 
 11 /* reduce() *************************************************************/
 12 
 13 static PyObject *
 14 functools_reduce(PyObject *self, PyObject *args)
 15 {
 16     PyObject *seq, *func, *result = NULL, *it;
 17 
 18     if (!PyArg_UnpackTuple(args, "reduce", 2, 3, &func, &seq, &result))
 19         return NULL;
 20     if (result != NULL)
 21         Py_INCREF(result);
 22 
 23     it = PyObject_GetIter(seq);
 24     if (it == NULL) {
 25         PyErr_SetString(PyExc_TypeError,
 26             "reduce() arg 2 must support iteration");
 27         Py_XDECREF(result);
 28         return NULL;
 29     }
 30 
 31     if ((args = PyTuple_New(2)) == NULL)
 32         goto Fail;
 33 
 34     for (;;) {
 35         PyObject *op2;
 36 
 37         if (args->ob_refcnt > 1) {
 38             Py_DECREF(args);
 39             if ((args = PyTuple_New(2)) == NULL)
 40                 goto Fail;
 41         }
 42 
 43         op2 = PyIter_Next(it);
 44         if (op2 == NULL) {
 45             if (PyErr_Occurred())
 46                 goto Fail;
 47             break;
 48         }
 49 
 50         if (result == NULL)
 51             result = op2;
 52         else {
 53             PyTuple_SetItem(args, 0, result);
 54             PyTuple_SetItem(args, 1, op2);
 55             if ((result = PyEval_CallObject(func, args)) == NULL)
 56                 goto Fail;
 57         }
 58     }
 59 
 60     Py_DECREF(args);
 61 
 62     if (result == NULL)
 63         PyErr_SetString(PyExc_TypeError,
 64                    "reduce() of empty sequence with no initial value");
 65 
 66     Py_DECREF(it);
 67     return result;
 68 
 69 Fail:
 70     Py_XDECREF(args);
 71     Py_XDECREF(result);
 72     Py_DECREF(it);
 73     return NULL;
 74 }
 75 
 76 PyDoc_STRVAR(reduce_doc,
 77 "reduce(function, sequence[, initial]) -> value\n\
 78 \n\
 79 Apply a function of two arguments cumulatively to the items of a sequence,\n\
 80 from left to right, so as to reduce the sequence to a single value.\n\
 81 For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates\n\
 82 ((((1+2)+3)+4)+5).  If initial is present, it is placed before the items\n\
 83 of the sequence in the calculation, and serves as a default when the\n\
 84 sequence is empty.");
 85 
 86 
 87 
 88 
 89 /* partial object **********************************************************/
 90 
 91 typedef struct {
 92     PyObject_HEAD
 93     PyObject *fn;
 94     PyObject *args;
 95     PyObject *kw;
 96     PyObject *dict;
 97     PyObject *weakreflist; /* List of weak references */
 98 } partialobject;
 99 
100 static PyTypeObject partial_type;
101 
102 static PyObject *
103 partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
104 {
105     PyObject *func;
106     partialobject *pto;
107 
108     if (PyTuple_GET_SIZE(args) < 1) {
109         PyErr_SetString(PyExc_TypeError,
110                         "type 'partial' takes at least one argument");
111         return NULL;
112     }
113 
114     func = PyTuple_GET_ITEM(args, 0);
115     if (!PyCallable_Check(func)) {
116         PyErr_SetString(PyExc_TypeError,
117                         "the first argument must be callable");
118         return NULL;
119     }
120 
121     /* create partialobject structure */
122     pto = (partialobject *)type->tp_alloc(type, 0);
123     if (pto == NULL)
124         return NULL;
125 
126     pto->fn = func;
127     Py_INCREF(func);
128     pto->args = PyTuple_GetSlice(args, 1, PY_SSIZE_T_MAX);
129     if (pto->args == NULL) {
130         pto->kw = NULL;
131         Py_DECREF(pto);
132         return NULL;
133     }
134     if (kw != NULL) {
135         pto->kw = PyDict_Copy(kw);
136         if (pto->kw == NULL) {
137             Py_DECREF(pto);
138             return NULL;
139         }
140     } else {
141         pto->kw = Py_None;
142         Py_INCREF(Py_None);
143     }
144 
145     pto->weakreflist = NULL;
146     pto->dict = NULL;
147 
148     return (PyObject *)pto;
149 }
150 
151 static void
152 partial_dealloc(partialobject *pto)
153 {
154     PyObject_GC_UnTrack(pto);
155     if (pto->weakreflist != NULL)
156         PyObject_ClearWeakRefs((PyObject *) pto);
157     Py_XDECREF(pto->fn);
158     Py_XDECREF(pto->args);
159     Py_XDECREF(pto->kw);
160     Py_XDECREF(pto->dict);
161     Py_TYPE(pto)->tp_free(pto);
162 }
163 
164 static PyObject *
165 partial_call(partialobject *pto, PyObject *args, PyObject *kw)
166 {
167     PyObject *ret;
168     PyObject *argappl = NULL, *kwappl = NULL;
169 
170     assert (PyCallable_Check(pto->fn));
171     assert (PyTuple_Check(pto->args));
172     assert (pto->kw == Py_None  ||  PyDict_Check(pto->kw));
173 
174     if (PyTuple_GET_SIZE(pto->args) == 0) {
175         argappl = args;
176         Py_INCREF(args);
177     } else if (PyTuple_GET_SIZE(args) == 0) {
178         argappl = pto->args;
179         Py_INCREF(pto->args);
180     } else {
181         argappl = PySequence_Concat(pto->args, args);
182         if (argappl == NULL)
183             return NULL;
184     }
185 
186     if (pto->kw == Py_None) {
187         kwappl = kw;
188         Py_XINCREF(kw);
189     } else {
190         kwappl = PyDict_Copy(pto->kw);
191         if (kwappl == NULL) {
192             Py_DECREF(argappl);
193             return NULL;
194         }
195         if (kw != NULL) {
196             if (PyDict_Merge(kwappl, kw, 1) != 0) {
197                 Py_DECREF(argappl);
198                 Py_DECREF(kwappl);
199                 return NULL;
200             }
201         }
202     }
203 
204     ret = PyObject_Call(pto->fn, argappl, kwappl);
205     Py_DECREF(argappl);
206     Py_XDECREF(kwappl);
207     return ret;
208 }
209 
210 static int
211 partial_traverse(partialobject *pto, visitproc visit, void *arg)
212 {
213     Py_VISIT(pto->fn);
214     Py_VISIT(pto->args);
215     Py_VISIT(pto->kw);
216     Py_VISIT(pto->dict);
217     return 0;
218 }
219 
220 PyDoc_STRVAR(partial_doc,
221 "partial(func, *args, **keywords) - new function with partial application\n\
222     of the given arguments and keywords.\n");
223 
224 #define OFF(x) offsetof(partialobject, x)
225 static PyMemberDef partial_memberlist[] = {
226     {"func",            T_OBJECT,       OFF(fn),        READONLY,
227      "function object to use in future partial calls"},
228     {"args",            T_OBJECT,       OFF(args),      READONLY,
229      "tuple of arguments to future partial calls"},
230     {"keywords",        T_OBJECT,       OFF(kw),        READONLY,
231      "dictionary of keyword arguments to future partial calls"},
232     {NULL}  /* Sentinel */
233 };
234 
235 static PyObject *
236 partial_get_dict(partialobject *pto)
237 {
238     if (pto->dict == NULL) {
239         pto->dict = PyDict_New();
240         if (pto->dict == NULL)
241             return NULL;
242     }
243     Py_INCREF(pto->dict);
244     return pto->dict;
245 }
246 
247 static int
248 partial_set_dict(partialobject *pto, PyObject *value)
249 {
250     PyObject *tmp;
251 
252     /* It is illegal to del p.__dict__ */
253     if (value == NULL) {
254         PyErr_SetString(PyExc_TypeError,
255                         "a partial object's dictionary may not be deleted");
256         return -1;
257     }
258     /* Can only set __dict__ to a dictionary */
259     if (!PyDict_Check(value)) {
260         PyErr_SetString(PyExc_TypeError,
261                         "setting partial object's dictionary to a non-dict");
262         return -1;
263     }
264     tmp = pto->dict;
265     Py_INCREF(value);
266     pto->dict = value;
267     Py_XDECREF(tmp);
268     return 0;
269 }
270 
271 static PyGetSetDef partial_getsetlist[] = {
272     {"__dict__", (getter)partial_get_dict, (setter)partial_set_dict},
273     {NULL} /* Sentinel */
274 };
275 
276 /* Pickle strategy:
277    __reduce__ by itself doesn't support getting kwargs in the unpickle
278    operation so we define a __setstate__ that replaces all the information
279    about the partial.  If we only replaced part of it someone would use
280    it as a hook to do strange things.
281  */
282 
283 PyObject *
284 partial_reduce(partialobject *pto, PyObject *unused)
285 {
286     return Py_BuildValue("O(O)(OOOO)", Py_TYPE(pto), pto->fn, pto->fn,
287                          pto->args, pto->kw,
288                          pto->dict ? pto->dict : Py_None);
289 }
290 
291 PyObject *
292 partial_setstate(partialobject *pto, PyObject *args)
293 {
294     PyObject *fn, *fnargs, *kw, *dict;
295     if (!PyArg_ParseTuple(args, "(OOOO):__setstate__",
296                           &fn, &fnargs, &kw, &dict))
297         return NULL;
298     Py_XDECREF(pto->fn);
299     Py_XDECREF(pto->args);
300     Py_XDECREF(pto->kw);
301     Py_XDECREF(pto->dict);
302     pto->fn = fn;
303     pto->args = fnargs;
304     pto->kw = kw;
305     if (dict != Py_None) {
306       pto->dict = dict;
307       Py_INCREF(dict);
308     } else {
309       pto->dict = NULL;
310     }
311     Py_INCREF(fn);
312     Py_INCREF(fnargs);
313     Py_INCREF(kw);
314     Py_RETURN_NONE;
315 }
316 
317 static PyMethodDef partial_methods[] = {
318     {"__reduce__", (PyCFunction)partial_reduce, METH_NOARGS},
319     {"__setstate__", (PyCFunction)partial_setstate, METH_VARARGS},
320     {NULL,              NULL}           /* sentinel */
321 };
322 
323 static PyTypeObject partial_type = {
324     PyVarObject_HEAD_INIT(NULL, 0)
325     "functools.partial",                /* tp_name */
326     sizeof(partialobject),              /* tp_basicsize */
327     0,                                  /* tp_itemsize */
328     /* methods */
329     (destructor)partial_dealloc,        /* tp_dealloc */
330     0,                                  /* tp_print */
331     0,                                  /* tp_getattr */
332     0,                                  /* tp_setattr */
333     0,                                  /* tp_compare */
334     0,                                  /* tp_repr */
335     0,                                  /* tp_as_number */
336     0,                                  /* tp_as_sequence */
337     0,                                  /* tp_as_mapping */
338     0,                                  /* tp_hash */
339     (ternaryfunc)partial_call,          /* tp_call */
340     0,                                  /* tp_str */
341     PyObject_GenericGetAttr,            /* tp_getattro */
342     PyObject_GenericSetAttr,            /* tp_setattro */
343     0,                                  /* tp_as_buffer */
344     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
345         Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_WEAKREFS,         /* tp_flags */
346     partial_doc,                        /* tp_doc */
347     (traverseproc)partial_traverse,     /* tp_traverse */
348     0,                                  /* tp_clear */
349     0,                                  /* tp_richcompare */
350     offsetof(partialobject, weakreflist),       /* tp_weaklistoffset */
351     0,                                  /* tp_iter */
352     0,                                  /* tp_iternext */
353     partial_methods,                    /* tp_methods */
354     partial_memberlist,                 /* tp_members */
355     partial_getsetlist,                 /* tp_getset */
356     0,                                  /* tp_base */
357     0,                                  /* tp_dict */
358     0,                                  /* tp_descr_get */
359     0,                                  /* tp_descr_set */
360     offsetof(partialobject, dict),      /* tp_dictoffset */
361     0,                                  /* tp_init */
362     0,                                  /* tp_alloc */
363     partial_new,                        /* tp_new */
364     PyObject_GC_Del,                    /* tp_free */
365 };
366 
367 
368 /* module level code ********************************************************/
369 
370 PyDoc_STRVAR(module_doc,
371 "Tools that operate on functions.");
372 
373 static PyMethodDef module_methods[] = {
374     {"reduce",          functools_reduce,     METH_VARARGS, reduce_doc},
375     {NULL,              NULL}           /* sentinel */
376 };
377 
378 PyMODINIT_FUNC
379 init_functools(void)
380 {
381     int i;
382     PyObject *m;
383     char *name;
384     PyTypeObject *typelist[] = {
385         &partial_type,
386         NULL
387     };
388 
389     m = Py_InitModule3("_functools", module_methods, module_doc);
390     if (m == NULL)
391         return;
392 
393     for (i=0 ; typelist[i] != NULL ; i++) {
394         if (PyType_Ready(typelist[i]) < 0)
395             return;
396         name = strchr(typelist[i]->tp_name, '.');
397         assert (name != NULL);
398         Py_INCREF(typelist[i]);
399         PyModule_AddObject(m, name+1, (PyObject *)typelist[i]);
400     }
401 }