cython-devel

changeset 2744:25af3c07d7c3

minor refactoring; fix handling first argument in classmethods; fix ticket #462: allow method(*args) in cdef classes
author Stefan Behnel <scoder@users.berlios.de>
date Sat Dec 05 23:39:57 2009 +0100 (2 years ago)
parents 3650762cf138
children d3155772b36f
files Cython/Compiler/Nodes.py tests/bugs.txt tests/run/cdef_classmethod.pyx tests/run/cdef_methods_T462.pyx
line diff
1.1 --- a/Cython/Compiler/Nodes.py Sat Dec 05 23:35:44 2009 +0100 1.2 +++ b/Cython/Compiler/Nodes.py Sat Dec 05 23:39:57 2009 +0100 1.3 @@ -671,7 +671,7 @@ 1.4 # longness integer 1.5 # complex boolean 1.6 # is_self_arg boolean Is self argument of C method 1.7 - # is_type_arg boolean Is type argument of class method 1.8 + # ##is_type_arg boolean Is type argument of class method 1.9 1.10 child_attrs = [] 1.11 arg_name = None # in case the argument name was interpreted as a type 1.12 @@ -690,6 +690,8 @@ 1.13 if self.is_self_arg and env.is_c_class_scope: 1.14 #print "CSimpleBaseTypeNode.analyse: defaulting to parent type" ### 1.15 type = env.parent_type 1.16 + ## elif self.is_type_arg and env.is_c_class_scope: 1.17 + ## type = Builtin.type_type 1.18 else: 1.19 type = py_object_type 1.20 else: 1.21 @@ -706,6 +708,8 @@ 1.22 elif could_be_name: 1.23 if self.is_self_arg and env.is_c_class_scope: 1.24 type = env.parent_type 1.25 + ## elif self.is_type_arg and env.is_c_class_scope: 1.26 + ## type = Builtin.type_type 1.27 else: 1.28 type = py_object_type 1.29 self.arg_name = self.name 1.30 @@ -1599,7 +1603,7 @@ 1.31 decorators = None 1.32 entry = None 1.33 acquire_gil = 0 1.34 - 1.35 + self_in_stararg = 0 1.36 1.37 def __init__(self, pos, **kwds): 1.38 FuncDefNode.__init__(self, pos, **kwds) 1.39 @@ -1675,6 +1679,28 @@ 1.40 directive_locals = getattr(cfunc, 'directive_locals', {})) 1.41 1.42 def analyse_declarations(self, env): 1.43 + self.is_classmethod = self.is_staticmethod = False 1.44 + if self.decorators: 1.45 + for decorator in self.decorators: 1.46 + func = decorator.decorator 1.47 + if func.is_name: 1.48 + self.is_classmethod |= func.name == 'classmethod' 1.49 + self.is_staticmethod |= func.name == 'staticmethod' 1.50 + 1.51 + if self.is_classmethod and env.lookup_here('classmethod'): 1.52 + # classmethod() was overridden - not much we can do here ... 1.53 + self.is_classmethod = False 1.54 + if self.is_staticmethod and env.lookup_here('staticmethod'): 1.55 + # staticmethod() was overridden - not much we can do here ... 1.56 + self.is_staticmethod = False 1.57 + 1.58 + self.analyse_argument_types(env) 1.59 + self.declare_pyfunction(env) 1.60 + self.analyse_signature(env) 1.61 + self.return_type = self.entry.signature.return_type() 1.62 + self.create_local_scope(env) 1.63 + 1.64 + def analyse_argument_types(self, env): 1.65 directive_locals = self.directive_locals = env.directives['locals'] 1.66 for arg in self.args: 1.67 if hasattr(arg, 'name'): 1.68 @@ -1707,27 +1733,8 @@ 1.69 if arg.not_none and not arg.type.is_extension_type: 1.70 error(self.pos, 1.71 "Only extension type arguments can have 'not None'") 1.72 - self.declare_pyfunction(env) 1.73 - self.analyse_signature(env) 1.74 - self.return_type = self.entry.signature.return_type() 1.75 - self.create_local_scope(env) 1.76 1.77 def analyse_signature(self, env): 1.78 - self.is_classmethod = self.is_staticmethod = False 1.79 - if self.decorators: 1.80 - for decorator in self.decorators: 1.81 - func = decorator.decorator 1.82 - if func.is_name: 1.83 - self.is_classmethod |= func.name == 'classmethod' 1.84 - self.is_staticmethod |= func.name == 'staticmethod' 1.85 - 1.86 - if self.is_classmethod and env.lookup_here('classmethod'): 1.87 - # classmethod() was overridden - not much we can do here ... 1.88 - self.is_classmethod = False 1.89 - if self.is_staticmethod and env.lookup_here('staticmethod'): 1.90 - # staticmethod() was overridden - not much we can do here ... 1.91 - self.is_staticmethod = False 1.92 - 1.93 any_type_tests_needed = 0 1.94 if self.entry.is_special: 1.95 self.entry.trivial_signature = len(self.args) == 1 and not (self.star_arg or self.starstar_arg) 1.96 @@ -1745,36 +1752,46 @@ 1.97 elif len(self.args) == 2: 1.98 if self.args[1].default is None and not self.args[1].kw_only: 1.99 self.entry.signature = TypeSlots.ibinaryfunc 1.100 + 1.101 sig = self.entry.signature 1.102 nfixed = sig.num_fixed_args() 1.103 - for i in range(nfixed): 1.104 - if i < len(self.args): 1.105 - arg = self.args[i] 1.106 - arg.is_generic = 0 1.107 - if sig.is_self_arg(i) and not self.is_staticmethod: 1.108 - if self.is_classmethod: 1.109 - arg.is_type_arg = 1 1.110 - arg.hdr_type = arg.type = Builtin.type_type 1.111 - else: 1.112 - arg.is_self_arg = 1 1.113 - arg.hdr_type = arg.type = env.parent_type 1.114 - arg.needs_conversion = 0 1.115 - else: 1.116 - arg.hdr_type = sig.fixed_arg_type(i) 1.117 - if not arg.type.same_as(arg.hdr_type): 1.118 - if arg.hdr_type.is_pyobject and arg.type.is_pyobject: 1.119 - arg.needs_type_test = 1 1.120 - any_type_tests_needed = 1 1.121 - else: 1.122 - arg.needs_conversion = 1 1.123 - if arg.needs_conversion: 1.124 - arg.hdr_cname = Naming.arg_prefix + arg.name 1.125 - else: 1.126 - arg.hdr_cname = Naming.var_prefix + arg.name 1.127 - else: 1.128 - self.bad_signature() 1.129 - return 1.130 - if nfixed < len(self.args): 1.131 + if sig is TypeSlots.pymethod_signature and nfixed == 1 \ 1.132 + and len(self.args) == 0 and self.star_arg: 1.133 + # this is the only case where a diverging number of 1.134 + # arguments is not an error - when we have no explicit 1.135 + # 'self' parameter as in method(*args) 1.136 + sig = self.entry.signature = TypeSlots.pyfunction_signature # self is not 'really' used 1.137 + self.self_in_stararg = 1 1.138 + nfixed = 0 1.139 + 1.140 + for i in range(min(nfixed, len(self.args))): 1.141 + arg = self.args[i] 1.142 + arg.is_generic = 0 1.143 + if sig.is_self_arg(i) and not self.is_staticmethod: 1.144 + if self.is_classmethod: 1.145 + arg.is_type_arg = 1 1.146 + arg.hdr_type = arg.type = Builtin.type_type 1.147 + else: 1.148 + arg.is_self_arg = 1 1.149 + arg.hdr_type = arg.type = env.parent_type 1.150 + arg.needs_conversion = 0 1.151 + else: 1.152 + arg.hdr_type = sig.fixed_arg_type(i) 1.153 + if not arg.type.same_as(arg.hdr_type): 1.154 + if arg.hdr_type.is_pyobject and arg.type.is_pyobject: 1.155 + arg.needs_type_test = 1 1.156 + any_type_tests_needed = 1 1.157 + else: 1.158 + arg.needs_conversion = 1 1.159 + if arg.needs_conversion: 1.160 + arg.hdr_cname = Naming.arg_prefix + arg.name 1.161 + else: 1.162 + arg.hdr_cname = Naming.var_prefix + arg.name 1.163 + 1.164 + if nfixed > len(self.args): 1.165 + self.bad_signature() 1.166 + return 1.167 + elif nfixed < len(self.args): 1.168 if not sig.has_generic_args: 1.169 self.bad_signature() 1.170 for arg in self.args: 1.171 @@ -1802,7 +1819,9 @@ 1.172 1.173 def signature_has_nongeneric_args(self): 1.174 argcount = len(self.args) 1.175 - if argcount == 0 or (argcount == 1 and self.args[0].is_self_arg): 1.176 + if argcount == 0 or ( 1.177 + argcount == 1 and (self.args[0].is_self_arg or 1.178 + self.args[0].is_type_arg)): 1.179 return 0 1.180 return 1 1.181 1.182 @@ -1845,7 +1864,7 @@ 1.183 arg.entry.used = 1 1.184 arg.entry.is_self_arg = arg.is_self_arg 1.185 if arg.hdr_type: 1.186 - if arg.is_self_arg or \ 1.187 + if arg.is_self_arg or arg.is_type_arg or \ 1.188 (arg.type.is_extension_type and not arg.hdr_type.is_extension_type): 1.189 arg.entry.is_declared_generic = 1 1.190 self.declare_python_arg(env, self.star_arg) 1.191 @@ -1881,12 +1900,12 @@ 1.192 def generate_function_header(self, code, with_pymethdef, proto_only=0): 1.193 arg_code_list = [] 1.194 sig = self.entry.signature 1.195 - if sig.has_dummy_arg: 1.196 + if sig.has_dummy_arg or self.self_in_stararg: 1.197 arg_code_list.append( 1.198 "PyObject *%s" % Naming.self_cname) 1.199 for arg in self.args: 1.200 if not arg.is_generic: 1.201 - if arg.is_self_arg: 1.202 + if arg.is_self_arg or arg.is_type_arg: 1.203 arg_code_list.append("PyObject *%s" % arg.hdr_cname) 1.204 else: 1.205 arg_code_list.append( 1.206 @@ -1941,7 +1960,7 @@ 1.207 def generate_argument_parsing_code(self, env, code): 1.208 # Generate PyArg_ParseTuple call for generic 1.209 # arguments, if any. 1.210 - if self.entry.signature.has_dummy_arg: 1.211 + if self.entry.signature.has_dummy_arg and not self.self_in_stararg: 1.212 # get rid of unused argument warning 1.213 code.putln("%s = %s;" % (Naming.self_cname, Naming.self_cname)) 1.214 1.215 @@ -1974,14 +1993,14 @@ 1.216 arg_entry = arg.entry 1.217 if arg.is_generic: 1.218 if arg.default: 1.219 - if not arg.is_self_arg: 1.220 + if not arg.is_self_arg and not arg.is_type_arg: 1.221 if arg.kw_only: 1.222 kw_only_args.append(arg) 1.223 else: 1.224 positional_args.append(arg) 1.225 elif arg.kw_only: 1.226 kw_only_args.append(arg) 1.227 - elif not arg.is_self_arg: 1.228 + elif not arg.is_self_arg and not arg.is_type_arg: 1.229 positional_args.append(arg) 1.230 1.231 self.generate_tuple_and_keyword_parsing_code( 1.232 @@ -2061,9 +2080,38 @@ 1.233 self.starstar_arg.entry.cname, self.error_value())) 1.234 self.starstar_arg.entry.xdecref_cleanup = 0 1.235 code.put_gotref(self.starstar_arg.entry.cname) 1.236 - 1.237 - 1.238 - if self.star_arg: 1.239 + 1.240 + if self.self_in_stararg: 1.241 + # need to create a new tuple with 'self' inserted as first item 1.242 + code.put("%s = PyTuple_New(PyTuple_GET_SIZE(%s)+1); if (unlikely(!%s)) " % ( 1.243 + self.star_arg.entry.cname, 1.244 + Naming.args_cname, 1.245 + self.star_arg.entry.cname)) 1.246 + if self.starstar_arg: 1.247 + code.putln("{") 1.248 + code.put_decref(self.starstar_arg.entry.cname, py_object_type) 1.249 + code.putln("return %s;" % self.error_value()) 1.250 + code.putln("}") 1.251 + else: 1.252 + code.putln("return %s;" % self.error_value()) 1.253 + code.put_gotref(self.star_arg.entry.cname) 1.254 + code.put_incref(Naming.self_cname, py_object_type) 1.255 + code.put_giveref(Naming.self_cname) 1.256 + code.putln("PyTuple_SET_ITEM(%s, 0, %s);" % ( 1.257 + self.star_arg.entry.cname, Naming.self_cname)) 1.258 + temp = code.funcstate.allocate_temp(PyrexTypes.c_py_ssize_t_type, manage_ref=False) 1.259 + code.putln("for (%s=0; %s < PyTuple_GET_SIZE(%s); %s++) {" % ( 1.260 + temp, temp, Naming.args_cname, temp)) 1.261 + code.putln("PyObject* item = PyTuple_GET_ITEM(%s, %s);" % ( 1.262 + Naming.args_cname, temp)) 1.263 + code.put_incref("item", py_object_type) 1.264 + code.put_giveref("item") 1.265 + code.putln("PyTuple_SET_ITEM(%s, %s+1, item);" % ( 1.266 + self.star_arg.entry.cname, temp)) 1.267 + code.putln("}") 1.268 + code.funcstate.release_temp(temp) 1.269 + self.star_arg.entry.xdecref_cleanup = 0 1.270 + elif self.star_arg: 1.271 code.put_incref(Naming.args_cname, py_object_type) 1.272 code.putln("%s = %s;" % ( 1.273 self.star_arg.entry.cname,
2.1 --- a/tests/bugs.txt Sat Dec 05 23:35:44 2009 +0100 2.2 +++ b/tests/bugs.txt Sat Dec 05 23:39:57 2009 +0100 2.3 @@ -7,4 +7,3 @@ 2.4 unsignedbehaviour_T184 2.5 missing_baseclass_in_predecl_T262 2.6 cfunc_call_tuple_args_T408 2.7 -cdef_methods_T462
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/tests/run/cdef_classmethod.pyx Sat Dec 05 23:39:57 2009 +0100 3.3 @@ -0,0 +1,76 @@ 3.4 + 3.5 +cimport cython 3.6 + 3.7 +cdef class cclass: 3.8 + 3.9 + @classmethod 3.10 + def test0(cls): 3.11 + """ 3.12 + >>> cclass.test0() 3.13 + 'type object' 3.14 + """ 3.15 + return cython.typeof(cls) 3.16 + 3.17 + @classmethod 3.18 + def test0_args(*args): 3.19 + """ 3.20 + >>> cclass.test0_args(1,2,3) 3.21 + ('Python object', (1, 2, 3)) 3.22 + """ 3.23 + return cython.typeof(args[0]), args[1:] 3.24 + 3.25 + @classmethod 3.26 + def test1(cls, arg): 3.27 + """ 3.28 + >>> cclass.test1(1) 3.29 + ('type object', 1) 3.30 + """ 3.31 + return cython.typeof(cls), arg 3.32 + 3.33 + @classmethod 3.34 + def test2(cls, arg1, arg2): 3.35 + """ 3.36 + >>> cclass.test2(1,2) 3.37 + ('type object', 1, 2) 3.38 + """ 3.39 + return cython.typeof(cls), arg1, arg2 3.40 + 3.41 + @classmethod 3.42 + def test1_args(cls, *args): 3.43 + """ 3.44 + >>> cclass.test1_args(1,2,3) 3.45 + ('type object', (1, 2, 3)) 3.46 + """ 3.47 + return cython.typeof(cls), args 3.48 + 3.49 + @classmethod 3.50 + def test2_args(cls, arg, *args): 3.51 + """ 3.52 + >>> cclass.test2_args(1,2,3) 3.53 + ('type object', 1, (2, 3)) 3.54 + """ 3.55 + return cython.typeof(cls), arg, args 3.56 + 3.57 + @classmethod 3.58 + def test0_args_kwargs(*args, **kwargs): 3.59 + """ 3.60 + >>> cclass.test0_args_kwargs(1,2,3) 3.61 + ('Python object', (1, 2, 3), {}) 3.62 + """ 3.63 + return cython.typeof(args[0]), args[1:], kwargs 3.64 + 3.65 + @classmethod 3.66 + def test1_args_kwargs(cls, *args, **kwargs): 3.67 + """ 3.68 + >>> cclass.test1_args_kwargs(1,2,3) 3.69 + ('type object', (1, 2, 3), {}) 3.70 + """ 3.71 + return cython.typeof(cls), args, kwargs 3.72 + 3.73 + @classmethod 3.74 + def test2_args_kwargs(cls, arg, *args, **kwargs): 3.75 + """ 3.76 + >>> cclass.test2_args_kwargs(1,2,3) 3.77 + ('type object', 1, (2, 3), {}) 3.78 + """ 3.79 + return cython.typeof(cls), arg, args, kwargs
4.1 --- a/tests/run/cdef_methods_T462.pyx Sat Dec 05 23:35:44 2009 +0100 4.2 +++ b/tests/run/cdef_methods_T462.pyx Sat Dec 05 23:39:57 2009 +0100 4.3 @@ -12,20 +12,27 @@ 4.4 def test_self_1(self, arg): 4.5 """ 4.6 >>> cclass().test_self_1(1) 4.7 - ('cclass', (1,)) 4.8 + ('cclass', 1) 4.9 """ 4.10 return cython.typeof(self), arg 4.11 4.12 def test_self_args(self, *args): 4.13 """ 4.14 - >>> cclass().normal_test1_args(1,2,3) 4.15 + >>> cclass().test_self_args(1,2,3) 4.16 ('cclass', (1, 2, 3)) 4.17 """ 4.18 return cython.typeof(self), args 4.19 4.20 def test_args(*args): 4.21 """ 4.22 - >>> cclass().normal_test0_args(1,2,3): 4.23 - ("Python object", (1, 2, 3)) 4.24 + >>> cclass().test_args(1,2,3) 4.25 + ('Python object', (1, 2, 3)) 4.26 """ 4.27 return cython.typeof(args[0]), args[1:] 4.28 + 4.29 + def test_args_kwargs(*args, **kwargs): 4.30 + """ 4.31 + >>> cclass().test_args_kwargs(1,2,3, a=4) 4.32 + ('Python object', (1, 2, 3), {'a': 4}) 4.33 + """ 4.34 + return cython.typeof(args[0]), args[1:], kwargs