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