Append optimization.
authorRobert Bradshaw <robertwb@math.washington.edu>
Sat, 26 Apr 2008 22:56:46 +0000 (15:56 -0700)
committerRobert Bradshaw <robertwb@math.washington.edu>
Sat, 26 Apr 2008 22:56:46 +0000 (15:56 -0700)
Cython/Compiler/Builtin.py
Cython/Compiler/ExprNodes.py
tests/run/append.pyx [new file with mode: 0644]

index 6651e50e9f3832d63b8be74287daa5fa98d4dc47..f2750cd1311dad4e4c9916dee6de2f9ad0cc2cbd 100644 (file)
@@ -52,6 +52,9 @@ builtin_function_table = [
     #  Can't do these easily until we have builtin type entries.
     #('typecheck',  "OO",   "i",     "PyObject_TypeCheck", False),
     #('issubtype',  "OO",   "i",     "PyType_IsSubtype",   False),
+
+    # Put in namespace append optimization.
+    ('__Pyx_PyObject_Append', "OO",  "O",     "__Pyx_PyObject_Append"),
 ]
 
 # Builtin types
index b9c760e0825f99e25854c8b872718fd5ed4bf78a..5745f700f72f3de83b32eed2fce0775f7b8dced9 100644 (file)
@@ -1519,12 +1519,15 @@ class SimpleCallNode(ExprNode):
     #  coerced_self   ExprNode or None     used internally
     #  wrapper_call   bool                 used internally
     
+    # optimized_call  str or None
+    
     subexprs = ['self', 'coerced_self', 'function', 'args', 'arg_tuple']
     
     self = None
     coerced_self = None
     arg_tuple = None
     wrapper_call = False
+    optimized_call = None
     
     def compile_time_value(self, denv):
         function = self.function.compile_time_value(denv)
@@ -1538,6 +1541,15 @@ class SimpleCallNode(ExprNode):
         function = self.function
         function.is_called = 1
         self.function.analyse_types(env)
+        if function.is_attribute and function.is_py_attr and \
+           function.attribute == "append" and len(self.args) == 1:
+            # L.append(x) is almost always applied to a list
+            self.py_func = self.function
+            self.function = NameNode(pos=self.function.pos, name="__Pyx_PyObject_Append")
+            self.function.analyse_types(env)
+            self.self = self.py_func.obj
+            function.obj = CloneNode(self.self)
+            env.use_utility_code(append_utility_code)
         if function.is_attribute and function.entry and function.entry.is_cmethod:
             # Take ownership of the object from which the attribute
             # was obtained, because we need to pass it as 'self'.
@@ -4105,3 +4117,18 @@ static void __Pyx_CppExn2PyErr() {
 """,""]
 
 #------------------------------------------------------------------------------------
+
+append_utility_code = [
+"""
+static inline PyObject* __Pyx_PyObject_Append(PyObject* L, PyObject* x) {
+    if (likely(PyList_CheckExact(L))) {
+        if (PyList_Append(L, x) < 0) return NULL;
+        Py_INCREF(Py_None);
+        return Py_None; // this is just to have an accurate signature
+    }
+    else {
+        return PyObject_CallMethod(L, "append", "O", x);
+    }
+}
+""",""
+]
\ No newline at end of file
diff --git a/tests/run/append.pyx b/tests/run/append.pyx
new file mode 100644 (file)
index 0000000..e20a8d9
--- /dev/null
@@ -0,0 +1,39 @@
+__doc__ = """
+>>> test_append([])
+None
+None
+got error
+[1, 2]
+>>> test_append(A())
+appending
+1
+appending
+2
+got error
+<append.A instance at ...>
+>>> test_append(B())
+None
+None
+None
+[1, 2, 3, 4]
+"""
+
+class A:
+    def append(self, x):
+        print "appending"
+        return x
+        
+class B(list):
+    def append(self, *args):
+        for arg in args:
+            list.append(self, arg)
+
+def test_append(L):
+    print L.append(1)
+    print L.append(2)
+    try:
+        print L.append(3,4)
+    except TypeError:
+        print "got error"
+    print L
+