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 }