LCOV - code coverage report
Current view: top level - Modules - _functoolsmodule.c (source / functions) Hit Total Coverage
Test: CPython lcov report Lines: 43 165 26.1 %
Date: 2017-04-19 Functions: 4 10 40.0 %

          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             : }

Generated by: LCOV version 1.10