implement tp_new() optimisation with args/kwargs
authorStefan Behnel <stefan_ml@behnel.de>
Sat, 23 Feb 2013 16:23:59 +0000 (17:23 +0100)
committerStefan Behnel <stefan_ml@behnel.de>
Sat, 23 Feb 2013 16:23:59 +0000 (17:23 +0100)
Cython/Compiler/Optimize.py
Cython/Utility/ObjectHandling.c
tests/run/tp_new.pyx

index 7fbdef3..2d03eef 100644 (file)
@@ -2142,14 +2142,22 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
 
     Pyx_tp_new_func_type = PyrexTypes.CFuncType(
         PyrexTypes.py_object_type, [
-            PyrexTypes.CFuncTypeArg("type", Builtin.type_type, None)
+            PyrexTypes.CFuncTypeArg("type",   Builtin.type_type, None),
+            PyrexTypes.CFuncTypeArg("args",   Builtin.tuple_type, None),
             ])
 
-    def _handle_simple_slot__new__(self, node, args, is_unbound_method):
-        """Replace 'exttype.__new__(exttype)' by a call to exttype->tp_new()
+    Pyx_tp_new_kwargs_func_type = PyrexTypes.CFuncType(
+        PyrexTypes.py_object_type, [
+            PyrexTypes.CFuncTypeArg("type",   Builtin.type_type, None),
+            PyrexTypes.CFuncTypeArg("args",   Builtin.tuple_type, None),
+            PyrexTypes.CFuncTypeArg("kwargs", Builtin.dict_type, None),
+        ])
+
+    def _handle_any_slot__new__(self, node, args, is_unbound_method, kwargs=None):
+        """Replace 'exttype.__new__(exttype, ...)' by a call to exttype->tp_new()
         """
         obj = node.function.obj
-        if not is_unbound_method or len(args) != 1:
+        if not is_unbound_method or len(args) < 1:
             return node
         type_arg = args[0]
         if not obj.is_name or not type_arg.is_name:
@@ -2177,11 +2185,24 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
             type_arg = type_arg.as_none_safe_node(
                 "object.__new__(X): X is not a type object (NoneType)")
 
-        return ExprNodes.PythonCapiCallNode(
-            node.pos, "__Pyx_tp_new", self.Pyx_tp_new_func_type,
-            args=[type_arg],
-            utility_code=UtilityCode.load_cached('tp_new', 'ObjectHandling.c'),
-            is_temp=node.is_temp
+        args_tuple = ExprNodes.TupleNode(node.pos, args=args[1:])
+        args_tuple = args_tuple.analyse_types(
+            self.current_env(), skip_children=True)
+
+        utility_code = UtilityCode.load_cached('tp_new', 'ObjectHandling.c')
+        if kwargs:
+            return ExprNodes.PythonCapiCallNode(
+                node.pos, "__Pyx_tp_new_kwargs", self.Pyx_tp_new_kwargs_func_type,
+                args=[type_arg, args_tuple, kwargs],
+                utility_code=utility_code,
+                is_temp=node.is_temp
+                )
+        else:
+            return ExprNodes.PythonCapiCallNode(
+                node.pos, "__Pyx_tp_new", self.Pyx_tp_new_func_type,
+                args=[type_arg, args_tuple],
+                utility_code=utility_code,
+                is_temp=node.is_temp
             )
 
     ### methods of builtin types
index 1d056a0..15e7efd 100644 (file)
@@ -653,9 +653,9 @@ bad:
 
 
 /////////////// tp_new.proto ///////////////
-//@substitute: naming
 
-static CYTHON_INLINE PyObject* __Pyx_tp_new(PyObject* type_obj) {
+#define __Pyx_tp_new(type_obj, args) __Pyx_tp_new_kwargs(type_obj, args, NULL)
+static CYTHON_INLINE PyObject* __Pyx_tp_new_kwargs(PyObject* type_obj, PyObject* args, PyObject* kwargs) {
     return (PyObject*) (((PyTypeObject*)(type_obj))->tp_new(
-        (PyTypeObject*)(type_obj), $empty_tuple, NULL));
+        (PyTypeObject*)(type_obj), args, kwargs));
 }
index 4107343..8b99da2 100644 (file)
@@ -2,28 +2,32 @@
 cimport cython
 
 cdef class MyType:
-    def __cinit__(self):
+    cdef public args, kwargs
+    def __cinit__(self, *args, **kwargs):
+        self.args, self.kwargs = args, kwargs
         print "CINIT"
-    def __init__(self):
+    def __init__(self, *args, **kwargs):
         print "INIT"
 
 cdef class MySubType(MyType):
-    def __cinit__(self):
+    def __cinit__(self, *args, **kwargs):
+        self.args, self.kwargs = args, kwargs
         print "CINIT(SUB)"
-    def __init__(self):
+    def __init__(self, *args, **kwargs):
         print "INIT"
 
 class MyClass(object):
-    def __cinit__(self):
+    def __cinit__(self, *args, **kwargs):
+        self.args, self.kwargs = args, kwargs
         print "CINIT"
-    def __init__(self):
+    def __init__(self, *args, **kwargs):
         print "INIT"
 
 class MyTypeSubClass(MyType):
-    def __cinit__(self):
+    def __cinit__(self, *args, **kwargs):
         # not called: Python class!
         print "CINIT(PYSUB)"
-    def __init__(self):
+    def __init__(self, *args, **kwargs):
         print "INIT"
 
 # only these can be safely optimised:
@@ -53,6 +57,36 @@ def make_new_typed_target():
 
 @cython.test_assert_path_exists('//PythonCapiCallNode')
 @cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode')
+def make_new_with_args():
+    """
+    >>> isinstance(make_new_with_args(), MyType)
+    CINIT
+    (1, 2, 3)
+    {}
+    True
+    """
+    m = MyType.__new__(MyType, 1, 2 ,3)
+    print m.args
+    print m.kwargs
+    return m
+
+@cython.test_assert_path_exists('//PythonCapiCallNode')
+@cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode')
+def make_new_with_args_kwargs():
+    """
+    >>> isinstance(make_new_with_args_kwargs(), MyType)
+    CINIT
+    (1, 2, 3)
+    {'a': 4}
+    True
+    """
+    m = MyType.__new__(MyType, 1, 2 ,3, a=4)
+    print m.args
+    print m.kwargs
+    return m
+
+@cython.test_assert_path_exists('//PythonCapiCallNode')
+@cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode')
 def make_new_builtin():
     """
     >>> isinstance(make_new_builtin(), tuple)