#include "Python.h" | |
#include "structmember.h" | |
/* _functools module written and maintained | |
by Hye-Shik Chang <perky@FreeBSD.org> | |
with adaptations by Raymond Hettinger <python@rcn.com> | |
Copyright (c) 2004, 2005, 2006 Python Software Foundation. | |
All rights reserved. | |
*/ | |
/* reduce() *************************************************************/ | |
static PyObject * | |
functools_reduce(PyObject *self, PyObject *args) | |
{ | |
PyObject *seq, *func, *result = NULL, *it; | |
if (!PyArg_UnpackTuple(args, "reduce", 2, 3, &func, &seq, &result)) | |
return NULL; | |
if (result != NULL) | |
Py_INCREF(result); | |
it = PyObject_GetIter(seq); | |
if (it == NULL) { | |
PyErr_SetString(PyExc_TypeError, | |
"reduce() arg 2 must support iteration"); | |
Py_XDECREF(result); | |
return NULL; | |
} | |
if ((args = PyTuple_New(2)) == NULL) | |
goto Fail; | |
for (;;) { | |
PyObject *op2; | |
if (args->ob_refcnt > 1) { | |
Py_DECREF(args); | |
if ((args = PyTuple_New(2)) == NULL) | |
goto Fail; | |
} | |
op2 = PyIter_Next(it); | |
if (op2 == NULL) { | |
if (PyErr_Occurred()) | |
goto Fail; | |
break; | |
} | |
if (result == NULL) | |
result = op2; | |
else { | |
PyTuple_SetItem(args, 0, result); | |
PyTuple_SetItem(args, 1, op2); | |
if ((result = PyEval_CallObject(func, args)) == NULL) | |
goto Fail; | |
} | |
} | |
Py_DECREF(args); | |
if (result == NULL) | |
PyErr_SetString(PyExc_TypeError, | |
"reduce() of empty sequence with no initial value"); | |
Py_DECREF(it); | |
return result; | |
Fail: | |
Py_XDECREF(args); | |
Py_XDECREF(result); | |
Py_DECREF(it); | |
return NULL; | |
} | |
PyDoc_STRVAR(reduce_doc, | |
"reduce(function, sequence[, initial]) -> value\n\ | |
\n\ | |
Apply a function of two arguments cumulatively to the items of a sequence,\n\ | |
from left to right, so as to reduce the sequence to a single value.\n\ | |
For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates\n\ | |
((((1+2)+3)+4)+5). If initial is present, it is placed before the items\n\ | |
of the sequence in the calculation, and serves as a default when the\n\ | |
sequence is empty."); | |
/* partial object **********************************************************/ | |
typedef struct { | |
PyObject_HEAD | |
PyObject *fn; | |
PyObject *args; | |
PyObject *kw; | |
PyObject *dict; | |
PyObject *weakreflist; /* List of weak references */ | |
} partialobject; | |
static PyTypeObject partial_type; | |
static PyObject * | |
partial_new(PyTypeObject *type, PyObject *args, PyObject *kw) | |
{ | |
PyObject *func; | |
partialobject *pto; | |
if (PyTuple_GET_SIZE(args) < 1) { | |
PyErr_SetString(PyExc_TypeError, | |
"type 'partial' takes at least one argument"); | |
return NULL; | |
} | |
func = PyTuple_GET_ITEM(args, 0); | |
if (!PyCallable_Check(func)) { | |
PyErr_SetString(PyExc_TypeError, | |
"the first argument must be callable"); | |
return NULL; | |
} | |
/* create partialobject structure */ | |
pto = (partialobject *)type->tp_alloc(type, 0); | |
if (pto == NULL) | |
return NULL; | |
pto->fn = func; | |
Py_INCREF(func); | |
pto->args = PyTuple_GetSlice(args, 1, PY_SSIZE_T_MAX); | |
if (pto->args == NULL) { | |
pto->kw = NULL; | |
Py_DECREF(pto); | |
return NULL; | |
} | |
if (kw != NULL) { | |
pto->kw = PyDict_Copy(kw); | |
if (pto->kw == NULL) { | |
Py_DECREF(pto); | |
return NULL; | |
} | |
} else { | |
pto->kw = Py_None; | |
Py_INCREF(Py_None); | |
} | |
pto->weakreflist = NULL; | |
pto->dict = NULL; | |
return (PyObject *)pto; | |
} | |
static void | |
partial_dealloc(partialobject *pto) | |
{ | |
PyObject_GC_UnTrack(pto); | |
if (pto->weakreflist != NULL) | |
PyObject_ClearWeakRefs((PyObject *) pto); | |
Py_XDECREF(pto->fn); | |
Py_XDECREF(pto->args); | |
Py_XDECREF(pto->kw); | |
Py_XDECREF(pto->dict); | |
Py_TYPE(pto)->tp_free(pto); | |
} | |
static PyObject * | |
partial_call(partialobject *pto, PyObject *args, PyObject *kw) | |
{ | |
PyObject *ret; | |
PyObject *argappl = NULL, *kwappl = NULL; | |
assert (PyCallable_Check(pto->fn)); | |
assert (PyTuple_Check(pto->args)); | |
assert (pto->kw == Py_None || PyDict_Check(pto->kw)); | |
if (PyTuple_GET_SIZE(pto->args) == 0) { | |
argappl = args; | |
Py_INCREF(args); | |
} else if (PyTuple_GET_SIZE(args) == 0) { | |
argappl = pto->args; | |
Py_INCREF(pto->args); | |
} else { | |
argappl = PySequence_Concat(pto->args, args); | |
if (argappl == NULL) | |
return NULL; | |
} | |
if (pto->kw == Py_None) { | |
kwappl = kw; | |
Py_XINCREF(kw); | |
} else { | |
kwappl = PyDict_Copy(pto->kw); | |
if (kwappl == NULL) { | |
Py_DECREF(argappl); | |
return NULL; | |
} | |
if (kw != NULL) { | |
if (PyDict_Merge(kwappl, kw, 1) != 0) { | |
Py_DECREF(argappl); | |
Py_DECREF(kwappl); | |
return NULL; | |
} | |
} | |
} | |
ret = PyObject_Call(pto->fn, argappl, kwappl); | |
Py_DECREF(argappl); | |
Py_XDECREF(kwappl); | |
return ret; | |
} | |
static int | |
partial_traverse(partialobject *pto, visitproc visit, void *arg) | |
{ | |
Py_VISIT(pto->fn); | |
Py_VISIT(pto->args); | |
Py_VISIT(pto->kw); | |
Py_VISIT(pto->dict); | |
return 0; | |
} | |
PyDoc_STRVAR(partial_doc, | |
"partial(func, *args, **keywords) - new function with partial application\n\ | |
of the given arguments and keywords.\n"); | |
#define OFF(x) offsetof(partialobject, x) | |
static PyMemberDef partial_memberlist[] = { | |
{"func", T_OBJECT, OFF(fn), READONLY, | |
"function object to use in future partial calls"}, | |
{"args", T_OBJECT, OFF(args), READONLY, | |
"tuple of arguments to future partial calls"}, | |
{"keywords", T_OBJECT, OFF(kw), READONLY, | |
"dictionary of keyword arguments to future partial calls"}, | |
{NULL} /* Sentinel */ | |
}; | |
static PyObject * | |
partial_get_dict(partialobject *pto) | |
{ | |
if (pto->dict == NULL) { | |
pto->dict = PyDict_New(); | |
if (pto->dict == NULL) | |
return NULL; | |
} | |
Py_INCREF(pto->dict); | |
return pto->dict; | |
} | |
static int | |
partial_set_dict(partialobject *pto, PyObject *value) | |
{ | |
PyObject *tmp; | |
/* It is illegal to del p.__dict__ */ | |
if (value == NULL) { | |
PyErr_SetString(PyExc_TypeError, | |
"a partial object's dictionary may not be deleted"); | |
return -1; | |
} | |
/* Can only set __dict__ to a dictionary */ | |
if (!PyDict_Check(value)) { | |
PyErr_SetString(PyExc_TypeError, | |
"setting partial object's dictionary to a non-dict"); | |
return -1; | |
} | |
tmp = pto->dict; | |
Py_INCREF(value); | |
pto->dict = value; | |
Py_XDECREF(tmp); | |
return 0; | |
} | |
static PyGetSetDef partial_getsetlist[] = { | |
{"__dict__", (getter)partial_get_dict, (setter)partial_set_dict}, | |
{NULL} /* Sentinel */ | |
}; | |
/* Pickle strategy: | |
__reduce__ by itself doesn't support getting kwargs in the unpickle | |
operation so we define a __setstate__ that replaces all the information | |
about the partial. If we only replaced part of it someone would use | |
it as a hook to do strange things. | |
*/ | |
PyObject * | |
partial_reduce(partialobject *pto, PyObject *unused) | |
{ | |
return Py_BuildValue("O(O)(OOOO)", Py_TYPE(pto), pto->fn, pto->fn, | |
pto->args, pto->kw, | |
pto->dict ? pto->dict : Py_None); | |
} | |
PyObject * | |
partial_setstate(partialobject *pto, PyObject *args) | |
{ | |
PyObject *fn, *fnargs, *kw, *dict; | |
if (!PyArg_ParseTuple(args, "(OOOO):__setstate__", | |
&fn, &fnargs, &kw, &dict)) | |
return NULL; | |
Py_XDECREF(pto->fn); | |
Py_XDECREF(pto->args); | |
Py_XDECREF(pto->kw); | |
Py_XDECREF(pto->dict); | |
pto->fn = fn; | |
pto->args = fnargs; | |
pto->kw = kw; | |
if (dict != Py_None) { | |
pto->dict = dict; | |
Py_INCREF(dict); | |
} else { | |
pto->dict = NULL; | |
} | |
Py_INCREF(fn); | |
Py_INCREF(fnargs); | |
Py_INCREF(kw); | |
Py_RETURN_NONE; | |
} | |
static PyMethodDef partial_methods[] = { | |
{"__reduce__", (PyCFunction)partial_reduce, METH_NOARGS}, | |
{"__setstate__", (PyCFunction)partial_setstate, METH_VARARGS}, | |
{NULL, NULL} /* sentinel */ | |
}; | |
static PyTypeObject partial_type = { | |
PyVarObject_HEAD_INIT(NULL, 0) | |
"functools.partial", /* tp_name */ | |
sizeof(partialobject), /* tp_basicsize */ | |
0, /* tp_itemsize */ | |
/* methods */ | |
(destructor)partial_dealloc, /* tp_dealloc */ | |
0, /* tp_print */ | |
0, /* tp_getattr */ | |
0, /* tp_setattr */ | |
0, /* tp_compare */ | |
0, /* tp_repr */ | |
0, /* tp_as_number */ | |
0, /* tp_as_sequence */ | |
0, /* tp_as_mapping */ | |
0, /* tp_hash */ | |
(ternaryfunc)partial_call, /* tp_call */ | |
0, /* tp_str */ | |
PyObject_GenericGetAttr, /* tp_getattro */ | |
PyObject_GenericSetAttr, /* tp_setattro */ | |
0, /* tp_as_buffer */ | |
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | | |
Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */ | |
partial_doc, /* tp_doc */ | |
(traverseproc)partial_traverse, /* tp_traverse */ | |
0, /* tp_clear */ | |
0, /* tp_richcompare */ | |
offsetof(partialobject, weakreflist), /* tp_weaklistoffset */ | |
0, /* tp_iter */ | |
0, /* tp_iternext */ | |
partial_methods, /* tp_methods */ | |
partial_memberlist, /* tp_members */ | |
partial_getsetlist, /* tp_getset */ | |
0, /* tp_base */ | |
0, /* tp_dict */ | |
0, /* tp_descr_get */ | |
0, /* tp_descr_set */ | |
offsetof(partialobject, dict), /* tp_dictoffset */ | |
0, /* tp_init */ | |
0, /* tp_alloc */ | |
partial_new, /* tp_new */ | |
PyObject_GC_Del, /* tp_free */ | |
}; | |
/* module level code ********************************************************/ | |
PyDoc_STRVAR(module_doc, | |
"Tools that operate on functions."); | |
static PyMethodDef module_methods[] = { | |
{"reduce", functools_reduce, METH_VARARGS, reduce_doc}, | |
{NULL, NULL} /* sentinel */ | |
}; | |
PyMODINIT_FUNC | |
init_functools(void) | |
{ | |
int i; | |
PyObject *m; | |
char *name; | |
PyTypeObject *typelist[] = { | |
&partial_type, | |
NULL | |
}; | |
m = Py_InitModule3("_functools", module_methods, module_doc); | |
if (m == NULL) | |
return; | |
for (i=0 ; typelist[i] != NULL ; i++) { | |
if (PyType_Ready(typelist[i]) < 0) | |
return; | |
name = strchr(typelist[i]->tp_name, '.'); | |
assert (name != NULL); | |
Py_INCREF(typelist[i]); | |
PyModule_AddObject(m, name+1, (PyObject *)typelist[i]); | |
} | |
} |