Commit 640f13ec authored by Stefan Behnel's avatar Stefan Behnel

make cfunc wrapper function support kwargs if the defining module wants it and...

make cfunc wrapper function support kwargs if the defining module wants it and give it a docstring that shows the argument names
parent 4bdb3cf4
...@@ -2661,22 +2661,25 @@ class CFuncType(CType): ...@@ -2661,22 +2661,25 @@ class CFuncType(CType):
return False return False
if arg.type.is_extension_type or arg.type.is_builtin_type: if arg.type.is_extension_type or arg.type.is_builtin_type:
env.use_utility_code(UtilityCode.load_cached("ExtTypeTest", "ObjectHandling.c")) env.use_utility_code(UtilityCode.load_cached("ExtTypeTest", "ObjectHandling.c"))
class Arg:
def __init__(self, ix, type): class Arg(object):
def __init__(self, ix, arg):
self.ix = ix self.ix = ix
self.name = 'ARG%s' % ix self.name = arg.name or 'ARG%s' % ix
self.type = type self.type = arg.type
self.type_name = 'TYPE%s' % ix self.type_name = 'TYPE%s' % ix
self.type_cname = type.declaration_code("") self.type_cname = self.type.declaration_code("")
if self.type.is_extension_type or self.type.is_builtin_type: if self.type.is_extension_type or self.type.is_builtin_type:
self.type_convert = '<%s>' % self.type_name self.type_convert = '<%s>' % self.type_name
elif type.is_pyobject: elif self.type.is_pyobject:
self.type_convert = '' self.type_convert = ''
else: else:
self.type_convert = '%s_to_py' % self.type_name self.type_convert = '%s_to_py' % self.type_name
def declare_type_def(self): def declare_type_def(self):
if self.type.is_extension_type or self.type.is_builtin_type or not self.type.is_pyobject: if self.type.is_extension_type or self.type.is_builtin_type or not self.type.is_pyobject:
return 'ctypedef void* %s "%s"' % (self.type_name, self.type_cname) return 'ctypedef void* %s "%s"' % (self.type_name, self.type_cname)
def declare_type_convert(self): def declare_type_convert(self):
if self.type.is_extension_type or self.type.is_builtin_type: if self.type.is_extension_type or self.type.is_builtin_type:
return 'cdef PyTypeObject* %s_TYPE "%s"' % (self.type_name, self.type.typeptr_cname) return 'cdef PyTypeObject* %s_TYPE "%s"' % (self.type_name, self.type.typeptr_cname)
...@@ -2684,6 +2687,7 @@ class CFuncType(CType): ...@@ -2684,6 +2687,7 @@ class CFuncType(CType):
return '' return ''
else: else:
return 'cdef %s %s "%s"(object) except *' % (self.type_name, self.type_convert, self.type.from_py_function) return 'cdef %s %s "%s"(object) except *' % (self.type_name, self.type_convert, self.type.from_py_function)
def check_type(self): def check_type(self):
if self.type.is_extension_type or self.type.is_builtin_type: if self.type.is_extension_type or self.type.is_builtin_type:
return '__Pyx_TypeTest(<PyObject*>%s, %s_TYPE)' % (self.name, self.type_name) return '__Pyx_TypeTest(<PyObject*>%s, %s_TYPE)' % (self.name, self.type_name)
...@@ -2710,14 +2714,16 @@ class CFuncType(CType): ...@@ -2710,14 +2714,16 @@ class CFuncType(CType):
context = { context = {
'cname': self.to_py_function, 'cname': self.to_py_function,
'args': [Arg(ix, arg.type) for ix, arg in enumerate(self.args)], 'args': [Arg(ix, arg) for ix, arg in enumerate(self.args)],
'return_type': return_type, 'return_type': return_type,
'declare_return_type': declare_return_type, 'declare_return_type': declare_return_type,
'declare_return_type_convert': declare_return_type_convert, 'declare_return_type_convert': declare_return_type_convert,
'maybe_return': maybe_return, 'maybe_return': maybe_return,
'except_clause': except_clause, 'except_clause': except_clause,
} }
env.use_utility_code(CythonUtilityCode.load("cfunc.to_py", "CFuncConvert.pyx", context=context)) env.use_utility_code(CythonUtilityCode.load(
"cfunc.to_py", "CFuncConvert.pyx",
context=context, compiler_directives=dict(env.directives)))
return True return True
......
...@@ -23,9 +23,12 @@ from . import UtilNodes ...@@ -23,9 +23,12 @@ from . import UtilNodes
class StringParseContext(Main.Context): class StringParseContext(Main.Context):
def __init__(self, name, include_directories=None): def __init__(self, name, include_directories=None, compiler_directives=None):
if include_directories is None: include_directories = [] if include_directories is None:
Main.Context.__init__(self, include_directories, {}, include_directories = []
if compiler_directives is None:
compiler_directives = {}
Main.Context.__init__(self, include_directories, compiler_directives,
create_testscope=False) create_testscope=False)
self.module_name = name self.module_name = name
......
...@@ -71,7 +71,7 @@ class CythonUtilityCode(Code.UtilityCodeBase): ...@@ -71,7 +71,7 @@ class CythonUtilityCode(Code.UtilityCodeBase):
is_cython_utility = True is_cython_utility = True
def __init__(self, impl, name="__pyxutil", prefix="", requires=None, def __init__(self, impl, name="__pyxutil", prefix="", requires=None,
file=None, from_scope=None, context=None): file=None, from_scope=None, context=None, compiler_directives=None):
# 1) We need to delay the parsing/processing, so that all modules can be # 1) We need to delay the parsing/processing, so that all modules can be
# imported without import loops # imported without import loops
# 2) The same utility code object can be used for multiple source files; # 2) The same utility code object can be used for multiple source files;
...@@ -86,6 +86,7 @@ class CythonUtilityCode(Code.UtilityCodeBase): ...@@ -86,6 +86,7 @@ class CythonUtilityCode(Code.UtilityCodeBase):
self.prefix = prefix self.prefix = prefix
self.requires = requires or [] self.requires = requires or []
self.from_scope = from_scope self.from_scope = from_scope
self.compiler_directives = compiler_directives
def get_tree(self, entries_only=False, cython_scope=None): def get_tree(self, entries_only=False, cython_scope=None):
from .AnalysedTreeTransforms import AutoTestDictTransform from .AnalysedTreeTransforms import AutoTestDictTransform
...@@ -95,7 +96,8 @@ class CythonUtilityCode(Code.UtilityCodeBase): ...@@ -95,7 +96,8 @@ class CythonUtilityCode(Code.UtilityCodeBase):
excludes = [AutoTestDictTransform] excludes = [AutoTestDictTransform]
from . import Pipeline, ParseTreeTransforms from . import Pipeline, ParseTreeTransforms
context = CythonUtilityCodeContext(self.name) context = CythonUtilityCodeContext(
self.name, compiler_directives=self.compiler_directives)
context.prefix = self.prefix context.prefix = self.prefix
context.cython_scope = cython_scope context.cython_scope = cython_scope
#context = StringParseContext(self.name) #context = StringParseContext(self.name)
......
...@@ -12,10 +12,11 @@ cdef extern from *: ...@@ -12,10 +12,11 @@ cdef extern from *:
{{declare_return_type_convert}} {{declare_return_type_convert}}
@cname("{{cname}}") @cname("{{cname}}")
cdef object {{cname}}({{return_type}} (*f)({{', '.join(arg.type_name for arg in args)}}) {{except_clause}}): cdef object {{cname}}({{return_type}} (*f)({{ ', '.join(arg.type_name for arg in args) }}) {{except_clause}}):
def wrap({{', '.join(arg.name for arg in args)}}): def wrap({{ ', '.join(arg.name for arg in args) }}):
"""wrap({{', '.join(arg.name for arg in args)}})"""
{{for arg in args}} {{for arg in args}}
{{arg.check_type()}} {{arg.check_type()}}
{{endfor}} {{endfor}}
{{maybe_return}} (f({{', '.join('%s(%s)' % (arg.type_convert, arg.name) for arg in args)}})) {{maybe_return}}(f({{ ', '.join('%s(%s)' % (arg.type_convert, arg.name) for arg in args) }}))
return wrap return wrap
# mode: run
# cython: always_allow_keywords=True
from libc.math cimport sqrt from libc.math cimport sqrt
cdef void empty_cfunc(): cdef void empty_cfunc():
...@@ -26,11 +30,26 @@ def call_square_c(x): ...@@ -26,11 +30,26 @@ def call_square_c(x):
return py_func(x) return py_func(x)
def return_square_c():
"""
>>> square_c = return_square_c()
>>> square_c(5)
25.0
>>> square_c(x=4)
16.0
>>> square_c.__doc__ # FIXME: try to make original C function name available
'wrap(x)'
"""
return square_c
def return_libc_sqrt(): def return_libc_sqrt():
""" """
>>> sqrt = return_libc_sqrt() >>> sqrt = return_libc_sqrt()
>>> sqrt(9) >>> sqrt(9)
3.0 3.0
>>> sqrt(x=9)
3.0
""" """
return sqrt return sqrt
......
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