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):
def generate_cyfunction_code(self, code):
if self.specialized_cpdefs:
constructor = "__pyx_FusedFunction_NewEx"
def_node = self.specialized_cpdefs[0]
else:
constructor = "__Pyx_CyFunction_NewEx"
def_node = self.def_node
if self.code_object:
code_object_result = self.code_object.py_result()
......@@ -5956,9 +5958,9 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
code_object_result = 'NULL'
flags = []
if self.def_node.is_staticmethod:
if def_node.is_staticmethod:
flags.append('__Pyx_CYFUNCTION_STATICMETHOD')
elif self.def_node.is_classmethod:
elif def_node.is_classmethod:
flags.append('__Pyx_CYFUNCTION_CLASSMETHOD')
if flags:
flags = ' | '.join(flags)
......@@ -5979,7 +5981,7 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
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"
class_node = code.pyclass_stack[-1]
code.put_incref(self.py_result(), py_object_type)
......
......@@ -472,7 +472,7 @@ __pyx_FusedFunction_descr_get(PyObject *self, PyObject *obj, PyObject *type)
func = (__pyx_FusedFunctionObject *) self;
if (func->self) {
if (func->self || func->func.flags & __Pyx_CYFUNCTION_STATICMETHOD) {
/* Do not allow rebinding */
Py_INCREF(self);
return self;
......@@ -490,12 +490,18 @@ __pyx_FusedFunction_descr_get(PyObject *self, PyObject *obj, PyObject *type)
if (!meth)
return NULL;
Py_XINCREF(func->func.func_classobj);
meth->func.func_classobj = func->func.func_classobj;
Py_XINCREF(func->__signatures__);
meth->__signatures__ = func->__signatures__;
Py_XINCREF(type);
meth->type = type;
if (func->func.flags & __Pyx_CYFUNCTION_CLASSMETHOD)
obj = type;
Py_XINCREF(obj);
meth->self = obj;
......@@ -554,9 +560,16 @@ __pyx_err:
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,
self->self, self->type);
self->self, self->type);
}
Py_DECREF(signature);
Py_XDECREF(unbound_result_func);
......@@ -576,11 +589,13 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw)
__pyx_FusedFunctionObject *binding_func = (__pyx_FusedFunctionObject *) func;
Py_ssize_t argc = PyTuple_GET_SIZE(args);
PyObject *new_args = NULL;
PyObject *new_func = NULL;
__pyx_FusedFunctionObject *new_func = NULL;
PyObject *result = 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 */
Py_ssize_t i;
new_args = PyTuple_New(argc + 1);
......@@ -598,7 +613,7 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw)
}
args = new_args;
} else if (binding_func->type) {
} else if (binding_func->type && !is_staticmethod) {
/* Unbound method call */
if (argc < 1) {
PyErr_Format(PyExc_TypeError, "Need at least one argument, 0 given.");
......@@ -607,7 +622,8 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw)
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,
"First argument should be of type %s, got %s.",
((PyTypeObject *) binding_func->type)->tp_name,
......@@ -625,11 +641,17 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw)
if (!tup)
goto __pyx_err;
func = new_func = PyCFunction_Call(func, tup, NULL);
new_func = (__pyx_FusedFunctionObject *) PyCFunction_Call(func, tup, NULL);
Py_DECREF(tup);
if (!new_func)
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);
......
......@@ -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 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