Commit d7d82683 authored by Stefan Behnel's avatar Stefan Behnel

implement binding Python methods in extension types

enables line tracing etc.
parent 46cd02cc
...@@ -1973,7 +1973,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1973,7 +1973,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
"};") "};")
def generate_method_table(self, env, code): def generate_method_table(self, env, code):
if env.is_c_class_scope and not env.pyfunc_entries: if env.is_c_class_scope and (not env.pyfunc_entries or env.directives['binding']):
return return
code.putln("") code.putln("")
code.putln( code.putln(
......
...@@ -3039,10 +3039,12 @@ class DefNode(FuncDefNode): ...@@ -3039,10 +3039,12 @@ class DefNode(FuncDefNode):
return False return False
if self.no_assignment_synthesis: if self.no_assignment_synthesis:
return False return False
if self.entry.is_special:
return False
# Should enable for module level as well, that will require more testing... # Should enable for module level as well, that will require more testing...
if self.entry.is_anonymous: if self.entry.is_anonymous:
return True return True
if env.is_module_scope: if env.is_module_scope or env.is_c_class_scope:
if code is None: if code is None:
return env.directives['binding'] return env.directives['binding']
else: else:
......
...@@ -471,7 +471,7 @@ class MethodTableSlot(SlotDescriptor): ...@@ -471,7 +471,7 @@ class MethodTableSlot(SlotDescriptor):
# Slot descriptor for the method table. # Slot descriptor for the method table.
def slot_code(self, scope): def slot_code(self, scope):
if scope.pyfunc_entries: if scope.pyfunc_entries and not scope.directives['binding']:
return scope.method_table_cname return scope.method_table_cname
else: else:
return "0" return "0"
......
...@@ -571,13 +571,10 @@ __Pyx_CyFunction_repr(__pyx_CyFunctionObject *op) ...@@ -571,13 +571,10 @@ __Pyx_CyFunction_repr(__pyx_CyFunctionObject *op)
#endif #endif
} }
#if CYTHON_COMPILING_IN_PYPY static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, PyObject *arg, PyObject *kw) {
// originally copied from PyCFunction_Call() in CPython's Objects/methodobject.c // originally copied from PyCFunction_Call() in CPython's Objects/methodobject.c
// PyPy does not have this function
static PyObject * __Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) {
PyCFunctionObject* f = (PyCFunctionObject*)func; PyCFunctionObject* f = (PyCFunctionObject*)func;
PyCFunction meth = f->m_ml->ml_meth; PyCFunction meth = f->m_ml->ml_meth;
PyObject *self = f->m_self;
Py_ssize_t size; Py_ssize_t size;
switch (f->m_ml->ml_flags & (METH_VARARGS | METH_KEYWORDS | METH_NOARGS | METH_O)) { switch (f->m_ml->ml_flags & (METH_VARARGS | METH_KEYWORDS | METH_NOARGS | METH_O)) {
...@@ -625,11 +622,38 @@ static PyObject * __Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject ...@@ -625,11 +622,38 @@ static PyObject * __Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject
f->m_ml->ml_name); f->m_ml->ml_name);
return NULL; return NULL;
} }
#else
static PyObject * __Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) { static CYTHON_INLINE PyObject *__Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) {
return PyCFunction_Call(func, arg, kw); return __Pyx_CyFunction_CallMethod(func, ((PyCFunctionObject*)func)->m_self, arg, kw);
}
static PyObject *__Pyx_CyFunction_CallAsMethod(PyObject *func, PyObject *args, PyObject *kw) {
PyObject *result;
__pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *) func;
if (cyfunc->flags & __Pyx_CYFUNCTION_CCLASS) {
Py_ssize_t argc;
PyObject *new_args;
PyObject *self;
argc = PyTuple_GET_SIZE(args);
new_args = PyTuple_GetSlice(args, 1, argc);
if (unlikely(!new_args))
return NULL;
self = PyTuple_GetItem(args, 0);
if (unlikely(!self)) {
Py_DECREF(new_args);
return NULL;
}
result = __Pyx_CyFunction_CallMethod(func, self, new_args, kw);
Py_DECREF(new_args);
} else {
result = __Pyx_CyFunction_Call(func, args, kw);
}
return result;
} }
#endif
static PyTypeObject __pyx_CyFunctionType_type = { static PyTypeObject __pyx_CyFunctionType_type = {
PyVarObject_HEAD_INIT(0, 0) PyVarObject_HEAD_INIT(0, 0)
...@@ -650,7 +674,7 @@ static PyTypeObject __pyx_CyFunctionType_type = { ...@@ -650,7 +674,7 @@ static PyTypeObject __pyx_CyFunctionType_type = {
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
0, /*tp_as_mapping*/ 0, /*tp_as_mapping*/
0, /*tp_hash*/ 0, /*tp_hash*/
__Pyx_CyFunction_Call, /*tp_call*/ __Pyx_CyFunction_CallAsMethod, /*tp_call*/
0, /*tp_str*/ 0, /*tp_str*/
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
...@@ -694,10 +718,6 @@ static PyTypeObject __pyx_CyFunctionType_type = { ...@@ -694,10 +718,6 @@ static PyTypeObject __pyx_CyFunctionType_type = {
static int __pyx_CyFunction_init(void) { static int __pyx_CyFunction_init(void) {
#if !CYTHON_COMPILING_IN_PYPY
// avoid a useless level of call indirection
__pyx_CyFunctionType_type.tp_call = PyCFunction_Call;
#endif
__pyx_CyFunctionType = __Pyx_FetchCommonType(&__pyx_CyFunctionType_type); __pyx_CyFunctionType = __Pyx_FetchCommonType(&__pyx_CyFunctionType_type);
if (__pyx_CyFunctionType == NULL) { if (__pyx_CyFunctionType == NULL) {
return -1; return -1;
...@@ -968,38 +988,14 @@ static PyObject * ...@@ -968,38 +988,14 @@ static PyObject *
__pyx_FusedFunction_callfunction(PyObject *func, PyObject *args, PyObject *kw) __pyx_FusedFunction_callfunction(PyObject *func, PyObject *args, PyObject *kw)
{ {
__pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *) func; __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *) func;
PyObject *result;
int static_specialized = (cyfunc->flags & __Pyx_CYFUNCTION_STATICMETHOD && int static_specialized = (cyfunc->flags & __Pyx_CYFUNCTION_STATICMETHOD &&
!((__pyx_FusedFunctionObject *) func)->__signatures__); !((__pyx_FusedFunctionObject *) func)->__signatures__);
if (cyfunc->flags & __Pyx_CYFUNCTION_CCLASS && !static_specialized) { if (cyfunc->flags & __Pyx_CYFUNCTION_CCLASS && !static_specialized) {
Py_ssize_t argc; return __Pyx_CyFunction_CallAsMethod(func, args, kw);
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 = __Pyx_CyFunction_Call(func, new_args, kw);
cyfunc->func.m_self = m_self;
Py_DECREF(new_args);
} else { } else {
result = __Pyx_CyFunction_Call(func, args, kw); return __Pyx_CyFunction_Call(func, args, kw);
} }
return result;
} }
// Note: the 'self' from method binding is passed in in the args tuple, // Note: the 'self' from method binding is passed in in the args tuple,
......
...@@ -21,8 +21,6 @@ setup( ...@@ -21,8 +21,6 @@ setup(
######## test_profile.py ########### ######## test_profile.py ###########
from collatz import collatz
try: try:
import line_profiler import line_profiler
except ImportError: except ImportError:
...@@ -30,20 +28,45 @@ except ImportError: ...@@ -30,20 +28,45 @@ except ImportError:
import sys import sys
sys.exit(0) sys.exit(0)
profile = line_profiler.LineProfiler(collatz)
profile.runcall(collatz, 19)
profile.print_stats()
stats = profile.get_stats() def assert_stats(profile, name):
assert len(stats.timings) > 0, "No profile stats." profile.print_stats()
for key, timings in stats.timings.items(): stats = profile.get_stats()
if key[-1] == 'collatz': assert len(stats.timings) > 0, "No profile stats."
for key, timings in stats.timings.items():
if key[-1] == name:
assert len(timings) > 0 assert len(timings) > 0
break break
else: else:
raise ValueError("No stats for collatz.") raise ValueError("No stats for %s." % name)
from collatz import collatz
func = collatz
profile = line_profiler.LineProfiler(func)
profile.runcall(func, 19)
assert_stats(profile, func.__name__)
from collatz import PyClass
obj = PyClass()
func = obj.py_pymethod
profile = line_profiler.LineProfiler(func)
profile.runcall(func)
assert_stats(profile, func.__name__)
from collatz import CClass
obj = CClass()
func = obj.c_pymethod
print(func)
print(type(func))
print(func.__func__)
profile = line_profiler.LineProfiler(func)
profile.runcall(func)
assert_stats(profile, func.__name__)
######## collatz.pyx ########### ######## collatz.pyx ###########
# cython: binding=True
def collatz(n): def collatz(n):
while n > 1: while n > 1:
...@@ -51,3 +74,24 @@ def collatz(n): ...@@ -51,3 +74,24 @@ def collatz(n):
n //= 2 n //= 2
else: else:
n = 3*n+1 n = 3*n+1
class PyClass(object):
def py_pymethod(self):
x = 1
for i in range(10):
a = x + 2
return a * 3
cdef class CClass:
def c_pymethod(self, c=2):
for i in range(10):
a = c + 1
y = self.cmethod(c + a)
return y * 4
cdef cmethod(self, s):
for i in range(10):
p = s + 3
return p * 5
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