From f7a50b7d254ec222b14545151bff89490580add6 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Sat, 23 Feb 2013 17:23:59 +0100 Subject: [PATCH] implement tp_new() optimisation with args/kwargs --- Cython/Compiler/Optimize.py | 39 ++++++++++++++++++++++++-------- Cython/Utility/ObjectHandling.c | 6 ++--- tests/run/tp_new.pyx | 50 ++++++++++++++++++++++++++++++++++------- 3 files changed, 75 insertions(+), 20 deletions(-) diff --git a/Cython/Compiler/Optimize.py b/Cython/Compiler/Optimize.py index 7fbdef3..2d03eef 100644 --- a/Cython/Compiler/Optimize.py +++ b/Cython/Compiler/Optimize.py @@ -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 diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 1d056a0..15e7efd 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -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)); } diff --git a/tests/run/tp_new.pyx b/tests/run/tp_new.pyx index 4107343..8b99da2 100644 --- a/tests/run/tp_new.pyx +++ b/tests/run/tp_new.pyx @@ -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) -- 2.7.4