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