[Python-checkins] r61028 - in python/trunk: Doc/library/operator.rst Lib/test/test_operator.py Misc/NEWS Modules/operator.c

georg.brandl python-checkins at python.org
Sun Feb 24 00:04:36 CET 2008


Author: georg.brandl
Date: Sun Feb 24 00:04:35 2008
New Revision: 61028

Modified:
   python/trunk/Doc/library/operator.rst
   python/trunk/Lib/test/test_operator.py
   python/trunk/Misc/NEWS
   python/trunk/Modules/operator.c
Log:
#1506171: added operator.methodcaller().


Modified: python/trunk/Doc/library/operator.rst
==============================================================================
--- python/trunk/Doc/library/operator.rst	(original)
+++ python/trunk/Doc/library/operator.rst	Sun Feb 24 00:04:35 2008
@@ -538,6 +538,17 @@
    [('orange', 1), ('banana', 2), ('apple', 3), ('pear', 5)]
 
 
+.. function:: methodcaller(name[, args...])
+
+   Return a callable object that calls the method *name* on its operand.  If
+   additional arguments and/or keyword arguments are given, they will be given
+   to the method as well.  After ``f = methodcaller('name')``, the call ``f(b)``
+   returns ``b.name()``.  After ``f = methodcaller('name', 'foo', bar=1)``, the
+   call ``f(b)`` returns ``b.name('foo', bar=1)``.
+
+   .. versionadded:: 2.6
+
+
 .. _operator-map:
 
 Mapping Operators to Functions

Modified: python/trunk/Lib/test/test_operator.py
==============================================================================
--- python/trunk/Lib/test/test_operator.py	(original)
+++ python/trunk/Lib/test/test_operator.py	Sun Feb 24 00:04:35 2008
@@ -440,6 +440,24 @@
         self.assertEqual(operator.itemgetter(2,10,5)(data), ('2', '10', '5'))
         self.assertRaises(TypeError, operator.itemgetter(2, 'x', 5), data)
 
+    def test_methodcaller(self):
+        self.assertRaises(TypeError, operator.methodcaller)
+        class A:
+            def foo(self, *args, **kwds):
+                return args[0] + args[1]
+            def bar(self, f=42):
+                return f
+        a = A()
+        f = operator.methodcaller('foo')
+        self.assertRaises(IndexError, f, a)
+        f = operator.methodcaller('foo', 1, 2)
+        self.assertEquals(f(a), 3)
+        f = operator.methodcaller('bar')
+        self.assertEquals(f(a), 42)
+        self.assertRaises(TypeError, f, a, a)
+        f = operator.methodcaller('bar', f=5)
+        self.assertEquals(f(a), 5)
+
     def test_inplace(self):
         class C(object):
             def __iadd__     (self, other): return "iadd"

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Sun Feb 24 00:04:35 2008
@@ -1196,6 +1196,8 @@
 Extension Modules
 -----------------
 
+- Patch #1506171: added operator.methodcaller().
+
 - Patch #1826: operator.attrgetter() now supports dotted attribute paths.
 
 - Patch #1957: syslogmodule: Release GIL when calling syslog(3)

Modified: python/trunk/Modules/operator.c
==============================================================================
--- python/trunk/Modules/operator.c	(original)
+++ python/trunk/Modules/operator.c	Sun Feb 24 00:04:35 2008
@@ -620,6 +620,139 @@
 	attrgetter_new,			/* tp_new */
 	0,				/* tp_free */
 };
