3 #include "structmember.h"
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.
12 /* partial object **********************************************************/
20 PyObject
*weakreflist
; /* List of weak references */
23 static PyTypeObject partial_type
;
26 partial_new(PyTypeObject
*type
, PyObject
*args
, PyObject
*kw
)
31 if (PyTuple_GET_SIZE(args
) < 1) {
32 PyErr_SetString(PyExc_TypeError
,
33 "type 'partial' takes at least one argument");
37 func
= PyTuple_GET_ITEM(args
, 0);
38 if (!PyCallable_Check(func
)) {
39 PyErr_SetString(PyExc_TypeError
,
40 "the first argument must be callable");
44 /* create partialobject structure */
45 pto
= (partialobject
*)type
->tp_alloc(type
, 0);
51 pto
->args
= PyTuple_GetSlice(args
, 1, PY_SSIZE_T_MAX
);
52 if (pto
->args
== NULL
) {
58 pto
->kw
= PyDict_Copy(kw
);
59 if (pto
->kw
== NULL
) {
68 pto
->weakreflist
= NULL
;
71 return (PyObject
*)pto
;
75 partial_dealloc(partialobject
*pto
)
77 PyObject_GC_UnTrack(pto
);
78 if (pto
->weakreflist
!= NULL
)
79 PyObject_ClearWeakRefs((PyObject
*) pto
);
81 Py_XDECREF(pto
->args
);
83 Py_XDECREF(pto
->dict
);
84 Py_TYPE(pto
)->tp_free(pto
);
88 partial_call(partialobject
*pto
, PyObject
*args
, PyObject
*kw
)
91 PyObject
*argappl
= NULL
, *kwappl
= NULL
;
93 assert (PyCallable_Check(pto
->fn
));
94 assert (PyTuple_Check(pto
->args
));
95 assert (pto
->kw
== Py_None
|| PyDict_Check(pto
->kw
));
97 if (PyTuple_GET_SIZE(pto
->args
) == 0) {
100 } else if (PyTuple_GET_SIZE(args
) == 0) {
102 Py_INCREF(pto
->args
);
104 argappl
= PySequence_Concat(pto
->args
, args
);
109 if (pto
->kw
== Py_None
) {
113 kwappl
= PyDict_Copy(pto
->kw
);
114 if (kwappl
== NULL
) {
119 if (PyDict_Merge(kwappl
, kw
, 1) != 0) {
127 ret
= PyObject_Call(pto
->fn
, argappl
, kwappl
);
134 partial_traverse(partialobject
*pto
, visitproc visit
, void *arg
)
143 PyDoc_STRVAR(partial_doc
,
144 "partial(func, *args, **keywords) - new function with partial application\n\
145 of the given arguments and keywords.\n");
147 #define OFF(x) offsetof(partialobject, x)
148 static PyMemberDef partial_memberlist
[] = {
149 {"func", T_OBJECT
, OFF(fn
), READONLY
,
150 "function object to use in future partial calls"},
151 {"args", T_OBJECT
, OFF(args
), READONLY
,
152 "tuple of arguments to future partial calls"},
153 {"keywords", T_OBJECT
, OFF(kw
), READONLY
,
154 "dictionary of keyword arguments to future partial calls"},
155 {NULL
} /* Sentinel */
159 partial_get_dict(partialobject
*pto
)
161 if (pto
->dict
== NULL
) {
162 pto
->dict
= PyDict_New();
163 if (pto
->dict
== NULL
)
166 Py_INCREF(pto
->dict
);
171 partial_set_dict(partialobject
*pto
, PyObject
*value
)
175 /* It is illegal to del p.__dict__ */
177 PyErr_SetString(PyExc_TypeError
,
178 "a partial object's dictionary may not be deleted");
181 /* Can only set __dict__ to a dictionary */
182 if (!PyDict_Check(value
)) {
183 PyErr_SetString(PyExc_TypeError
,
184 "setting partial object's dictionary to a non-dict");
194 static PyGetSetDef partial_getsetlist
[] = {
195 {"__dict__", (getter
)partial_get_dict
, (setter
)partial_set_dict
},
196 {NULL
} /* Sentinel */
200 __reduce__ by itself doesn't support getting kwargs in the unpickle
201 operation so we define a __setstate__ that replaces all the information
202 about the partial. If we only replaced part of it someone would use
203 it as a hook to do stange things.
207 partial_reduce(partialobject
*pto
, PyObject
*unused
)
209 return Py_BuildValue("O(O)(OOOO)", Py_TYPE(pto
), pto
->fn
, pto
->fn
,
211 pto
->dict
? pto
->dict
: Py_None
);
215 partial_setstate(partialobject
*pto
, PyObject
*args
)
217 PyObject
*fn
, *fnargs
, *kw
, *dict
;
218 if (!PyArg_ParseTuple(args
, "(OOOO):__setstate__",
219 &fn
, &fnargs
, &kw
, &dict
))
222 Py_XDECREF(pto
->args
);
224 Py_XDECREF(pto
->dict
);
228 if (dict
!= Py_None
) {
240 static PyMethodDef partial_methods
[] = {
241 {"__reduce__", (PyCFunction
)partial_reduce
, METH_NOARGS
},
242 {"__setstate__", (PyCFunction
)partial_setstate
, METH_VARARGS
},
243 {NULL
, NULL
} /* sentinel */
246 static PyTypeObject partial_type
= {
247 PyVarObject_HEAD_INIT(NULL
, 0)
248 "functools.partial", /* tp_name */
249 sizeof(partialobject
), /* tp_basicsize */
252 (destructor
)partial_dealloc
, /* tp_dealloc */
258 0, /* tp_as_number */
259 0, /* tp_as_sequence */
260 0, /* tp_as_mapping */
262 (ternaryfunc
)partial_call
, /* tp_call */
264 PyObject_GenericGetAttr
, /* tp_getattro */
265 PyObject_GenericSetAttr
, /* tp_setattro */
266 0, /* tp_as_buffer */
267 Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_HAVE_GC
|
268 Py_TPFLAGS_BASETYPE
, /* tp_flags */
269 partial_doc
, /* tp_doc */
270 (traverseproc
)partial_traverse
, /* tp_traverse */
272 0, /* tp_richcompare */
273 offsetof(partialobject
, weakreflist
), /* tp_weaklistoffset */
276 partial_methods
, /* tp_methods */
277 partial_memberlist
, /* tp_members */
278 partial_getsetlist
, /* tp_getset */
281 0, /* tp_descr_get */
282 0, /* tp_descr_set */
283 offsetof(partialobject
, dict
), /* tp_dictoffset */
286 partial_new
, /* tp_new */
287 PyObject_GC_Del
, /* tp_free */
291 /* reduce (used to be a builtin) ********************************************/
294 functools_reduce(PyObject
*self
, PyObject
*args
)
296 PyObject
*seq
, *func
, *result
= NULL
, *it
;
298 if (!PyArg_UnpackTuple(args
, "reduce", 2, 3, &func
, &seq
, &result
))
303 it
= PyObject_GetIter(seq
);
305 PyErr_SetString(PyExc_TypeError
,
306 "reduce() arg 2 must support iteration");
311 if ((args
= PyTuple_New(2)) == NULL
)
317 if (args
->ob_refcnt
> 1) {
319 if ((args
= PyTuple_New(2)) == NULL
)
323 op2
= PyIter_Next(it
);
325 if (PyErr_Occurred())
333 PyTuple_SetItem(args
, 0, result
);
334 PyTuple_SetItem(args
, 1, op2
);
335 if ((result
= PyEval_CallObject(func
, args
)) == NULL
)
343 PyErr_SetString(PyExc_TypeError
,
344 "reduce() of empty sequence with no initial value");
356 PyDoc_STRVAR(functools_reduce_doc
,
357 "reduce(function, sequence[, initial]) -> value\n\
359 Apply a function of two arguments cumulatively to the items of a sequence,\n\
360 from left to right, so as to reduce the sequence to a single value.\n\
361 For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates\n\
362 ((((1+2)+3)+4)+5). If initial is present, it is placed before the items\n\
363 of the sequence in the calculation, and serves as a default when the\n\
364 sequence is empty.");
366 /* module level code ********************************************************/
368 PyDoc_STRVAR(module_doc
,
369 "Tools that operate on functions.");
371 static PyMethodDef module_methods
[] = {
372 {"reduce", functools_reduce
, METH_VARARGS
, functools_reduce_doc
},
373 {NULL
, NULL
} /* sentinel */
377 static struct PyModuleDef _functoolsmodule
= {
378 PyModuleDef_HEAD_INIT
,
390 PyInit__functools(void)
395 PyTypeObject
*typelist
[] = {
400 m
= PyModule_Create(&_functoolsmodule
);
404 for (i
=0 ; typelist
[i
] != NULL
; i
++) {
405 if (PyType_Ready(typelist
[i
]) < 0) {
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
]);