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         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     /* bpo-31095: UnTrack is needed before calling any callbacks */
    147     PyObject_GC_UnTrack(pto);
    148     if (pto->weakreflist != NULL)
    149         PyObject_ClearWeakRefs((PyObject *) pto);
    150     Py_XDECREF(pto->fn);
    151     Py_XDECREF(pto->args);
    152     Py_XDECREF(pto->kw);
    153     Py_XDECREF(pto->dict);
    154     Py_TYPE(pto)->tp_free(pto);
    155 }
    156 
    157 static PyObject *
    158 partial_call(partialobject *pto, PyObject *args, PyObject *kw)
    159 {
    160     PyObject *ret;
    161     PyObject *argappl, *kwappl;
    162 
    163     assert (PyCallable_Check(pto->fn));
    164     assert (PyTuple_Check(pto->args));
    165     assert (PyDict_Check(pto->kw));
    166 
    167     if (PyTuple_GET_SIZE(pto->args) == 0) {
    168         argappl = args;
    169         Py_INCREF(args);
    170     } else if (PyTuple_GET_SIZE(args) == 0) {
    171         argappl = pto->args;
    172         Py_INCREF(pto->args);
    173     } else {
    174         argappl = PySequence_Concat(pto->args, args);
    175         if (argappl == NULL)
    176             return NULL;
    177         assert(PyTuple_Check(argappl));
    178     }
    179 
    180     if (PyDict_Size(pto->kw) == 0) {
    181         kwappl = kw;
    182         Py_XINCREF(kwappl);
    183     } else {
    184         kwappl = PyDict_Copy(pto->kw);
    185         if (kwappl == NULL) {
    186             Py_DECREF(argappl);
    187             return NULL;
    188         }
    189         if (kw != NULL) {
    190             if (PyDict_Merge(kwappl, kw, 1) != 0) {
    191                 Py_DECREF(argappl);
    192                 Py_DECREF(kwappl);
    193                 return NULL;
    194             }
    195         }
    196     }
    197 
    198     ret = PyObject_Call(pto->fn, argappl, kwappl);
    199     Py_DECREF(argappl);
    200     Py_XDECREF(kwappl);
    201     return ret;
    202 }
    203 
    204 static int
    205 partial_traverse(partialobject *pto, visitproc visit, void *arg)
    206 {
    207     Py_VISIT(pto->fn);
    208     Py_VISIT(pto->args);
    209     Py_VISIT(pto->kw);
    210     Py_VISIT(pto->dict);
    211     return 0;
    212 }
    213 
    214 PyDoc_STRVAR(partial_doc,
    215 "partial(func, *args, **keywords) - new function with partial application\n\
    216     of the given arguments and keywords.\n");
    217 
    218 #define OFF(x) offsetof(partialobject, x)
    219 static PyMemberDef partial_memberlist[] = {
    220     {"func",            T_OBJECT,       OFF(fn),        READONLY,
    221      "function object to use in future partial calls"},
    222     {"args",            T_OBJECT,       OFF(args),      READONLY,
    223      "tuple of arguments to future partial calls"},
    224     {"keywords",        T_OBJECT,       OFF(kw),        READONLY,
    225      "dictionary of keyword arguments to future partial calls"},
    226     {NULL}  /* Sentinel */
    227 };
    228 
    229 static PyObject *
    230 partial_get_dict(partialobject *pto)
    231 {
    232     if (pto->dict == NULL) {
    233         pto->dict = PyDict_New();
    234         if (pto->dict == NULL)
    235             return NULL;
    236     }
    237     Py_INCREF(pto->dict);
    238     return pto->dict;
    239 }
    240 
    241 static int
    242 partial_set_dict(partialobject *pto, PyObject *value)
    243 {
    244     PyObject *tmp;
    245 
    246     /* It is illegal to del p.__dict__ */
    247     if (value == NULL) {
    248         PyErr_SetString(PyExc_TypeError,
    249                         "a partial object's dictionary may not be deleted");
    250         return -1;
    251     }
    252     /* Can only set __dict__ to a dictionary */
    253     if (!PyDict_Check(value)) {
    254         PyErr_SetString(PyExc_TypeError,
    255                         "setting partial object's dictionary to a non-dict");
    256         return -1;
    257     }
    258     tmp = pto->dict;
    259     Py_INCREF(value);
    260     pto->dict = value;
    261     Py_XDECREF(tmp);
    262     return 0;
    263 }
    264 
    265 static PyGetSetDef partial_getsetlist[] = {
    266     {"__dict__", (getter)partial_get_dict, (setter)partial_set_dict},
    267     {NULL} /* Sentinel */
    268 };
    269 
    270 /* Pickle strategy:
    271    __reduce__ by itself doesn't support getting kwargs in the unpickle
    272    operation so we define a __setstate__ that replaces all the information
    273    about the partial.  If we only replaced part of it someone would use
    274    it as a hook to do strange things.
    275  */
    276 
    277 PyObject *
    278 partial_reduce(partialobject *pto, PyObject *unused)
    279 {
    280     return Py_BuildValue("O(O)(OOOO)", Py_TYPE(pto), pto->fn, pto->fn,
    281                          pto->args, pto->kw,
    282                          pto->dict ? pto->dict : Py_None);
    283 }
    284 
    285 PyObject *
    286 partial_setstate(partialobject *pto, PyObject *state)
    287 {
    288     PyObject *fn, *fnargs, *kw, *dict;
    289 
    290     if (!PyTuple_Check(state) ||
    291         !PyArg_ParseTuple(state, "OOOO", &fn, &fnargs, &kw, &dict) ||
    292         !PyCallable_Check(fn) ||
    293         !PyTuple_Check(fnargs) ||
    294         (kw != Py_None && !PyDict_Check(kw)))
    295     {
    296         PyErr_SetString(PyExc_TypeError, "invalid partial state");
    297         return NULL;
    298     }
    299 
    300     if(!PyTuple_CheckExact(fnargs))
    301         fnargs = PySequence_Tuple(fnargs);
    302     else
    303         Py_INCREF(fnargs);
    304     if (fnargs == NULL)
    305         return NULL;
    306 
    307     if (kw == Py_None)
    308         kw = PyDict_New();
    309     else if(!PyDict_CheckExact(kw))
    310         kw = PyDict_Copy(kw);
    311     else
    312         Py_INCREF(kw);
    313     if (kw == NULL) {
    314         Py_DECREF(fnargs);
    315         return NULL;
    316     }
    317 
    318     Py_INCREF(fn);
    319     if (dict == Py_None)
    320         dict = NULL;
    321     else
    322         Py_INCREF(dict);
    323 
    324     Py_SETREF(pto->fn, fn);
    325     Py_SETREF(pto->args, fnargs);
    326     Py_SETREF(pto->kw, kw);
    327     Py_XSETREF(pto->dict, dict);
    328     Py_RETURN_NONE;
    329 }
    330 
    331 static PyMethodDef partial_methods[] = {
    332     {"__reduce__", (PyCFunction)partial_reduce, METH_NOARGS},
    333     {"__setstate__", (PyCFunction)partial_setstate, METH_O},
    334     {NULL,              NULL}           /* sentinel */
    335 };
    336 
    337 static PyTypeObject partial_type = {
    338     PyVarObject_HEAD_INIT(NULL, 0)
    339     "functools.partial",                /* tp_name */
    340     sizeof(partialobject),              /* tp_basicsize */
    341     0,                                  /* tp_itemsize */
    342     /* methods */
    343     (destructor)partial_dealloc,        /* tp_dealloc */
    344     0,                                  /* tp_print */
    345     0,                                  /* tp_getattr */
    346     0,                                  /* tp_setattr */
    347     0,                                  /* tp_compare */
    348     0,                                  /* tp_repr */
    349     0,                                  /* tp_as_number */
    350     0,                                  /* tp_as_sequence */
    351     0,                                  /* tp_as_mapping */
    352     0,                                  /* tp_hash */
    353     (ternaryfunc)partial_call,          /* tp_call */
    354     0,                                  /* tp_str */
    355     PyObject_GenericGetAttr,            /* tp_getattro */
    356     PyObject_GenericSetAttr,            /* tp_setattro */
    357     0,                                  /* tp_as_buffer */
    358     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
    359         Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_WEAKREFS,         /* tp_flags */
    360     partial_doc,                        /* tp_doc */
    361     (traverseproc)partial_traverse,     /* tp_traverse */
    362     0,                                  /* tp_clear */
    363     0,                                  /* tp_richcompare */
    364     offsetof(partialobject, weakreflist),       /* tp_weaklistoffset */
    365     0,                                  /* tp_iter */
    366     0,                                  /* tp_iternext */
    367     partial_methods,                    /* tp_methods */
    368     partial_memberlist,                 /* tp_members */
    369     partial_getsetlist,                 /* tp_getset */
    370     0,                                  /* tp_base */
    371     0,                                  /* tp_dict */
    372     0,                                  /* tp_descr_get */
    373     0,                                  /* tp_descr_set */
    374     offsetof(partialobject, dict),      /* tp_dictoffset */
    375     0,                                  /* tp_init */
    376     0,                                  /* tp_alloc */
    377     partial_new,                        /* tp_new */
    378     PyObject_GC_Del,                    /* tp_free */
    379 };
    380 
    381 
    382 /* module level code ********************************************************/
    383 
    384 PyDoc_STRVAR(module_doc,
    385 "Tools that operate on functions.");
    386 
    387 static PyMethodDef module_methods[] = {
    388     {"reduce",          functools_reduce,     METH_VARARGS, reduce_doc},
    389     {NULL,              NULL}           /* sentinel */
    390 };
    391 
    392 PyMODINIT_FUNC
    393 init_functools(void)
    394 {
    395     int i;
    396     PyObject *m;
    397     char *name;
    398     PyTypeObject *typelist[] = {
    399         &partial_type,
    400         NULL
    401     };
    402 
    403     m = Py_InitModule3("_functools", module_methods, module_doc);
    404     if (m == NULL)
    405         return;
    406 
    407     for (i=0 ; typelist[i] != NULL ; i++) {
    408         if (PyType_Ready(typelist[i]) < 0)
    409             return;
    410         name = strchr(typelist[i]->tp_name, '.');
    411         assert (name != NULL);
    412         Py_INCREF(typelist[i]);
    413         PyModule_AddObject(m, name+1, (PyObject *)typelist[i]);
    414     }
    415 }
    416