PageRenderTime 286ms CodeModel.GetById 80ms app.highlight 111ms RepoModel.GetById 90ms app.codeStats 0ms

/Modules/_functoolsmodule.c

http://unladen-swallow.googlecode.com/
C | 355 lines | 292 code | 50 blank | 13 comment | 66 complexity | a7d0bdb3af09c72ccf4da8deac60a5f1 MD5 | raw file
  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
 14static PyObject *
 15functools_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
 70Fail:
 71	Py_XDECREF(args);
 72	Py_XDECREF(result);
 73	Py_DECREF(it);
 74	return NULL;
 75}
 76
 77PyDoc_STRVAR(reduce_doc,
 78"reduce(function, sequence[, initial]) -> value\n\
 79\n\
 80Apply a function of two arguments cumulatively to the items of a sequence,\n\
 81from left to right, so as to reduce the sequence to a single value.\n\
 82For 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\
 84of the sequence in the calculation, and serves as a default when the\n\
 85sequence is empty.");
 86
 87
 88
 89
 90/* partial object **********************************************************/
 91
 92typedef 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
101static PyTypeObject partial_type;
102
103static PyObject *
104partial_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	if (kw != NULL) {
136		pto->kw = PyDict_Copy(kw);
137		if (pto->kw == NULL) {
138			Py_DECREF(pto);
139			return NULL;
140		}
141	} else {
142		pto->kw = Py_None;
143		Py_INCREF(Py_None);
144	}
145
146	pto->weakreflist = NULL;
147	pto->dict = NULL;
148
149	return (PyObject *)pto;
150}
151
152static void
153partial_dealloc(partialobject *pto)
154{
155	PyObject_GC_UnTrack(pto);
156	if (pto->weakreflist != NULL)
157		PyObject_ClearWeakRefs((PyObject *) pto);
158	Py_XDECREF(pto->fn);
159	Py_XDECREF(pto->args);
160	Py_XDECREF(pto->kw);
161	Py_XDECREF(pto->dict);
162	Py_TYPE(pto)->tp_free(pto);
163}
164
165static PyObject *
166partial_call(partialobject *pto, PyObject *args, PyObject *kw)
167{
168	PyObject *ret;
169	PyObject *argappl = NULL, *kwappl = NULL;
170
171	assert (PyCallable_Check(pto->fn));
172	assert (PyTuple_Check(pto->args));
173	assert (pto->kw == Py_None  ||  PyDict_Check(pto->kw));
174
175	if (PyTuple_GET_SIZE(pto->args) == 0) {
176		argappl = args;
177		Py_INCREF(args);
178	} else if (PyTuple_GET_SIZE(args) == 0) {
179		argappl = pto->args;
180		Py_INCREF(pto->args);
181	} else {
182		argappl = PySequence_Concat(pto->args, args);
183		if (argappl == NULL)
184			return NULL;
185	}
186
187	if (pto->kw == Py_None) {
188		kwappl = kw;
189		Py_XINCREF(kw);
190	} else {
191		kwappl = PyDict_Copy(pto->kw);
192		if (kwappl == NULL) {
193			Py_DECREF(argappl);
194			return NULL;
195		}
196		if (kw != NULL) {
197			if (PyDict_Merge(kwappl, kw, 1) != 0) {
198				Py_DECREF(argappl);
199				Py_DECREF(kwappl);
200				return NULL;
201			}
202		}
203	}
204
205	ret = PyObject_Call(pto->fn, argappl, kwappl);
206	Py_DECREF(argappl);
207	Py_XDECREF(kwappl);
208	return ret;
209}
210
211static int
212partial_traverse(partialobject *pto, visitproc visit, void *arg)
213{
214	Py_VISIT(pto->fn);
215	Py_VISIT(pto->args);
216	Py_VISIT(pto->kw);
217	Py_VISIT(pto->dict);
218	return 0;
219}
220
221PyDoc_STRVAR(partial_doc,
222"partial(func, *args, **keywords) - new function with partial application\n\
223	of the given arguments and keywords.\n");
224
225#define OFF(x) offsetof(partialobject, x)
226static PyMemberDef partial_memberlist[] = {
227	{"func",	T_OBJECT,	OFF(fn),	READONLY,
228	 "function object to use in future partial calls"},
229	{"args",	T_OBJECT,	OFF(args),	READONLY,
230	 "tuple of arguments to future partial calls"},
231	{"keywords",	T_OBJECT,	OFF(kw),	READONLY,
232	 "dictionary of keyword arguments to future partial calls"},
233	{NULL}  /* Sentinel */
234};
235
236static PyObject *
237partial_get_dict(partialobject *pto)
238{
239	if (pto->dict == NULL) {
240		pto->dict = PyDict_New();
241		if (pto->dict == NULL)
242			return NULL;
243	}
244	Py_INCREF(pto->dict);
245	return pto->dict;
246}
247
248static int
249partial_set_dict(partialobject *pto, PyObject *value)
250{
251	PyObject *tmp;
252
253	/* It is illegal to del p.__dict__ */
254	if (value == NULL) {
255		PyErr_SetString(PyExc_TypeError,
256				"a partial object's dictionary may not be deleted");
257		return -1;
258	}
259	/* Can only set __dict__ to a dictionary */
260	if (!PyDict_Check(value)) {
261		PyErr_SetString(PyExc_TypeError,
262				"setting partial object's dictionary to a non-dict");
263		return -1;
264	}
265	tmp = pto->dict;
266	Py_INCREF(value);
267	pto->dict = value;
268	Py_XDECREF(tmp);
269	return 0;
270}
271
272static PyGetSetDef partial_getsetlist[] = {
273	{"__dict__", (getter)partial_get_dict, (setter)partial_set_dict},
274	{NULL} /* Sentinel */
275};
276
277static PyTypeObject partial_type = {
278	PyVarObject_HEAD_INIT(NULL, 0)
279	"functools.partial",		/* tp_name */
280	sizeof(partialobject),		/* tp_basicsize */
281	0,				/* tp_itemsize */
282	/* methods */
283	(destructor)partial_dealloc,	/* tp_dealloc */
284	0,				/* tp_print */
285	0,				/* tp_getattr */
286	0,				/* tp_setattr */
287	0,				/* tp_compare */
288	0,				/* tp_repr */
289	0,				/* tp_as_number */
290	0,				/* tp_as_sequence */
291	0,				/* tp_as_mapping */
292	0,				/* tp_hash */
293	(ternaryfunc)partial_call,	/* tp_call */
294	0,				/* tp_str */
295	PyObject_GenericGetAttr,	/* tp_getattro */
296	PyObject_GenericSetAttr,	/* tp_setattro */
297	0,				/* tp_as_buffer */
298	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
299		Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_WEAKREFS,	/* tp_flags */
300	partial_doc,			/* tp_doc */
301	(traverseproc)partial_traverse,	/* tp_traverse */
302	0,				/* tp_clear */
303	0,				/* tp_richcompare */
304	offsetof(partialobject, weakreflist),	/* tp_weaklistoffset */
305	0,				/* tp_iter */
306	0,				/* tp_iternext */
307	0,				/* tp_methods */
308	partial_memberlist,		/* tp_members */
309	partial_getsetlist,		/* tp_getset */
310	0,				/* tp_base */
311	0,				/* tp_dict */
312	0,				/* tp_descr_get */
313	0,				/* tp_descr_set */
314	offsetof(partialobject, dict),	/* tp_dictoffset */
315	0,				/* tp_init */
316	0,				/* tp_alloc */
317	partial_new,			/* tp_new */
318	PyObject_GC_Del,		/* tp_free */
319};
320
321
322/* module level code ********************************************************/
323
324PyDoc_STRVAR(module_doc,
325"Tools that operate on functions.");
326
327static PyMethodDef module_methods[] = {
328 	{"reduce",	functools_reduce,     METH_VARARGS, reduce_doc},
329 	{NULL,		NULL}		/* sentinel */
330};
331
332PyMODINIT_FUNC
333init_functools(void)
334{
335	int i;
336	PyObject *m;
337	char *name;
338	PyTypeObject *typelist[] = {
339		&partial_type,
340		NULL
341	};
342
343	m = Py_InitModule3("_functools", module_methods, module_doc);
344	if (m == NULL)
345		return;
346
347	for (i=0 ; typelist[i] != NULL ; i++) {
348		if (PyType_Ready(typelist[i]) < 0)
349			return;
350		name = strchr(typelist[i]->tp_name, '.');
351		assert (name != NULL);
352		Py_INCREF(typelist[i]);
353		PyModule_AddObject(m, name+1, (PyObject *)typelist[i]);
354	}
355}