Commit 395b9584 authored by Stefan Behnel's avatar Stefan Behnel

Merge branch '_binding_methods'

parents ceafa531 5f0eb2c6
...@@ -46,6 +46,9 @@ Bugs fixed ...@@ -46,6 +46,9 @@ Bugs fixed
* Some function signatures in ``libc.math`` and ``numpy.pxd`` were incorrect. * Some function signatures in ``libc.math`` and ``numpy.pxd`` were incorrect.
Patch by Michael Seifert. Patch by Michael Seifert.
* Misplaced usages of the module-level ``linetrace`` and ``profile`` directives
were silently ignored.
Other changes Other changes
------------- -------------
......
...@@ -1877,11 +1877,13 @@ class NameNode(AtomicExprNode): ...@@ -1877,11 +1877,13 @@ class NameNode(AtomicExprNode):
def analyse_target_types(self, env): def analyse_target_types(self, env):
self.analyse_entry(env, is_target=True) self.analyse_entry(env, is_target=True)
if self.entry.is_cfunction and self.entry.as_variable: entry = self.entry
if self.entry.is_overridable or not self.is_lvalue() and self.entry.fused_cfunction: if entry.is_cfunction and entry.as_variable:
# FIXME: unify "is_overridable" flags below
if (entry.is_overridable or entry.type.is_overridable) or not self.is_lvalue() and entry.fused_cfunction:
# We need this for assigning to cpdef names and for the fused 'def' TreeFragment # We need this for assigning to cpdef names and for the fused 'def' TreeFragment
self.entry = self.entry.as_variable entry = self.entry = entry.as_variable
self.type = self.entry.type self.type = entry.type
if self.type.is_const: if self.type.is_const:
error(self.pos, "Assignment to const '%s'" % self.name) error(self.pos, "Assignment to const '%s'" % self.name)
...@@ -1890,10 +1892,10 @@ class NameNode(AtomicExprNode): ...@@ -1890,10 +1892,10 @@ class NameNode(AtomicExprNode):
if not self.is_lvalue(): if not self.is_lvalue():
error(self.pos, "Assignment to non-lvalue '%s'" % self.name) error(self.pos, "Assignment to non-lvalue '%s'" % self.name)
self.type = PyrexTypes.error_type self.type = PyrexTypes.error_type
self.entry.used = 1 entry.used = 1
if self.entry.type.is_buffer: if entry.type.is_buffer:
from . import Buffer from . import Buffer
Buffer.used_buffer_aux_vars(self.entry) Buffer.used_buffer_aux_vars(entry)
return self return self
def analyse_rvalue_entry(self, env): def analyse_rvalue_entry(self, env):
...@@ -5583,7 +5585,7 @@ class PyMethodCallNode(SimpleCallNode): ...@@ -5583,7 +5585,7 @@ class PyMethodCallNode(SimpleCallNode):
len(args)+1, len(args)+1,
self_arg, self_arg,
', '.join(arg.py_result() for arg in args))) ', '.join(arg.py_result() for arg in args)))
code.putln("%s = __Pyx_PyFunction_FastCall(%s, %s+1-%s, %d+%s, NULL); %s" % ( code.putln("%s = __Pyx_PyFunction_FastCall(%s, %s+1-%s, %d+%s); %s" % (
self.result(), self.result(),
function, function,
Naming.quick_temp_cname, Naming.quick_temp_cname,
...@@ -8730,8 +8732,16 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin): ...@@ -8730,8 +8732,16 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
default_args = [] default_args = []
default_kwargs = [] default_kwargs = []
annotations = [] annotations = []
# For global cpdef functions and def/cpdef methods in cdef classes, we must use global constants
# for default arguments to avoid the dependency on the CyFunction object as 'self' argument
# in the underlying C function. Basically, cpdef functions/methods are static C functions,
# so their optional arguments must be static, too.
# TODO: change CyFunction implementation to pass both function object and owning object for method calls
must_use_constants = env.is_c_class_scope or (self.def_node.is_wrapper and env.is_module_scope)
for arg in self.def_node.args: for arg in self.def_node.args:
if arg.default: if arg.default and not must_use_constants:
if not arg.default.is_literal: if not arg.default.is_literal:
arg.is_dynamic = True arg.is_dynamic = True
if arg.type.is_pyobject: if arg.type.is_pyobject:
...@@ -8820,8 +8830,10 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin): ...@@ -8820,8 +8830,10 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
self.pos, args=[defaults_tuple, defaults_kwdict])), self.pos, args=[defaults_tuple, defaults_kwdict])),
decorators=None, decorators=None,
name=StringEncoding.EncodedString("__defaults__")) name=StringEncoding.EncodedString("__defaults__"))
defaults_getter.analyse_declarations(env) # defaults getter must never live in class scopes, it's always a module function
defaults_getter = defaults_getter.analyse_expressions(env) module_scope = env.global_scope()
defaults_getter.analyse_declarations(module_scope)
defaults_getter = defaults_getter.analyse_expressions(module_scope)
defaults_getter.body = defaults_getter.body.analyse_expressions( defaults_getter.body = defaults_getter.body.analyse_expressions(
defaults_getter.local_scope) defaults_getter.local_scope)
defaults_getter.py_wrapper_required = False defaults_getter.py_wrapper_required = False
...@@ -8891,7 +8903,7 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin): ...@@ -8891,7 +8903,7 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
elif def_node.is_classmethod: elif def_node.is_classmethod:
flags.append('__Pyx_CYFUNCTION_CLASSMETHOD') flags.append('__Pyx_CYFUNCTION_CLASSMETHOD')
if def_node.local_scope.parent_scope.is_c_class_scope: if def_node.local_scope.parent_scope.is_c_class_scope and not def_node.entry.is_anonymous:
flags.append('__Pyx_CYFUNCTION_CCLASS') flags.append('__Pyx_CYFUNCTION_CCLASS')
if flags: if flags:
......
...@@ -1975,12 +1975,13 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1975,12 +1975,13 @@ 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:
return return
binding = env.directives['binding']
code.putln("") code.putln("")
code.putln( code.putln(
"static PyMethodDef %s[] = {" % ( "static PyMethodDef %s[] = {" % (
env.method_table_cname)) env.method_table_cname))
for entry in env.pyfunc_entries: for entry in env.pyfunc_entries:
if not entry.fused_cfunction: if not entry.fused_cfunction and not (binding and entry.is_overridable):
code.put_pymethoddef(entry, ",") code.put_pymethoddef(entry, ",")
code.putln( code.putln(
"{0, 0, 0, 0}") "{0, 0, 0, 0}")
......
...@@ -197,6 +197,7 @@ class Node(object): ...@@ -197,6 +197,7 @@ class Node(object):
is_nonecheck = 0 is_nonecheck = 0
is_literal = 0 is_literal = 0
is_terminator = 0 is_terminator = 0
is_wrapper = False # is a DefNode wrapper for a C function
temps = None temps = None
# All descendants should set child_attrs to a list of the attributes # All descendants should set child_attrs to a list of the attributes
...@@ -1879,7 +1880,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1879,7 +1880,7 @@ class FuncDefNode(StatNode, BlockNode):
if profile or linetrace: if profile or linetrace:
# this looks a bit late, but if we don't get here due to a # this looks a bit late, but if we don't get here due to a
# fatal error before hand, it's not really worth tracing # fatal error before hand, it's not really worth tracing
if isinstance(self, DefNode) and self.is_wrapper: if self.is_wrapper:
trace_name = self.entry.name + " (wrapper)" trace_name = self.entry.name + " (wrapper)"
else: else:
trace_name = self.entry.name trace_name = self.entry.name
...@@ -2243,6 +2244,11 @@ class CFuncDefNode(FuncDefNode): ...@@ -2243,6 +2244,11 @@ class CFuncDefNode(FuncDefNode):
def unqualified_name(self): def unqualified_name(self):
return self.entry.name return self.entry.name
@property
def code_object(self):
# share the CodeObject with the cpdef wrapper (if available)
return self.py_func.code_object if self.py_func else None
def analyse_declarations(self, env): def analyse_declarations(self, env):
self.is_c_class_method = env.is_c_class_scope self.is_c_class_method = env.is_c_class_scope
if self.directive_locals is None: if self.directive_locals is None:
...@@ -2360,6 +2366,7 @@ class CFuncDefNode(FuncDefNode): ...@@ -2360,6 +2366,7 @@ class CFuncDefNode(FuncDefNode):
is_wrapper=1) is_wrapper=1)
self.py_func.is_module_scope = env.is_module_scope self.py_func.is_module_scope = env.is_module_scope
self.py_func.analyse_declarations(env) self.py_func.analyse_declarations(env)
self.py_func.entry.is_overridable = True
self.py_func_stat = StatListNode(self.pos, stats=[self.py_func]) self.py_func_stat = StatListNode(self.pos, stats=[self.py_func])
self.py_func.type = PyrexTypes.py_object_type self.py_func.type = PyrexTypes.py_object_type
self.entry.as_variable = self.py_func.entry self.entry.as_variable = self.py_func.entry
...@@ -2436,7 +2443,10 @@ class CFuncDefNode(FuncDefNode): ...@@ -2436,7 +2443,10 @@ class CFuncDefNode(FuncDefNode):
def analyse_expressions(self, env): def analyse_expressions(self, env):
self.local_scope.directives = env.directives self.local_scope.directives = env.directives
if self.py_func is not None: if self.py_func_stat is not None:
# this will also analyse the default values and the function name assignment
self.py_func_stat = self.py_func_stat.analyse_expressions(env)
elif self.py_func is not None:
# this will also analyse the default values # this will also analyse the default values
self.py_func = self.py_func.analyse_expressions(env) self.py_func = self.py_func.analyse_expressions(env)
else: else:
...@@ -3022,16 +3032,17 @@ class DefNode(FuncDefNode): ...@@ -3022,16 +3032,17 @@ class DefNode(FuncDefNode):
def needs_assignment_synthesis(self, env, code=None): def needs_assignment_synthesis(self, env, code=None):
if self.is_staticmethod: if self.is_staticmethod:
return True return True
if self.is_wrapper or self.specialized_cpdefs or self.entry.is_fused_specialized: if self.specialized_cpdefs or self.entry.is_fused_specialized:
return False return False
if self.no_assignment_synthesis: if self.no_assignment_synthesis:
return False return False
# Should enable for module level as well, that will require more testing... if self.entry.is_special:
return False
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 self.local_scope.directives['binding']
else: else:
return code.globalstate.directives['binding'] return code.globalstate.directives['binding']
return env.is_py_class_scope or env.is_closure_scope return env.is_py_class_scope or env.is_closure_scope
...@@ -3044,7 +3055,8 @@ class DefNode(FuncDefNode): ...@@ -3044,7 +3055,8 @@ class DefNode(FuncDefNode):
def generate_function_definitions(self, env, code): def generate_function_definitions(self, env, code):
if self.defaults_getter: if self.defaults_getter:
self.defaults_getter.generate_function_definitions(env, code) # defaults getter must never live in class scopes, it's always a module function
self.defaults_getter.generate_function_definitions(env.global_scope(), code)
# Before closure cnames are mangled # Before closure cnames are mangled
if self.py_wrapper_required: if self.py_wrapper_required:
......
...@@ -1697,6 +1697,8 @@ if VALUE is not None: ...@@ -1697,6 +1697,8 @@ if VALUE is not None:
def visit_DefNode(self, node): def visit_DefNode(self, node):
node = self.visit_FuncDefNode(node) node = self.visit_FuncDefNode(node)
env = self.current_env() env = self.current_env()
if isinstance(node, Nodes.DefNode) and node.is_wrapper:
env = env.parent_scope
if (not isinstance(node, Nodes.DefNode) or if (not isinstance(node, Nodes.DefNode) or
node.fused_py_func or node.is_generator_body or node.fused_py_func or node.is_generator_body or
not node.needs_assignment_synthesis(env)): not node.needs_assignment_synthesis(env)):
...@@ -1949,13 +1951,28 @@ class CalculateQualifiedNamesTransform(EnvTransform): ...@@ -1949,13 +1951,28 @@ class CalculateQualifiedNamesTransform(EnvTransform):
return node return node
def visit_PyCFunctionNode(self, node): def visit_PyCFunctionNode(self, node):
orig_qualified_name = self.qualified_name[:]
if node.def_node.is_wrapper and self.qualified_name and self.qualified_name[-1] == '<locals>':
self.qualified_name.pop()
self._set_qualname(node)
else:
self._set_qualname(node, node.def_node.name) self._set_qualname(node, node.def_node.name)
self.visitchildren(node) self.visitchildren(node)
self.qualified_name = orig_qualified_name
return node return node
def visit_DefNode(self, node): def visit_DefNode(self, node):
if node.is_wrapper and self.qualified_name:
assert self.qualified_name[-1] == '<locals>', self.qualified_name
orig_qualified_name = self.qualified_name[:]
self.qualified_name.pop()
self._set_qualname(node)
self._super_visit_FuncDefNode(node)
self.qualified_name = orig_qualified_name
else:
self._set_qualname(node, node.name) self._set_qualname(node, node.name)
return self.visit_FuncDefNode(node) self.visit_FuncDefNode(node)
return node
def visit_FuncDefNode(self, node): def visit_FuncDefNode(self, node):
orig_qualified_name = self.qualified_name[:] orig_qualified_name = self.qualified_name[:]
......
...@@ -99,6 +99,12 @@ class Signature(object): ...@@ -99,6 +99,12 @@ class Signature(object):
self.exception_check = ret_format != 'r' and self.error_value is not None self.exception_check = ret_format != 'r' and self.error_value is not None
self.is_staticmethod = False self.is_staticmethod = False
def __repr__(self):
return '<Signature[%s(%s%s)]>' % (
self.ret_format,
', '.join(self.fixed_arg_format),
'*' if self.has_generic_args else '')
def num_fixed_args(self): def num_fixed_args(self):
return len(self.fixed_arg_format) return len(self.fixed_arg_format)
......
...@@ -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,
......
...@@ -1273,7 +1273,7 @@ static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name ...@@ -1273,7 +1273,7 @@ static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name
#if CYTHON_FAST_PYCALL #if CYTHON_FAST_PYCALL
if (PyFunction_Check(function)) { if (PyFunction_Check(function)) {
PyObject *args[2] = {self, arg}; PyObject *args[2] = {self, arg};
result = __Pyx_PyFunction_FastCall(function, args, 2, NULL); result = __Pyx_PyFunction_FastCall(function, args, 2);
goto done; goto done;
} }
#endif #endif
...@@ -1321,7 +1321,7 @@ static PyObject* __Pyx_PyObject_CallMethod2(PyObject* obj, PyObject* method_name ...@@ -1321,7 +1321,7 @@ static PyObject* __Pyx_PyObject_CallMethod2(PyObject* obj, PyObject* method_name
#if CYTHON_FAST_PYCALL #if CYTHON_FAST_PYCALL
if (PyFunction_Check(function)) { if (PyFunction_Check(function)) {
PyObject *args[3] = {self, arg1, arg2}; PyObject *args[3] = {self, arg1, arg2};
result = __Pyx_PyFunction_FastCall(function, args, 3, NULL); result = __Pyx_PyFunction_FastCall(function, args, 3);
goto done; goto done;
} }
#endif #endif
...@@ -1341,7 +1341,7 @@ static PyObject* __Pyx_PyObject_CallMethod2(PyObject* obj, PyObject* method_name ...@@ -1341,7 +1341,7 @@ static PyObject* __Pyx_PyObject_CallMethod2(PyObject* obj, PyObject* method_name
#if CYTHON_FAST_PYCALL #if CYTHON_FAST_PYCALL
if (PyFunction_Check(method)) { if (PyFunction_Check(method)) {
PyObject *args[2] = {arg1, arg2}; PyObject *args[2] = {arg1, arg2};
result = __Pyx_PyFunction_FastCall(method, args, 2, NULL); result = __Pyx_PyFunction_FastCall(method, args, 2);
goto done; goto done;
} else } else
#endif #endif
...@@ -1433,11 +1433,14 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_CallMethO(PyObject *func, PyObject ...@@ -1433,11 +1433,14 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_CallMethO(PyObject *func, PyObject
/////////////// PyFunctionFastCall.proto /////////////// /////////////// PyFunctionFastCall.proto ///////////////
#if CYTHON_FAST_PYCALL #if CYTHON_FAST_PYCALL
#define __Pyx_PyFunction_FastCall(func, args, nargs) \
__Pyx_PyFunction_FastCallDict((func), (args), (nargs), NULL)
// let's assume that the non-public C-API function might still change during the 3.6 beta phase // let's assume that the non-public C-API function might still change during the 3.6 beta phase
#if 1 || PY_VERSION_HEX < 0x030600B1 #if 1 || PY_VERSION_HEX < 0x030600B1
static PyObject *__Pyx_PyFunction_FastCall(PyObject *func, PyObject **args, int nargs, PyObject *kwargs); static PyObject *__Pyx_PyFunction_FastCallDict(PyObject *func, PyObject **args, int nargs, PyObject *kwargs);
#else #else
#define __Pyx_PyFunction_FastCall(func, args, nargs, kwargs) _PyFunction_FastCall(func, args, nargs, kwargs) #define __Pyx_PyFunction_FastCallDict(func, args, nargs, kwargs) _PyFunction_FastCallDict(func, args, nargs, kwargs)
#endif #endif
#endif #endif
...@@ -1447,8 +1450,8 @@ static PyObject *__Pyx_PyFunction_FastCall(PyObject *func, PyObject **args, int ...@@ -1447,8 +1450,8 @@ static PyObject *__Pyx_PyFunction_FastCall(PyObject *func, PyObject **args, int
#if CYTHON_FAST_PYCALL #if CYTHON_FAST_PYCALL
#include "frameobject.h" #include "frameobject.h"
static PyObject* __Pyx_PyFunction_FastCallNoKw(PyObject **args, Py_ssize_t na, static PyObject* __Pyx_PyFunction_FastCallNoKw(PyCodeObject *co, PyObject **args, Py_ssize_t na,
PyCodeObject *co, PyObject *globals) { PyObject *globals) {
PyFrameObject *f; PyFrameObject *f;
PyThreadState *tstate = PyThreadState_GET(); PyThreadState *tstate = PyThreadState_GET();
PyObject **fastlocals; PyObject **fastlocals;
...@@ -1483,7 +1486,7 @@ static PyObject* __Pyx_PyFunction_FastCallNoKw(PyObject **args, Py_ssize_t na, ...@@ -1483,7 +1486,7 @@ static PyObject* __Pyx_PyFunction_FastCallNoKw(PyObject **args, Py_ssize_t na,
#if 1 || PY_VERSION_HEX < 0x030600B1 #if 1 || PY_VERSION_HEX < 0x030600B1
static PyObject *__Pyx_PyFunction_FastCall(PyObject *func, PyObject **args, int nargs, CYTHON_UNUSED PyObject *kwargs) { static PyObject *__Pyx_PyFunction_FastCallDict(PyObject *func, PyObject **args, int nargs, PyObject *kwargs) {
PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
PyObject *globals = PyFunction_GET_GLOBALS(func); PyObject *globals = PyFunction_GET_GLOBALS(func);
PyObject *argdefs = PyFunction_GET_DEFAULTS(func); PyObject *argdefs = PyFunction_GET_DEFAULTS(func);
...@@ -1494,18 +1497,60 @@ static PyObject *__Pyx_PyFunction_FastCall(PyObject *func, PyObject **args, int ...@@ -1494,18 +1497,60 @@ static PyObject *__Pyx_PyFunction_FastCall(PyObject *func, PyObject **args, int
//PyObject *name, *qualname; //PyObject *name, *qualname;
//#endif //#endif
#endif #endif
PyObject *kwtuple, **k;
PyObject **d; PyObject **d;
int nd; int nd;
Py_ssize_t nk;
PyObject *result;
assert(kwargs == NULL || PyDict_Check(kwargs));
nk = kwargs ? PyDict_Size(kwargs) : 0;
/* CPython issue #27128: support for keywords will come later */ if (Py_EnterRecursiveCall(" while calling a Python object")) {
assert(kwargs == NULL); return NULL;
}
if (argdefs == NULL && co->co_argcount == nargs && if (
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
co->co_kwonlyargcount == 0 && co->co_kwonlyargcount == 0 &&
#endif #endif
likely(kwargs == NULL || nk == 0) &&
co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) {
return __Pyx_PyFunction_FastCallNoKw(args, nargs, co, globals); /* Fast paths */
if (argdefs == NULL && co->co_argcount == nargs) {
result = __Pyx_PyFunction_FastCallNoKw(co, args, nargs, globals);
goto done;
}
else if (nargs == 0 && argdefs != NULL
&& co->co_argcount == Py_SIZE(argdefs)) {
/* function called with no arguments, but all parameters have
a default value: use default values as arguments .*/
args = &PyTuple_GET_ITEM(argdefs, 0);
result =__Pyx_PyFunction_FastCallNoKw(co, args, Py_SIZE(argdefs), globals);
goto done;
}
}
if (kwargs != NULL) {
Py_ssize_t pos, i;
kwtuple = PyTuple_New(2 * nk);
if (kwtuple == NULL) {
result = NULL;
goto done;
}
k = &PyTuple_GET_ITEM(kwtuple, 0);
pos = i = 0;
while (PyDict_Next(kwargs, &pos, &k[i], &k[i+1])) {
Py_INCREF(k[i]);
Py_INCREF(k[i+1]);
i += 2;
}
nk = i / 2;
}
else {
kwtuple = NULL;
k = NULL;
} }
closure = PyFunction_GET_CLOSURE(func); closure = PyFunction_GET_CLOSURE(func);
...@@ -1534,16 +1579,21 @@ static PyObject *__Pyx_PyFunction_FastCall(PyObject *func, PyObject **args, int ...@@ -1534,16 +1579,21 @@ static PyObject *__Pyx_PyFunction_FastCall(PyObject *func, PyObject **args, int
// closure, name, qualname); // closure, name, qualname);
//#elif PY_MAJOR_VERSION >= 3 //#elif PY_MAJOR_VERSION >= 3
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
return PyEval_EvalCodeEx((PyObject*)co, globals, (PyObject *)NULL, result = PyEval_EvalCodeEx((PyObject*)co, globals, (PyObject *)NULL,
args, nargs, args, nargs,
NULL, 0, k, (int)nk,
d, nd, kwdefs, closure); d, nd, kwdefs, closure);
#else #else
return PyEval_EvalCodeEx(co, globals, (PyObject *)NULL, result = PyEval_EvalCodeEx(co, globals, (PyObject *)NULL,
args, nargs, args, nargs,
NULL, 0, k, (int)nk,
d, nd, closure); d, nd, closure);
#endif #endif
Py_XDECREF(kwtuple);
done:
Py_LeaveRecursiveCall();
return result;
} }
#endif // CPython < 3.6 #endif // CPython < 3.6
#endif // CYTHON_FAST_PYCALL #endif // CYTHON_FAST_PYCALL
...@@ -1573,7 +1623,7 @@ static PyObject* __Pyx__PyObject_CallOneArg(PyObject *func, PyObject *arg) { ...@@ -1573,7 +1623,7 @@ static PyObject* __Pyx__PyObject_CallOneArg(PyObject *func, PyObject *arg) {
static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg) { static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg) {
#if CYTHON_FAST_PYCALL #if CYTHON_FAST_PYCALL
if (PyFunction_Check(func)) { if (PyFunction_Check(func)) {
return __Pyx_PyFunction_FastCall(func, &arg, 1, NULL); return __Pyx_PyFunction_FastCall(func, &arg, 1);
} }
#endif #endif
#ifdef __Pyx_CyFunction_USED #ifdef __Pyx_CyFunction_USED
...@@ -1620,7 +1670,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func); /*proto ...@@ -1620,7 +1670,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func); /*proto
static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func) { static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func) {
#if CYTHON_FAST_PYCALL #if CYTHON_FAST_PYCALL
if (PyFunction_Check(func)) { if (PyFunction_Check(func)) {
return __Pyx_PyFunction_FastCall(func, NULL, 0, NULL); return __Pyx_PyFunction_FastCall(func, NULL, 0);
} }
#endif #endif
#ifdef __Pyx_CyFunction_USED #ifdef __Pyx_CyFunction_USED
...@@ -1667,7 +1717,7 @@ static PyObject* __Pyx_PyObject_CallMatrixMethod(PyObject* method, PyObject* arg ...@@ -1667,7 +1717,7 @@ static PyObject* __Pyx_PyObject_CallMatrixMethod(PyObject* method, PyObject* arg
#if CYTHON_FAST_PYCALL #if CYTHON_FAST_PYCALL
if (PyFunction_Check(function)) { if (PyFunction_Check(function)) {
PyObject *args[2] = {self, arg}; PyObject *args[2] = {self, arg};
result = __Pyx_PyFunction_FastCall(function, args, 2, NULL); result = __Pyx_PyFunction_FastCall(function, args, 2);
goto done; goto done;
} }
#endif #endif
......
# mode: run
# tag: cyfunction
# cython: binding=True
cimport cython
class PyClass(object):
a = 2
class PyClass99(object):
a = 99
def pymethod(self, x, y=1, z=PyClass):
"""
>>> obj = PyClass99()
>>> obj.pymethod(0)
(0, 1, 2)
"""
return x, y, z.a
cdef class CyClass:
cpdef cpmethod(self, x, y=1, z=PyClass):
"""
>>> obj = CyClass()
>>> obj.cpmethod(0)
(0, 1, 2)
>>> obj.cpmethod(0, 3)
(0, 3, 2)
>>> obj.cpmethod(0, 3, PyClass)
(0, 3, 2)
>>> obj.cpmethod(0, 3, 5)
Traceback (most recent call last):
AttributeError: 'int' object has no attribute 'a'
"""
return x, y, z.a
y_value = 3
p_class = PyClass
cpdef cpmethod2(self, x, y=y_value, z=p_class):
"""
>>> obj = CyClass()
>>> obj.cpmethod2(0)
(0, 3, 2)
"""
return x, y, z.a
def pymethod(self, x, y=y_value, z=p_class):
"""
>>> obj = CyClass()
>>> obj.pymethod(0)
(0, 3, 2)
"""
return x, y, z.a
# change values to check that defaults above stay unmodified
y_value = 98
p_class = PyClass99
cpdef func(x, y=1, z=PyClass):
"""
>>> func(0)
(0, 1, 2)
>>> func(0, 3)
(0, 3, 2)
>>> func(0, 3, PyClass)
(0, 3, 2)
>>> func(0, 3, 5)
Traceback (most recent call last):
AttributeError: 'int' object has no attribute 'a'
"""
return x, y, z.a
@cython.ccall
def pyfunc(x, y=1, z=PyClass):
"""
>>> pyfunc(0)
(0, 1, 2)
>>> pyfunc(0, 3)
(0, 3, 2)
>>> pyfunc(0, 3, PyClass)
(0, 3, 2)
>>> pyfunc(0, 3, 5)
Traceback (most recent call last):
AttributeError: 'int' object has no attribute 'a'
"""
return x, y, z.a
cpdef func(x, y=*, z=*)
# mode: run
# tag: cyfunction
# cython: binding=True
import cython
class PyClass(object):
a = 2
class PyClass99(object):
a = 99
def pymethod(self, x, y=1, z=PyClass):
"""
>>> obj = PyClass99()
>>> obj.pymethod(0)
(0, 1, 2)
"""
return x, y, z.a
def func(x, y=1, z=PyClass):
"""
>>> func(0)
(0, 1, 2)
>>> func(0, 3)
(0, 3, 2)
>>> func(0, 3, PyClass)
(0, 3, 2)
>>> func(0, 3, 5)
Traceback (most recent call last):
AttributeError: 'int' object has no attribute 'a'
"""
return x, y, z.a
@cython.ccall
def pyfunc(x, y=1, z=PyClass):
"""
>>> pyfunc(0)
(0, 1, 2)
>>> pyfunc(0, 3)
(0, 3, 2)
>>> pyfunc(0, 3, PyClass)
(0, 3, 2)
>>> pyfunc(0, 3, 5)
Traceback (most recent call last):
AttributeError: 'int' object has no attribute 'a'
"""
return x, y, z.a
...@@ -7,10 +7,6 @@ from distutils.extension import Extension ...@@ -7,10 +7,6 @@ from distutils.extension import Extension
from distutils.core import setup from distutils.core import setup
from Cython.Build import cythonize from Cython.Build import cythonize
from Cython.Compiler.Options import _directive_defaults
_directive_defaults['linetrace'] = True
_directive_defaults['binding'] = True
extensions = [ extensions = [
Extension("collatz", ["collatz.pyx"], define_macros=[('CYTHON_TRACE', '1')]) Extension("collatz", ["collatz.pyx"], define_macros=[('CYTHON_TRACE', '1')])
] ]
...@@ -21,8 +17,6 @@ setup( ...@@ -21,8 +17,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,24 +24,98 @@ except ImportError: ...@@ -30,24 +24,98 @@ 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 cp_collatz
func = cp_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
profile = line_profiler.LineProfiler(func)
profile.runcall(func)
assert_stats(profile, func.__name__)
func = obj.cp_pymethod
profile = line_profiler.LineProfiler(func)
profile.runcall(func, 19)
assert_stats(profile, func.__name__)
######## collatz.pyx ########### ######## collatz.pyx ###########
# cython: linetrace=True
cimport cython
@cython.binding(True)
def collatz(n): def collatz(n):
while n > 1: while n > 1:
if n % 2 == 0: if n % 2 == 0:
n //= 2 n //= 2
else: else:
n = 3*n+1 n = 3*n+1
@cython.binding(True)
cpdef cp_collatz(n):
while n > 1:
if n % 2 == 0:
n //= 2
else:
n = 3*n+1
@cython.binding(True)
class PyClass(object):
def py_pymethod(self):
x = 1
for i in range(10):
a = x + 2
return a * 3
@cython.binding(True)
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
cpdef cp_pymethod(self, r):
for i in range(10):
a = r + 1
z = self.c_pymethod(a) + self.cmethod(r)
return z * 2
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