Commit 835010c7 authored by Mark Florisson's avatar Mark Florisson

Support fused static- and classmethods + super()

parent f66909db
...@@ -5947,8 +5947,10 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin): ...@@ -5947,8 +5947,10 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
def generate_cyfunction_code(self, code): def generate_cyfunction_code(self, code):
if self.specialized_cpdefs: if self.specialized_cpdefs:
constructor = "__pyx_FusedFunction_NewEx" constructor = "__pyx_FusedFunction_NewEx"
def_node = self.specialized_cpdefs[0]
else: else:
constructor = "__Pyx_CyFunction_NewEx" constructor = "__Pyx_CyFunction_NewEx"
def_node = self.def_node
if self.code_object: if self.code_object:
code_object_result = self.code_object.py_result() code_object_result = self.code_object.py_result()
...@@ -5956,9 +5958,9 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin): ...@@ -5956,9 +5958,9 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
code_object_result = 'NULL' code_object_result = 'NULL'
flags = [] flags = []
if self.def_node.is_staticmethod: if def_node.is_staticmethod:
flags.append('__Pyx_CYFUNCTION_STATICMETHOD') flags.append('__Pyx_CYFUNCTION_STATICMETHOD')
elif self.def_node.is_classmethod: elif def_node.is_classmethod:
flags.append('__Pyx_CYFUNCTION_CLASSMETHOD') flags.append('__Pyx_CYFUNCTION_CLASSMETHOD')
if flags: if flags:
flags = ' | '.join(flags) flags = ' | '.join(flags)
...@@ -5979,7 +5981,7 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin): ...@@ -5979,7 +5981,7 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
code.put_gotref(self.py_result()) code.put_gotref(self.py_result())
if self.def_node.requires_classobj: if def_node.requires_classobj:
assert code.pyclass_stack, "pyclass_stack is empty" assert code.pyclass_stack, "pyclass_stack is empty"
class_node = code.pyclass_stack[-1] class_node = code.pyclass_stack[-1]
code.put_incref(self.py_result(), py_object_type) code.put_incref(self.py_result(), py_object_type)
......
...@@ -472,7 +472,7 @@ __pyx_FusedFunction_descr_get(PyObject *self, PyObject *obj, PyObject *type) ...@@ -472,7 +472,7 @@ __pyx_FusedFunction_descr_get(PyObject *self, PyObject *obj, PyObject *type)
func = (__pyx_FusedFunctionObject *) self; func = (__pyx_FusedFunctionObject *) self;
if (func->self) { if (func->self || func->func.flags & __Pyx_CYFUNCTION_STATICMETHOD) {
/* Do not allow rebinding */ /* Do not allow rebinding */
Py_INCREF(self); Py_INCREF(self);
return self; return self;
...@@ -490,12 +490,18 @@ __pyx_FusedFunction_descr_get(PyObject *self, PyObject *obj, PyObject *type) ...@@ -490,12 +490,18 @@ __pyx_FusedFunction_descr_get(PyObject *self, PyObject *obj, PyObject *type)
if (!meth) if (!meth)
return NULL; return NULL;
Py_XINCREF(func->func.func_classobj);
meth->func.func_classobj = func->func.func_classobj;
Py_XINCREF(func->__signatures__); Py_XINCREF(func->__signatures__);
meth->__signatures__ = func->__signatures__; meth->__signatures__ = func->__signatures__;
Py_XINCREF(type); Py_XINCREF(type);
meth->type = type; meth->type = type;
if (func->func.flags & __Pyx_CYFUNCTION_CLASSMETHOD)
obj = type;
Py_XINCREF(obj); Py_XINCREF(obj);
meth->self = obj; meth->self = obj;
...@@ -554,9 +560,16 @@ __pyx_err: ...@@ -554,9 +560,16 @@ __pyx_err:
unbound_result_func = PyObject_GetItem(self->__signatures__, signature); unbound_result_func = PyObject_GetItem(self->__signatures__, signature);
if (unbound_result_func) if (unbound_result_func) {
__pyx_FusedFunctionObject *unbound = (__pyx_FusedFunctionObject *) unbound_result_func;
Py_CLEAR(unbound->func.func_classobj);
Py_XINCREF(self->func.func_classobj);
unbound->func.func_classobj = self->func.func_classobj;
result_func = __pyx_FusedFunction_descr_get(unbound_result_func, result_func = __pyx_FusedFunction_descr_get(unbound_result_func,
self->self, self->type); self->self, self->type);
}
Py_DECREF(signature); Py_DECREF(signature);
Py_XDECREF(unbound_result_func); Py_XDECREF(unbound_result_func);
...@@ -576,11 +589,13 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw) ...@@ -576,11 +589,13 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw)
__pyx_FusedFunctionObject *binding_func = (__pyx_FusedFunctionObject *) func; __pyx_FusedFunctionObject *binding_func = (__pyx_FusedFunctionObject *) func;
Py_ssize_t argc = PyTuple_GET_SIZE(args); Py_ssize_t argc = PyTuple_GET_SIZE(args);
PyObject *new_args = NULL; PyObject *new_args = NULL;
PyObject *new_func = NULL; __pyx_FusedFunctionObject *new_func = NULL;
PyObject *result = NULL; PyObject *result = NULL;
PyObject *self = NULL; PyObject *self = NULL;
int is_staticmethod = binding_func->func.flags & __Pyx_CYFUNCTION_STATICMETHOD;
int is_classmethod = binding_func->func.flags & __Pyx_CYFUNCTION_CLASSMETHOD;
if (binding_func->self) { if (binding_func->self && !is_staticmethod) {
/* Bound method call, put 'self' in the args tuple */ /* Bound method call, put 'self' in the args tuple */
Py_ssize_t i; Py_ssize_t i;
new_args = PyTuple_New(argc + 1); new_args = PyTuple_New(argc + 1);
...@@ -598,7 +613,7 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw) ...@@ -598,7 +613,7 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw)
} }
args = new_args; args = new_args;
} else if (binding_func->type) { } else if (binding_func->type && !is_staticmethod) {
/* Unbound method call */ /* Unbound method call */
if (argc < 1) { if (argc < 1) {
PyErr_Format(PyExc_TypeError, "Need at least one argument, 0 given."); PyErr_Format(PyExc_TypeError, "Need at least one argument, 0 given.");
...@@ -607,7 +622,8 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw) ...@@ -607,7 +622,8 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw)
self = PyTuple_GET_ITEM(args, 0); self = PyTuple_GET_ITEM(args, 0);
} }
if (self && !PyObject_IsInstance(self, binding_func->type)) { if (self && !is_classmethod && !is_staticmethod &&
!PyObject_IsInstance(self, binding_func->type)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"First argument should be of type %s, got %s.", "First argument should be of type %s, got %s.",
((PyTypeObject *) binding_func->type)->tp_name, ((PyTypeObject *) binding_func->type)->tp_name,
...@@ -625,11 +641,17 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw) ...@@ -625,11 +641,17 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw)
if (!tup) if (!tup)
goto __pyx_err; goto __pyx_err;
func = new_func = PyCFunction_Call(func, tup, NULL); new_func = (__pyx_FusedFunctionObject *) PyCFunction_Call(func, tup, NULL);
Py_DECREF(tup); Py_DECREF(tup);
if (!new_func) if (!new_func)
goto __pyx_err; goto __pyx_err;
Py_CLEAR(new_func->func.func_classobj);
Py_XINCREF(binding_func->func.func_classobj);
new_func->func.func_classobj = binding_func->func.func_classobj;
func = (PyObject *) new_func;
} }
result = PyCFunction_Call(func, args, kw); result = PyCFunction_Call(func, args, kw);
......
...@@ -128,3 +128,98 @@ def args_kwargs(fused_t obj, cython.floating myf = 1.2, *args, **kwargs): ...@@ -128,3 +128,98 @@ def args_kwargs(fused_t obj, cython.floating myf = 1.2, *args, **kwargs):
""" """
print cython.typeof(obj), cython.typeof(myf) print cython.typeof(obj), cython.typeof(myf)
print obj, "%.2f" % myf, "%.2f" % f, args, kwargs print obj, "%.2f" % myf, "%.2f" % f, args, kwargs
class BaseClass(object):
"""
Test fused class/static/normal methods and super() without args
"""
@staticmethod
def mystaticmethod(cython.integral arg1):
print cython.typeof(arg1), arg1
@classmethod
def myclassmethod(cls, cython.integral arg1):
print cls, cython.typeof(arg1), arg1
def normalmethod(self, cython.integral arg1):
print self, cython.typeof(arg1), arg1
def __repr__(self):
return "<%s.%s object>" % (__name__, type(self).__name__)
class SubClass(BaseClass):
@staticmethod
def mystaticmethod(cython.integral arg1):
print cython.typeof(arg1), arg1
super().mystaticmethod(arg1 + 1)
@classmethod
def myclassmethod(cls, cython.integral arg1):
print cls, cython.typeof(arg1), arg1
super().myclassmethod(arg1 + 1)
def normalmethod(self, cython.integral arg1):
print self, cython.typeof(arg1), arg1
super().normalmethod(arg1 + 1)
class SubSubClass(SubClass):
pass
def test_fused_def_super():
"""
>>> test_fused_def_super()
<class 'fused_def.SubClass'> long 14
<class 'fused_def.SubClass'> long 15
<class 'fused_def.SubClass'> long 15
<class 'fused_def.SubClass'> long 16
<class 'fused_def.SubClass'> short 16
<class 'fused_def.SubClass'> long 17
<class 'fused_def.SubClass'> short 17
<class 'fused_def.SubClass'> long 18
<fused_def.SubClass object> long 18
<fused_def.SubClass object> long 19
<fused_def.SubClass object> long 19
<fused_def.SubClass object> long 20
<fused_def.SubClass object> short 20
<fused_def.SubClass object> long 21
<fused_def.SubClass object> short 21
<fused_def.SubClass object> long 22
"""
obj = SubClass()
cls = SubClass
#obj.mystaticmethod(10)
#cls.mystaticmethod(11)
#obj.mystaticmethod[cy.short](12)
#cls.mystaticmethod[cy.short](13)
obj.myclassmethod(14)
cls.myclassmethod(15)
obj.myclassmethod[cy.short](16)
cls.myclassmethod[cy.short](17)
obj.normalmethod(18)
cls.normalmethod(obj, 19)
obj.normalmethod[cy.short](20)
cls.normalmethod[cy.short](obj, 21)
def test_fused_def_classmethod():
"""
>>> test_fused_def_classmethod()
<class 'fused_def.SubSubClass'> long 10
<class 'fused_def.SubSubClass'> long 11
<class 'fused_def.SubSubClass'> long 11
<class 'fused_def.SubSubClass'> long 12
<class 'fused_def.SubSubClass'> short 12
<class 'fused_def.SubSubClass'> long 13
<class 'fused_def.SubSubClass'> short 13
<class 'fused_def.SubSubClass'> long 14
"""
SubSubClass().myclassmethod(10)
SubSubClass.myclassmethod(11)
SubSubClass().myclassmethod[cy.short](12)
SubSubClass.myclassmethod[cy.short](13)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment