Home | History | Annotate | Download | only in Modules
      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