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 Py_DECREF(pto); 132 return NULL; 133 } 134 pto->kw = (kw != NULL) ? PyDict_Copy(kw) : PyDict_New(); 135 if (pto->kw == NULL) { 136 Py_DECREF(pto); 137 return NULL; 138 } 139 140 return (PyObject *)pto; 141 } 142 143 static void 144 partial_dealloc(partialobject *pto) 145 { 146 PyObject_GC_UnTrack(pto); 147 if (pto->weakreflist != NULL) 148 PyObject_ClearWeakRefs((PyObject *) pto); 149 Py_XDECREF(pto->fn); 150 Py_XDECREF(pto->args); 151 Py_XDECREF(pto->kw); 152 Py_XDECREF(pto->dict); 153 Py_TYPE(pto)->tp_free(pto); 154 } 155 156 static PyObject * 157 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 if (PyTuple_GET_SIZE(pto->args) == 0) { 167 argappl = args; 168 Py_INCREF(args); 169 } else if (PyTuple_GET_SIZE(args) == 0) { 170 argappl = pto->args; 171 Py_INCREF(pto->args); 172 } else { 173 argappl = PySequence_Concat(pto->args, args); 174 if (argappl == NULL) 175 return NULL; 176 assert(PyTuple_Check(argappl)); 177 } 178 179 if (PyDict_Size(pto->kw) == 0) { 180 kwappl = kw; 181 Py_XINCREF(kwappl); 182 } else { 183 kwappl = PyDict_Copy(pto->kw); 184 if (kwappl == NULL) { 185 Py_DECREF(argappl); 186 return NULL; 187 } 188 if (kw != NULL) { 189 if (PyDict_Merge(kwappl, kw, 1) != 0) { 190 Py_DECREF(argappl); 191 Py_DECREF(kwappl); 192 return NULL; 193 } 194 } 195 } 196 197 ret = PyObject_Call(pto->fn, argappl, kwappl); 198 Py_DECREF(argappl); 199 Py_XDECREF(kwappl); 200 return ret; 201 } 202 203 static int 204 partial_traverse(partialobject *pto, visitproc visit, void *arg) 205 { 206 Py_VISIT(pto->fn); 207 Py_VISIT(pto->args); 208 Py_VISIT(pto->kw); 209 Py_VISIT(pto->dict); 210 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 partial_get_dict(partialobject *pto) 230 { 231 if (pto->dict == NULL) { 232 pto->dict = PyDict_New(); 233 if (pto->dict == NULL) 234 return NULL; 235 } 236 Py_INCREF(pto->dict); 237 return pto->dict; 238 } 239 240 static int 241 partial_set_dict(partialobject *pto, PyObject *value) 242 { 243 PyObject *tmp; 244 245 /* It is illegal to del p.__dict__ */ 246 if (value == NULL) { 247 PyErr_SetString(PyExc_TypeError, 248 "a partial object's dictionary may not be deleted"); 249 return -1; 250 } 251 /* Can only set __dict__ to a dictionary */ 252 if (!PyDict_Check(value)) { 253 PyErr_SetString(PyExc_TypeError, 254 "setting partial object's dictionary to a non-dict"); 255 return -1; 256 } 257 tmp = pto->dict; 258 Py_INCREF(value); 259 pto->dict = value; 260 Py_XDECREF(tmp); 261 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 partial_reduce(partialobject *pto, PyObject *unused) 278 { 279 return Py_BuildValue("O(O)(OOOO)", Py_TYPE(pto), pto->fn, pto->fn, 280 pto->args, pto->kw, 281 pto->dict ? pto->dict : Py_None); 282 } 283 284 PyObject * 285 partial_setstate(partialobject *pto, PyObject *state) 286 { 287 PyObject *fn, *fnargs, *kw, *dict; 288 289 if (!PyTuple_Check(state) || 290 !PyArg_ParseTuple(state, "OOOO", &fn, &fnargs, &kw, &dict) || 291 !PyCallable_Check(fn) || 292 !PyTuple_Check(fnargs) || 293 (kw != Py_None && !PyDict_Check(kw))) 294 { 295 PyErr_SetString(PyExc_TypeError, "invalid partial state"); 296 return NULL; 297 } 298 299 if(!PyTuple_CheckExact(fnargs)) 300 fnargs = PySequence_Tuple(fnargs); 301 else 302 Py_INCREF(fnargs); 303 if (fnargs == NULL) 304 return NULL; 305 306 if (kw == Py_None) 307 kw = PyDict_New(); 308 else if(!PyDict_CheckExact(kw)) 309 kw = PyDict_Copy(kw); 310 else 311 Py_INCREF(kw); 312 if (kw == NULL) { 313 Py_DECREF(fnargs); 314 return NULL; 315 } 316 317 Py_INCREF(fn); 318 if (dict == Py_None) 319 dict = NULL; 320 else 321 Py_INCREF(dict); 322 323 Py_SETREF(pto->fn, fn); 324 Py_SETREF(pto->args, fnargs); 325 Py_SETREF(pto->kw, kw); 326 Py_XSETREF(pto->dict, dict); 327 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 init_functools(void) 393 { 394 int i; 395 PyObject *m; 396 char *name; 397 PyTypeObject *typelist[] = { 398 &partial_type, 399 NULL 400 }; 401 402 m = Py_InitModule3("_functools", module_methods, module_doc); 403 if (m == NULL) 404 return; 405 406 for (i=0 ; typelist[i] != NULL ; i++) { 407 if (PyType_Ready(typelist[i]) < 0) 408 return; 409 name = strchr(typelist[i]->tp_name, '.'); 410 assert (name != NULL); 411 Py_INCREF(typelist[i]); 412 PyModule_AddObject(m, name+1, (PyObject *)typelist[i]); 413 } 414 } 415