+
+
+/* methodcaller object **********************************************************/
+
+typedef struct {
+	PyObject_HEAD
+	PyObject *name;
+	PyObject *args;
+	PyObject *kwds;
+} methodcallerobject;
+
+static PyTypeObject methodcaller_type;
+
+static PyObject *
+methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+	methodcallerobject *mc;
+	PyObject *name, *newargs;
+
+	if (PyTuple_GET_SIZE(args) < 1) {
+		PyErr_SetString(PyExc_TypeError, "methodcaller needs at least "
+				"one argument, the method name");
+		return NULL;
+	}
+
+	/* create methodcallerobject structure */
+	mc = PyObject_GC_New(methodcallerobject, &methodcaller_type);
+	if (mc == NULL) 
+		return NULL;	
+
+	newargs = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
+	if (newargs == NULL) {
+		Py_DECREF(mc);
+		return NULL;
+	}
+	mc->args = newargs;
+
+	name = PyTuple_GET_ITEM(args, 0);
+	Py_INCREF(name);
+	mc->name = name;
+
+	Py_XINCREF(kwds);
+	mc->kwds = kwds;
+
+	PyObject_GC_Track(mc);
+	return (PyObject *)mc;
+}
+
+static void
+methodcaller_dealloc(methodcallerobject *mc)
+{
+	PyObject_GC_UnTrack(mc);
+	Py_XDECREF(mc->name);
+	Py_XDECREF(mc->args);
+	Py_XDECREF(mc->kwds);
+	PyObject_GC_Del(mc);
+}
+
+static int
+methodcaller_traverse(methodcallerobject *mc, visitproc visit, void *arg)
+{
+	Py_VISIT(mc->args);
+	Py_VISIT(mc->kwds);
+	return 0;
+}
+
+static PyObject *
+methodcaller_call(methodcallerobject *mc, PyObject *args, PyObject *kw)
+{
+	PyObject *method, *obj, *result;
+
+	if (!PyArg_UnpackTuple(args, "methodcaller", 1, 1, &obj))
+		return NULL;
+	method = PyObject_GetAttr(obj, mc->name);
+	if (method == NULL)
+		return NULL;
+	result = PyObject_Call(method, mc->args, mc->kwds);
+	Py_DECREF(method);
+	return result;
+}
+
+PyDoc_STRVAR(methodcaller_doc,
+"methodcaller(name, ...) --> methodcaller object\n\
+\n\
+Return a callable object that calls the given method on its operand.\n\
+After, f = methodcaller('name'), the call f(r) returns r.name().\n\
+After, g = methodcaller('name', 'date', foo=1), the call g(r) returns\n\
+r.name('date', foo=1).");
+
+static PyTypeObject methodcaller_type = {
+	PyVarObject_HEAD_INIT(NULL, 0)
+	"operator.methodcaller",	/* tp_name */
+	sizeof(methodcallerobject),	/* tp_basicsize */
+	0,				/* tp_itemsize */
+	/* methods */
+	(destructor)methodcaller_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)methodcaller_call,	/* tp_call */
+	0,				/* tp_str */
+	PyObject_GenericGetAttr,	/* tp_getattro */
+	0,				/* tp_setattro */
+	0,				/* tp_as_buffer */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
+	methodcaller_doc,			/* tp_doc */
+	(traverseproc)methodcaller_traverse,	/* tp_traverse */
+	0,				/* tp_clear */
+	0,				/* tp_richcompare */
+	0,				/* tp_weaklistoffset */
+	0,				/* tp_iter */
+	0,				/* tp_iternext */
+	0,				/* tp_methods */
+	0,				/* tp_members */
+	0,				/* tp_getset */
+	0,				/* tp_base */
+	0,				/* tp_dict */
+	0,				/* tp_descr_get */
+	0,				/* tp_descr_set */
+	0,				/* tp_dictoffset */
+	0,				/* tp_init */
+	0,				/* tp_alloc */
+	methodcaller_new,		/* tp_new */
+	0,				/* tp_free */
+};
+
+
 /* Initialization function for the module (*must* be called initoperator) */
 
 PyMODINIT_FUNC
@@ -642,4 +775,9 @@
 		return;
 	Py_INCREF(&attrgetter_type);
 	PyModule_AddObject(m, "attrgetter", (PyObject *)&attrgetter_type);
+
+	if (PyType_Ready(&methodcaller_type) < 0)
+		return;
+	Py_INCREF(&methodcaller_type);
+	PyModule_AddObject(m, "methodcaller", (PyObject *)&methodcaller_type);
 }


More information about the Python-checkins mailing list