From eed018908def6ab1509317692dd8911d251c1a29 Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Sat, 26 Apr 2008 15:56:46 -0700 Subject: [PATCH] Append optimization. --- Cython/Compiler/Builtin.py | 3 +++ Cython/Compiler/ExprNodes.py | 27 +++++++++++++++++++++++++ tests/run/append.pyx | 39 ++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 tests/run/append.pyx diff --git a/Cython/Compiler/Builtin.py b/Cython/Compiler/Builtin.py index 6651e50e..f2750cd1 100644 --- a/Cython/Compiler/Builtin.py +++ b/Cython/Compiler/Builtin.py @@ -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 diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index b9c760e0..5745f700 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -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 index 00000000..e20a8d99 --- /dev/null +++ b/tests/run/append.pyx @@ -0,0 +1,39 @@ +__doc__ = """ +>>> test_append([]) +None +None +got error +[1, 2] +>>> test_append(A()) +appending +1 +appending +2 +got error + +>>> 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 + -- 2.26.2