Commit 824ec8d6 authored by Mark Florisson's avatar Mark Florisson

Support fused def in cdef classes

parent 5c1338cb
......@@ -5962,6 +5962,10 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
flags.append('__Pyx_CYFUNCTION_STATICMETHOD')
elif def_node.is_classmethod:
flags.append('__Pyx_CYFUNCTION_CLASSMETHOD')
if def_node.local_scope.parent_scope.is_c_class_scope:
flags.append('__Pyx_CYFUNCTION_CCLASS')
if flags:
flags = ' | '.join(flags)
else:
......
......@@ -2627,6 +2627,8 @@ class DefNode(FuncDefNode):
# (in case this is a specialization)
# specialized_cpdefs [DefNode] list of specialized cpdef DefNodes
# py_cfunc_node PyCFunctionNode/InnerFunctionNode The PyCFunction to create and assign
#
# decorator_indirection IndirectionNode Used to remove __Pyx_Method_ClassMethod for fused functions
child_attrs = ["args", "star_arg", "starstar_arg", "body", "decorators"]
......@@ -2858,6 +2860,9 @@ class DefNode(FuncDefNode):
sig.is_staticmethod = True
sig.has_generic_args = True
if self.is_classmethod and self.has_fused_arguments and env.is_c_class_scope:
del self.decorator_indirection.stats[:]
for i in range(min(nfixed, len(self.args))):
arg = self.args[i]
arg.is_generic = 0
......@@ -4910,6 +4915,15 @@ class PassStatNode(StatNode):
pass
class IndirectionNode(StatListNode):
"""
This adds an indirection so that the node can be shared and a subtree can
be removed at any time by clearing self.stats.
"""
def __init__(self, stats):
super(IndirectionNode, self).__init__(stats[0].pos, stats=stats)
class BreakStatNode(StatNode):
child_attrs = []
......
......@@ -1227,6 +1227,8 @@ class DecoratorTransform(ScopeTrackingTransform, SkipDeclarations):
to the function/class name - except for cdef class methods. For
those, the reassignment is required as methods are originally
defined in the PyMethodDef struct.
The IndirectionNode allows DefNode to override the decorator
"""
def visit_DefNode(self, func_node):
......@@ -1250,6 +1252,9 @@ class DecoratorTransform(ScopeTrackingTransform, SkipDeclarations):
node.pos,
lhs = name_node,
rhs = decorator_result)
reassignment = Nodes.IndirectionNode([reassignment])
node.decorator_indirection = reassignment
return [node, reassignment]
class CnameDirectivesTransform(CythonTransform, SkipDeclarations):
......
......@@ -6,6 +6,7 @@
#define __Pyx_CYFUNCTION_STATICMETHOD 0x01
#define __Pyx_CYFUNCTION_CLASSMETHOD 0x02
#define __Pyx_CYFUNCTION_CCLASS 0x04
#define __Pyx_CyFunction_GetClosure(f) \
(((__pyx_CyFunctionObject *) (f))->func_closure)
......@@ -473,7 +474,7 @@ __pyx_FusedFunction_descr_get(PyObject *self, PyObject *obj, PyObject *type)
func = (__pyx_FusedFunctionObject *) self;
if (func->self || func->func.flags & __Pyx_CYFUNCTION_STATICMETHOD) {
/* Do not allow rebinding */
/* Do not allow rebinding and don't do anything for static methods */
Py_INCREF(self);
return self;
}
......@@ -577,9 +578,49 @@ __pyx_err:
return result_func;
}
static PyObject *
__pyx_FusedFunction_callfunction(PyObject *func, PyObject *args, PyObject *kw)
{
__pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *) func;
PyObject *result;
int static_specialized = (cyfunc->flags & __Pyx_CYFUNCTION_STATICMETHOD &&
!((__pyx_FusedFunctionObject *) func)->__signatures__);
//PyObject_Print(args, stdout, Py_PRINT_RAW);
if (cyfunc->flags & __Pyx_CYFUNCTION_CCLASS && !static_specialized) {
Py_ssize_t argc;
PyObject *new_args;
PyObject *self;
PyObject *m_self;
argc = PyTuple_GET_SIZE(args);
new_args = PyTuple_GetSlice(args, 1, argc);
if (!new_args)
return NULL;
self = PyTuple_GetItem(args, 0);
if (!self)
return NULL;
m_self = cyfunc->func.m_self;
cyfunc->func.m_self = self;
result = PyCFunction_Call(func, new_args, kw);
cyfunc->func.m_self = m_self;
Py_DECREF(new_args);
} else {
result = PyCFunction_Call(func, args, kw);
}
return result;
}
/* Note: the 'self' from method binding is passed in in the args tuple,
whereas PyCFunctionObject's m_self is passed in as the first
argument to the C function. For extension methods we also need
argument to the C function. For extension methods we need
to pass 'self' as 'm_self' and not as the first element of the
args tuple.
*/
......@@ -595,7 +636,7 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw)
int is_staticmethod = binding_func->func.flags & __Pyx_CYFUNCTION_STATICMETHOD;
int is_classmethod = binding_func->func.flags & __Pyx_CYFUNCTION_CLASSMETHOD;
if (binding_func->self && !is_staticmethod) {
if (binding_func->self) {
/* Bound method call, put 'self' in the args tuple */
Py_ssize_t i;
new_args = PyTuple_New(argc + 1);
......@@ -613,7 +654,7 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw)
}
args = new_args;
} else if (binding_func->type && !is_staticmethod) {
} else if (binding_func->type) {
/* Unbound method call */
if (argc < 1) {
PyErr_Format(PyExc_TypeError, "Need at least one argument, 0 given.");
......@@ -632,16 +673,12 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw)
}
if (binding_func->__signatures__) {
/*
binaryfunc meth = (binaryfunc) binding_func->func.m_ml->ml_meth;
func = new_func = meth(binding_func->__signatures__, args);
*/
PyObject *tup = PyTuple_Pack(3, binding_func->__signatures__, args,
kw == NULL ? Py_None : kw);
kw == NULL ? Py_None : kw);
if (!tup)
goto __pyx_err;
new_func = (__pyx_FusedFunctionObject *) PyCFunction_Call(func, tup, NULL);
new_func = (__pyx_FusedFunctionObject *) __pyx_FusedFunction_callfunction(func, tup, NULL);;
Py_DECREF(tup);
if (!new_func)
......@@ -654,7 +691,7 @@ __pyx_FusedFunction_call(PyObject *func, PyObject *args, PyObject *kw)
func = (PyObject *) new_func;
}
result = PyCFunction_Call(func, args, kw);
result = __pyx_FusedFunction_callfunction(func, args, kw);
__pyx_err:
Py_XDECREF(new_args);
Py_XDECREF((PyObject *) new_func);
......
......@@ -231,3 +231,66 @@ def test_fused_def_classmethod():
SubSubClass().myclassmethod[cy.short](12)
SubSubClass.myclassmethod[cy.short](13)
cdef class CBaseClass(object):
"""
Test fused def and cpdef methods in cdef classes.
>>> import cython as cy
>>> obj = CBaseClass()
>>> cls = CBaseClass
>>> obj.mystaticmethod(10)
long 10
>>> obj.mystaticmethod[cy.short](10)
short 10
>>> cls.mystaticmethod(10)
long 10
>>> cls.mystaticmethod[cy.short](10)
short 10
>>> obj.myclassmethod(10)
CBaseClass long 10
>>> obj.myclassmethod[cy.short](10)
CBaseClass short 10
>>> cls.myclassmethod(10)
CBaseClass long 10
>>> cls.myclassmethod[cy.short](10)
CBaseClass short 10
>>> obj.normalmethod(10, 11, 12)
<fused_def.CBaseClass object> long 10 11 12
>>> obj.normalmethod[cy.short](10, 11, 12)
<fused_def.CBaseClass object> short 10 11 12
>>> cls.normalmethod(obj, 10, 11, 12)
<fused_def.CBaseClass object> long 10 11 12
>>> cls.normalmethod[cy.short](obj, 10, 11, 12)
<fused_def.CBaseClass object> short 10 11 12
>>> obj.cpdefmethod(10)
<fused_def.CBaseClass object> long 10
>>> obj.cpdefmethod[cy.short](10)
<fused_def.CBaseClass object> short 10
>>> cls.cpdefmethod(obj, 10)
<fused_def.CBaseClass object> long 10
>>> cls.cpdefmethod[cy.short](obj, 10)
<fused_def.CBaseClass object> short 10
"""
@staticmethod
def mystaticmethod(cython.integral arg1):
print cython.typeof(arg1), arg1
@classmethod
def myclassmethod(cls, cython.integral arg1):
print cls.__name__, cython.typeof(arg1), arg1
def normalmethod(self, cython.integral arg1, arg2, arg3):
print self, cython.typeof(arg1), arg1, arg2, arg3
cpdef cpdefmethod(self, cython.integral arg1):
print self, cython.typeof(arg1), arg1
def __repr__(self):
return "<%s.%s object>" % (__name__, type(self).__name__)
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