[Jython-checkins] jython: Add operator.methodcaller. Thanks Jezreel Ng!

frank.wierzbicki jython-checkins at python.org
Fri Jun 22 21:53:05 CEST 2012


http://hg.python.org/jython/rev/136461d1ad22
changeset:   6735:136461d1ad22
user:        Jezreel Ng <jezreel at gmail.com>
date:        Fri Jun 22 12:52:17 2012 -0700
summary:
  Add operator.methodcaller. Thanks Jezreel Ng!

files:
  CPythonLib.includes                  |   2 +
  CoreExposed.includes                 |   1 +
  Lib/test/test_operator.py            |   1 -
  NEWS                                 |   4 +-
  src/org/python/modules/operator.java |  82 ++++++++++++++-
  5 files changed, 79 insertions(+), 11 deletions(-)


diff --git a/CPythonLib.includes b/CPythonLib.includes
--- a/CPythonLib.includes
+++ b/CPythonLib.includes
@@ -115,6 +115,7 @@
 pprint.py
 profile.py
 pstats.py
+pty.py
 pyclbr.py
 pydoc_topics.py
 Queue.py
@@ -154,6 +155,7 @@
 tokenize.py
 trace.py
 traceback.py
+tty.py
 tzparse.py
 urllib2.py
 urlparse.py
diff --git a/CoreExposed.includes b/CoreExposed.includes
--- a/CoreExposed.includes
+++ b/CoreExposed.includes
@@ -92,6 +92,7 @@
 org/python/modules/_weakref/ReferenceType.class
 org/python/modules/operator$PyAttrGetter.class
 org/python/modules/operator$PyItemGetter.class
+org/python/modules/operator$PyMethodCaller.class
 org/python/modules/posix/PyStatResult.class
 org/python/modules/random/PyRandom.class
 org/python/modules/thread/PyLocal.class
diff --git a/Lib/test/test_operator.py b/Lib/test/test_operator.py
--- a/Lib/test/test_operator.py
+++ b/Lib/test/test_operator.py
@@ -447,7 +447,6 @@
         self.assertEqual(operator.itemgetter(2,10,5)(data), ('2', '10', '5'))
         self.assertRaises(TypeError, operator.itemgetter(2, 'x', 5), data)
 
-    @unittest.skip("FIXME: broken")
     def test_methodcaller(self):
         self.assertRaises(TypeError, operator.methodcaller)
         class A:
diff --git a/NEWS b/NEWS
--- a/NEWS
+++ b/NEWS
@@ -2,9 +2,11 @@
 
 Jython 2.7a3
   Bugs Fixed
+    - [ 1909 ] attrgetter does not parse dotted attributes
+    - [ 1924 ] Implement operator.methodcaller
     - [ 1934 ] Break itertools.compress into a separate class 
+    - [ 1933 ] Break itertools.cycle into a separate class
     - [ 1932 ] Make check for iterability in chain() arguments lazy
-    - [ 1933 ] Break itertools.cycle into a separate class
     - [ 1931 ] Check that there are exactly 2 filter args
     - [ 1913 ] Support short -W options
     - [ 1897 ] 2.7.0ax only has partial ssl support
diff --git a/src/org/python/modules/operator.java b/src/org/python/modules/operator.java
--- a/src/org/python/modules/operator.java
+++ b/src/org/python/modules/operator.java
@@ -6,12 +6,14 @@
 import org.python.core.Py;
 import org.python.core.PyBuiltinFunctionSet;
 import org.python.core.PyIgnoreMethodTag;
+import org.python.core.PyMethod;
 import org.python.core.PyNewWrapper;
 import org.python.core.PyObject;
 import org.python.core.PyString;
 import org.python.core.PyTuple;
 import org.python.core.PyType;
 import org.python.core.PyUnicode;
+import org.python.expose.ExposedGet;
 import org.python.expose.ExposedMethod;
 import org.python.expose.ExposedNew;
 import org.python.expose.ExposedType;
@@ -243,6 +245,7 @@
 
         dict.__setitem__("attrgetter", PyAttrGetter.TYPE);
         dict.__setitem__("itemgetter", PyItemGetter.TYPE);
+        dict.__setitem__("methodcaller", PyMethodCaller.TYPE);
     }
 
     public static int countOf(PyObject seq, PyObject item) {
@@ -316,15 +319,7 @@
             // XXX: We should probably have a PyObject.__getattr__(PyObject) that does
             // this. This is different than __builtin__.getattr (in how it handles
             // exceptions)
-            String nameStr;
-            if (name instanceof PyUnicode) {
-                nameStr = ((PyUnicode)name).encode();
-            } else if (name instanceof PyString) {
-                nameStr = name.asString();
-            } else {
-                throw Py.TypeError(String.format("attribute name must be string, not '%.200s'",
-                                                 name.getType().fastGetName()));
-            }
+            String nameStr = ensureStringAttribute(name);
             String[] components = nameStr.split("\\.");
             for (String component : components) {
                 obj = obj.__getattr__(component.intern());
@@ -379,4 +374,73 @@
             return new PyTuple(result);
         }
     }
+
+    /**
+     * The methodcaller type.
+     */
+    @ExposedType(name = "operator.methodcaller", isBaseType = false)
+    static class PyMethodCaller extends PyObject {
+
+        public static final PyType TYPE = PyType.fromClass(PyMethodCaller.class);
+
+        public String name;
+        public PyObject[] args;
+        public String[] keywords;
+
+        @ExposedGet
+        public static PyString __doc__ = new PyString(
+                "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)");
+
+        public PyMethodCaller(String name, PyObject[] args, String[] keywords) {
+            this.name = name;
+            this.args = args;
+            this.keywords = keywords;
+        }
+
+        @ExposedNew
+        final static PyObject methodcaller___new__(PyNewWrapper new_, boolean init,
+                                                  PyType subtype, PyObject[] args,
+                                                  String[] keywords) {
+
+            if (args.length == 0) {
+                throw Py.TypeError("methodcaller needs at least one argument, the method name");
+            }
+            String nameStr = ensureStringAttribute(args[0]);
+            PyObject[] newArgs = new PyObject[args.length-1];
+            System.arraycopy(args, 1, newArgs, 0, args.length-1);
+            return new PyMethodCaller(nameStr, newArgs, keywords);
+        }
+
+        @Override
+        public PyObject __call__(PyObject[] args, String[] keywords) {
+            return methodcaller___call__(args, keywords);
+        }
+
+        @ExposedMethod
+        final PyObject methodcaller___call__(PyObject[] args, String[] keywords) {
+            if (args.length > 1) {
+                throw Py.TypeError("methodcaller expected 1 arguments, got " + args.length);
+            }
+            ArgParser ap = new ArgParser("methodcaller", args, Py.NoKeywords, "obj");
+            PyObject obj = ap.getPyObject(0);
+            return obj.invoke(name, this.args, this.keywords);
+        }
+    }
+
+    private static String ensureStringAttribute(PyObject name) {
+        String nameStr;
+        if (name instanceof PyUnicode) {
+            nameStr = ((PyUnicode)name).encode();
+        } else if (name instanceof PyString) {
+            nameStr = name.asString();
+        } else {
+            throw Py.TypeError(String.format("attribute name must be string, not '%.200s'",
+                    name.getType().fastGetName()));
+        }
+        return nameStr;
+    }
 }

-- 
Repository URL: http://hg.python.org/jython


More information about the Jython-checkins mailing list