/Modules/_functoolsmodule.c

http://unladen-swallow.googlecode.com/ · C · 355 lines · 292 code · 50 blank · 13 comment · 66 complexity · a7d0bdb3af09c72ccf4da8deac60a5f1 MD5 · raw file

  1. #include "Python.h"
  2. #include "structmember.h"
  3. /* _functools module written and maintained
  4. by Hye-Shik Chang <perky@FreeBSD.org>
  5. with adaptations by Raymond Hettinger <python@rcn.com>
  6. Copyright (c) 2004, 2005, 2006 Python Software Foundation.
  7. All rights reserved.
  8. */
  9. /* reduce() *************************************************************/
  10. static PyObject *
  11. functools_reduce(PyObject *self, PyObject *args)
  12. {
  13. PyObject *seq, *func, *result = NULL, *it;
  14. if (!PyArg_UnpackTuple(args, "reduce", 2, 3, &func, &seq, &result))
  15. return NULL;
  16. if (result != NULL)
  17. Py_INCREF(result);
  18. it = PyObject_GetIter(seq);
  19. if (it == NULL) {
  20. PyErr_SetString(PyExc_TypeError,
  21. "reduce() arg 2 must support iteration");
  22. Py_XDECREF(result);
  23. return NULL;
  24. }
  25. if ((args = PyTuple_New(2)) == NULL)
  26. goto Fail;
  27. for (;;) {
  28. PyObject *op2;
  29. if (args->ob_refcnt > 1) {
  30. Py_DECREF(args);
  31. if ((args = PyTuple_New(2)) == NULL)
  32. goto Fail;
  33. }
  34. op2 = PyIter_Next(it);
  35. if (op2 == NULL) {
  36. if (PyErr_Occurred())
  37. goto Fail;
  38. break;
  39. }
  40. if (result == NULL)
  41. result = op2;
  42. else {
  43. PyTuple_SetItem(args, 0, result);
  44. PyTuple_SetItem(args, 1, op2);
  45. if ((result = PyEval_CallObject(func, args)) == NULL)
  46. goto Fail;
  47. }
  48. }
  49. Py_DECREF(args);
  50. if (result == NULL)
  51. PyErr_SetString(PyExc_TypeError,
  52. "reduce() of empty sequence with no initial value");
  53. Py_DECREF(it);
  54. return result;
  55. Fail:
  56. Py_XDECREF(args);
  57. Py_XDECREF(result);
  58. Py_DECREF(it);
  59. return NULL;
  60. }
  61. PyDoc_STRVAR(reduce_doc,
  62. "reduce(function, sequence[, initial]) -> value\n\
  63. \n\
  64. Apply a function of two arguments cumulatively to the items of a sequence,\n\
  65. from left to right, so as to reduce the sequence to a single value.\n\
  66. For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates\n\
  67. ((((1+2)+3)+4)+5). If initial is present, it is placed before the items\n\
  68. of the sequence in the calculation, and serves as a default when the\n\
  69. sequence is empty.");
  70. /* partial object **********************************************************/
  71. typedef struct {
  72. PyObject_HEAD
  73. PyObject *fn;
  74. PyObject *args;
  75. PyObject *kw;
  76. PyObject *dict;
  77. PyObject *weakreflist; /* List of weak references */
  78. } partialobject;
  79. static PyTypeObject partial_type;
  80. static PyObject *
  81. partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
  82. {
  83. PyObject *func;
  84. partialobject *pto;
  85. if (PyTuple_GET_SIZE(args) < 1) {
  86. PyErr_SetString(PyExc_TypeError,
  87. "type 'partial' takes at least one argument");
  88. return NULL;
  89. }
  90. func = PyTuple_GET_ITEM(args, 0);
  91. if (!PyCallable_Check(func)) {
  92. PyErr_SetString(PyExc_TypeError,
  93. "the first argument must be callable");
  94. return NULL;
  95. }
  96. /* create partialobject structure */
  97. pto = (partialobject *)type->tp_alloc(type, 0);
  98. if (pto == NULL)
  99. return NULL;
  100. pto->fn = func;
  101. Py_INCREF(func);
  102. pto->args = PyTuple_GetSlice(args, 1, PY_SSIZE_T_MAX);
  103. if (pto->args == NULL) {
  104. pto->kw = NULL;
  105. Py_DECREF(pto);
  106. return NULL;
  107. }
  108. if (kw != NULL) {
  109. pto->kw = PyDict_Copy(kw);
  110. if (pto->kw == NULL) {
  111. Py_DECREF(pto);
  112. return NULL;
  113. }
  114. } else {
  115. pto->kw = Py_None;
  116. Py_INCREF(Py_None);
  117. }
  118. pto->weakreflist = NULL;
  119. pto->dict = NULL;
  120. return (PyObject *)pto;
  121. }
  122. static void
  123. partial_dealloc(partialobject *pto)
  124. {
  125. PyObject_GC_UnTrack(pto);
  126. if (pto->weakreflist != NULL)
  127. PyObject_ClearWeakRefs((PyObject *) pto);
  128. Py_XDECREF(pto->fn);
  129. Py_XDECREF(pto->args);
  130. Py_XDECREF(pto->kw);
  131. Py_XDECREF(pto->dict);
  132. Py_TYPE(pto)->tp_free(pto);
  133. }
  134. static PyObject *
  135. partial_call(partialobject *pto, PyObject *args, PyObject *kw)
  136. {
  137. PyObject *ret;
  138. PyObject *argappl = NULL, *kwappl = NULL;
  139. assert (PyCallable_Check(pto->fn));
  140. assert (PyTuple_Check(pto->args));
  141. assert (pto->kw == Py_None || PyDict_Check(pto->kw));
  142. if (PyTuple_GET_SIZE(pto->args) == 0) {
  143. argappl = args;
  144. Py_INCREF(args);
  145. } else if (PyTuple_GET_SIZE(args) == 0) {
  146. argappl = pto->args;
  147. Py_INCREF(pto->args);
  148. } else {
  149. argappl = PySequence_Concat(pto->args, args);
  150. if (argappl == NULL)
  151. return NULL;
  152. }
  153. if (pto->kw == Py_None) {
  154. kwappl = kw;
  155. Py_XINCREF(kw);
  156. } else {
  157. kwappl = PyDict_Copy(pto->kw);
  158. if (kwappl == NULL) {
  159. Py_DECREF(argappl);
  160. return NULL;
  161. }
  162. if (kw != NULL) {
  163. if (PyDict_Merge(kwappl, kw, 1) != 0) {
  164. Py_DECREF(argappl);
  165. Py_DECREF(kwappl);
  166. return NULL;
  167. }
  168. }
  169. }
  170. ret = PyObject_Call(pto->fn, argappl, kwappl);
  171. Py_DECREF(argappl);
  172. Py_XDECREF(kwappl);
  173. return ret;
  174. }
  175. static int
  176. partial_traverse(partialobject *pto, visitproc visit, void *arg)
  177. {
  178. Py_VISIT(pto->fn);
  179. Py_VISIT(pto->args);
  180. Py_VISIT(pto->kw);
  181. Py_VISIT(pto->dict);
  182. return 0;
  183. }
  184. PyDoc_STRVAR(partial_doc,
  185. "partial(func, *args, **keywords) - new function with partial application\n\
  186. of the given arguments and keywords.\n");
  187. #define OFF(x) offsetof(partialobject, x)
  188. static PyMemberDef partial_memberlist[] = {
  189. {"func", T_OBJECT, OFF(fn), READONLY,
  190. "function object to use in future partial calls"},
  191. {"args", T_OBJECT, OFF(args), READONLY,
  192. "tuple of arguments to future partial calls"},
  193. {"keywords", T_OBJECT, OFF(kw), READONLY,
  194. "dictionary of keyword arguments to future partial calls"},
  195. {NULL} /* Sentinel */
  196. };
  197. static PyObject *
  198. partial_get_dict(partialobject *pto)
  199. {
  200. if (pto->dict == NULL) {
  201. pto->dict = PyDict_New();
  202. if (pto->dict == NULL)
  203. return NULL;
  204. }
  205. Py_INCREF(pto->dict);
  206. return pto->dict;
  207. }
  208. static int
  209. partial_set_dict(partialobject *pto, PyObject *value)
  210. {
  211. PyObject *tmp;
  212. /* It is illegal to del p.__dict__ */
  213. if (value == NULL) {
  214. PyErr_SetString(PyExc_TypeError,
  215. "a partial object's dictionary may not be deleted");
  216. return -1;
  217. }
  218. /* Can only set __dict__ to a dictionary */
  219. if (!PyDict_Check(value)) {
  220. PyErr_SetString(PyExc_TypeError,
  221. "setting partial object's dictionary to a non-dict");
  222. return -1;
  223. }
  224. tmp = pto->dict;
  225. Py_INCREF(value);
  226. pto->dict = value;
  227. Py_XDECREF(tmp);
  228. return 0;
  229. }
  230. static PyGetSetDef partial_getsetlist[] = {
  231. {"__dict__", (getter)partial_get_dict, (setter)partial_set_dict},
  232. {NULL} /* Sentinel */
  233. };
  234. static PyTypeObject partial_type = {
  235. PyVarObject_HEAD_INIT(NULL, 0)
  236. "functools.partial", /* tp_name */
  237. sizeof(partialobject), /* tp_basicsize */
  238. 0, /* tp_itemsize */
  239. /* methods */
  240. (destructor)partial_dealloc, /* tp_dealloc */
  241. 0, /* tp_print */
  242. 0, /* tp_getattr */
  243. 0, /* tp_setattr */
  244. 0, /* tp_compare */
  245. 0, /* tp_repr */
  246. 0, /* tp_as_number */
  247. 0, /* tp_as_sequence */
  248. 0, /* tp_as_mapping */
  249. 0, /* tp_hash */
  250. (ternaryfunc)partial_call, /* tp_call */
  251. 0, /* tp_str */
  252. PyObject_GenericGetAttr, /* tp_getattro */
  253. PyObject_GenericSetAttr, /* tp_setattro */
  254. 0, /* tp_as_buffer */
  255. Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
  256. Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */
  257. partial_doc, /* tp_doc */
  258. (traverseproc)partial_traverse, /* tp_traverse */
  259. 0, /* tp_clear */
  260. 0, /* tp_richcompare */
  261. offsetof(partialobject, weakreflist), /* tp_weaklistoffset */
  262. 0, /* tp_iter */
  263. 0, /* tp_iternext */
  264. 0, /* tp_methods */
  265. partial_memberlist, /* tp_members */
  266. partial_getsetlist, /* tp_getset */
  267. 0, /* tp_base */
  268. 0, /* tp_dict */
  269. 0, /* tp_descr_get */
  270. 0, /* tp_descr_set */
  271. offsetof(partialobject, dict), /* tp_dictoffset */
  272. 0, /* tp_init */
  273. 0, /* tp_alloc */
  274. partial_new, /* tp_new */
  275. PyObject_GC_Del, /* tp_free */
  276. };
  277. /* module level code ********************************************************/
  278. PyDoc_STRVAR(module_doc,
  279. "Tools that operate on functions.");
  280. static PyMethodDef module_methods[] = {
  281. {"reduce", functools_reduce, METH_VARARGS, reduce_doc},
  282. {NULL, NULL} /* sentinel */
  283. };
  284. PyMODINIT_FUNC
  285. init_functools(void)
  286. {
  287. int i;
  288. PyObject *m;
  289. char *name;
  290. PyTypeObject *typelist[] = {
  291. &partial_type,
  292. NULL
  293. };
  294. m = Py_InitModule3("_functools", module_methods, module_doc);
  295. if (m == NULL)
  296. return;
  297. for (i=0 ; typelist[i] != NULL ; i++) {
  298. if (PyType_Ready(typelist[i]) < 0)
  299. return;
  300. name = strchr(typelist[i]->tp_name, '.');
  301. assert (name != NULL);
  302. Py_INCREF(typelist[i]);
  303. PyModule_AddObject(m, name+1, (PyObject *)typelist[i]);
  304. }
  305. }