Commit 0daf7250 authored by Mark Florisson's avatar Mark Florisson

Support cname() decorators in CythonUtilityCode

parent 7a0f63b2
...@@ -63,6 +63,9 @@ class CythonScope(ModuleScope): ...@@ -63,6 +63,9 @@ class CythonScope(ModuleScope):
defining = 1, defining = 1,
cname = 'PyObject_TypeCheck') cname = 'PyObject_TypeCheck')
self.test_cythonscope()
def test_cythonscope(self):
# A special function just to make it easy to test the scope and # A special function just to make it easy to test the scope and
# utility code functionality in isolation. It is available to # utility code functionality in isolation. It is available to
# "end-users" but nobody will know it is there anyway... # "end-users" but nobody will know it is there anyway...
...@@ -71,7 +74,7 @@ class CythonScope(ModuleScope): ...@@ -71,7 +74,7 @@ class CythonScope(ModuleScope):
CFuncType(py_object_type, [CFuncTypeArg("value", c_int_type, None)]), CFuncType(py_object_type, [CFuncTypeArg("value", c_int_type, None)]),
pos=None, pos=None,
defining=1, defining=1,
cname='__pyx_cython__testscope' cname='__pyx_testscope'
) )
entry.utility_code_definition = cython_testscope_utility_code entry.utility_code_definition = cython_testscope_utility_code
...@@ -87,7 +90,7 @@ class CythonScope(ModuleScope): ...@@ -87,7 +90,7 @@ class CythonScope(ModuleScope):
CFuncType(py_object_type, [CFuncTypeArg("value", c_int_type, None)]), CFuncType(py_object_type, [CFuncTypeArg("value", c_int_type, None)]),
pos=None, pos=None,
defining=1, defining=1,
cname='__pyx_cython_view__testscope' cname='__pyx_view_testscope'
) )
entry.utility_code_definition = cythonview_testscope_utility_code entry.utility_code_definition = cythonview_testscope_utility_code
...@@ -98,11 +101,13 @@ def create_cython_scope(context): ...@@ -98,11 +101,13 @@ def create_cython_scope(context):
return CythonScope() return CythonScope()
cython_testscope_utility_code = CythonUtilityCode(u""" cython_testscope_utility_code = CythonUtilityCode(u"""
@cname('__pyx_testscope')
cdef object _testscope(int value): cdef object _testscope(int value):
return "hello from cython scope, value=%d" % value return "hello from cython scope, value=%d" % value
""", name="cython utility code", prefix="__pyx_cython_") """)# #, name="cython utility code", prefix="__pyx_cython_")
cythonview_testscope_utility_code = CythonUtilityCode(u""" cythonview_testscope_utility_code = CythonUtilityCode(u"""
@cname('__pyx_view_testscope')
cdef object _testscope(int value): cdef object _testscope(int value):
return "hello from cython.view scope, value=%d" % value return "hello from cython.view scope, value=%d" % value
""", name="cython utility code", prefix="__pyx_cython_view_") """) #, name="cython utility code", prefix="__pyx_cython_view_")
...@@ -6720,6 +6720,26 @@ class ParallelRangeNode(ParallelStatNode): ...@@ -6720,6 +6720,26 @@ class ParallelRangeNode(ParallelStatNode):
code.end_block() # pragma omp parallel end block code.end_block() # pragma omp parallel end block
class CnameDecoratorNode(StatNode):
"""
This node is for the cname decorator in CythonUtilityCode:
@cname('the_cname')
cdef func(...):
...
node the node to which the cname decorator is applied
cname the cname the node should get
"""
child_attrs = ['node']
def analyse_declarations(self, env):
self.node.analyse_declarations(env)
self.node.entry.cname = self.cname
self.node.entry.func_cname = self.cname
#------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------
# #
# Runtime support code # Runtime support code
......
...@@ -1241,7 +1241,50 @@ class DecoratorTransform(CythonTransform, SkipDeclarations): ...@@ -1241,7 +1241,50 @@ class DecoratorTransform(CythonTransform, SkipDeclarations):
rhs = decorator_result) rhs = decorator_result)
return [node, reassignment] return [node, reassignment]
class CnameDirectivesTransform(CythonTransform, SkipDeclarations):
"""
Only part of the CythonUtilityCode pipeline. Must be run before
DecoratorTransform in case this is a decorator for a cdef class.
It filters out @cname('my_cname') decorators and rewrites them to
CnameDecoratorNodes.
"""
def handle_function(self, node):
for i, decorator in enumerate(node.decorators):
decorator = decorator.decorator
if (isinstance(decorator, ExprNodes.CallNode) and
decorator.function.is_name and
decorator.function.name == 'cname'):
args, kwargs = decorator.explicit_args_kwds()
if kwargs:
raise AssertionError(
"cname decorator does not take keyword arguments")
if len(args) != 1:
raise AssertionError(
"cname decorator takes exactly one argument")
if not (args[0].is_literal and
args[0].type == Builtin.str_type):
raise AssertionError(
"argument to cname decorator must be a string literal")
cname = args[0].compile_time_value(None)
del node.decorators[i]
node = Nodes.CnameDecoratorNode(pos=node.pos, node=node,
cname=cname)
break
self.visitchildren(node)
return node
visit_CFuncDefNode = handle_function
# visit_FuncDefNode = handle_function
# visit_ClassDefNode = handle_function
class ForwardDeclareTypes(CythonTransform): class ForwardDeclareTypes(CythonTransform):
def visit_CompilerDirectivesNode(self, node): def visit_CompilerDirectivesNode(self, node):
...@@ -1527,6 +1570,10 @@ if VALUE is not None: ...@@ -1527,6 +1570,10 @@ if VALUE is not None:
self.visitchildren(node) self.visitchildren(node)
return None return None
def visit_CnameDecoratorNode(self, node):
self.visitchildren(node)
return node.node
def create_Property(self, entry): def create_Property(self, entry):
if entry.visibility == 'public': if entry.visibility == 'public':
if entry.type.is_pyobject: if entry.type.is_pyobject:
......
...@@ -237,6 +237,26 @@ def create_pyx_as_pxd_pipeline(context, result): ...@@ -237,6 +237,26 @@ def create_pyx_as_pxd_pipeline(context, result):
pipeline.append(fake_pxd) pipeline.append(fake_pxd)
return pipeline return pipeline
def insert_into_pipeline(pipeline, transform, before=None, after=None):
"""
Insert a new transform into the pipeline after or before an instance of
the given class. e.g.
pipeline = insert_into_pipeline(pipeline, transform,
after=AnalyseDeclarationsTransform)
"""
assert before or after
cls = before or after
for i, t in enumerate(pipeline):
if isinstance(t, cls):
break
if after:
i += 1
return pipeline[:i] + [transform] + pipeline[i:]
# #
# Running a pipeline # Running a pipeline
# #
......
...@@ -2,30 +2,10 @@ from TreeFragment import parse_from_strings, StringParseContext ...@@ -2,30 +2,10 @@ from TreeFragment import parse_from_strings, StringParseContext
from Scanning import StringSourceDescriptor from Scanning import StringSourceDescriptor
import Symtab import Symtab
import Naming import Naming
from Cython.Compiler import Visitor
class NonManglingModuleScope(Symtab.ModuleScope):
def mangle(self, prefix, name=None):
if name:
if prefix in (Naming.typeobj_prefix, Naming.func_prefix):
# Functions, classes etc. gets a manually defined prefix easily
# manually callable instead (the one passed to CythonUtilityCode)
prefix = self.prefix
return "%s%s" % (prefix, name)
else:
return self.base.name
class CythonUtilityCodeContext(StringParseContext): class CythonUtilityCode(object):
scope = None
def find_module(self, module_name, relative_to = None, pos = None, need_pxd = 1):
if module_name != self.module_name:
raise AssertionError("Not yet supporting any cimports/includes from string code snippets")
if self.scope is None:
self.scope = NonManglingModuleScope(module_name, parent_module = None, context = self)
self.scope.prefix = self.prefix
return self.scope
class CythonUtilityCode:
""" """
Utility code written in the Cython language itself. Utility code written in the Cython language itself.
""" """
...@@ -48,17 +28,23 @@ class CythonUtilityCode: ...@@ -48,17 +28,23 @@ class CythonUtilityCode:
# any __test__ in user code; not desired # any __test__ in user code; not desired
excludes = [AutoTestDictTransform] excludes = [AutoTestDictTransform]
import Pipeline import Pipeline, ParseTreeTransforms
context = CythonUtilityCodeContext(self.name) #context = CythonUtilityCodeContext(self.name)
context.prefix = self.prefix #context.prefix = self.prefix
context = StringParseContext(self.name)
tree = parse_from_strings(self.name, self.pyx, context=context) tree = parse_from_strings(self.name, self.pyx, context=context)
pipeline = Pipeline.create_pipeline(context, 'pyx', exclude_classes=excludes) pipeline = Pipeline.create_pipeline(context, 'pyx', exclude_classes=excludes)
transform = ParseTreeTransforms.CnameDirectivesTransform(context)
# InterpretCompilerDirectives already does a cdef declarator check
#before = ParseTreeTransforms.DecoratorTransform
before = ParseTreeTransforms.InterpretCompilerDirectives
pipeline = Pipeline.insert_into_pipeline(pipeline, transform,
before=before)
(err, tree) = Pipeline.run_pipeline(pipeline, tree) (err, tree) = Pipeline.run_pipeline(pipeline, tree)
assert not err assert not err
return tree return tree
def put_code(self, output): def put_code(self, output):
pass pass
...@@ -4,9 +4,9 @@ from cython cimport _testscope as tester ...@@ -4,9 +4,9 @@ from cython cimport _testscope as tester
from cython.view cimport _testscope as viewtester from cython.view cimport _testscope as viewtester
def f(): def test_cdef_cython_utility():
""" """
>>> f() >>> test_cdef_cython_utility()
hello from cython scope, value=4 hello from cython scope, value=4
hello from cython.view scope, value=4 hello from cython.view scope, value=4
hello from cython scope, value=3 hello from cython scope, value=3
......
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