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
