Commit 765f2b7a authored by Stefan Behnel's avatar Stefan Behnel

clean up directive decorator parsing, support boolean directives as plain...

clean up directive decorator parsing, support boolean directives as plain names, support directives on cdef classes
parent 0722bb26
...@@ -727,7 +727,18 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations): ...@@ -727,7 +727,18 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
return directives return directives
directives.append(self.try_to_parse_directive(optname, args, kwds, node.function.pos)) directives.append(self.try_to_parse_directive(optname, args, kwds, node.function.pos))
return directives return directives
elif isinstance(node, (AttributeNode, NameNode)):
self.visit(node)
optname = node.as_cython_attribute()
if optname:
directivetype = Options.directive_types.get(optname)
if directivetype is bool:
return [(optname, True)]
elif directivetype is None:
return [(optname, None)]
else:
raise PostParseError(
node.pos, "The '%s' directive should be used as a function call." % optname)
return None return None
def try_to_parse_directive(self, optname, args, kwds, pos): def try_to_parse_directive(self, optname, args, kwds, pos):
...@@ -771,57 +782,66 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations): ...@@ -771,57 +782,66 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
# Handle decorators # Handle decorators
def visit_FuncDefNode(self, node): def visit_FuncDefNode(self, node):
directives = [] directives = self._extract_directives(node, 'function')
if node.decorators: if not directives:
# Split the decorators into two lists -- real decorators and directives return self.visit_Node(node)
realdecs = [] body = StatListNode(node.pos, stats=[node])
for dec in node.decorators: return self.visit_with_directives(body, directives)
new_directives = self.try_to_parse_directives(dec.decorator)
if new_directives is not None: def visit_CVarDefNode(self, node):
directives.extend(new_directives) for dec in node.decorators:
for directive in self.try_to_parse_directives(dec.decorator) or ():
if directive is not None and directive[0] == u'locals':
node.directive_locals = directive[1]
else: else:
realdecs.append(dec) self.context.nonfatal_error(PostParseError(dec.pos,
if realdecs and isinstance(node, CFuncDefNode): "Cdef functions can only take cython.locals() decorator."))
raise PostParseError(realdecs[0].pos, "Cdef functions cannot take arbitrary decorators.") return node
def visit_CClassDefNode(self, node):
directives = self._extract_directives(node, 'cclass')
if not directives:
return self.visit_Node(node)
body = StatListNode(node.pos, stats=[node])
return self.visit_with_directives(body, directives)
def _extract_directives(self, node, scope_name):
if not node.decorators:
return {}
# Split the decorators into two lists -- real decorators and directives
directives = []
realdecs = []
for dec in node.decorators:
new_directives = self.try_to_parse_directives(dec.decorator)
if new_directives is not None:
for directive in new_directives:
if self.check_directive_scope(node.pos, directive[0], scope_name):
directives.append(directive)
else: else:
node.decorators = realdecs realdecs.append(dec)
if realdecs and isinstance(node, (CFuncDefNode, CClassDefNode)):
if directives: raise PostParseError(realdecs[0].pos, "Cdef functions/classes cannot take arbitrary decorators.")
optdict = {} else:
directives.reverse() # Decorators coming first take precedence node.decorators = realdecs
for directive in directives: # merge or override repeated directives
name, value = directive optdict = {}
legal_scopes = Options.directive_scopes.get(name, None) directives.reverse() # Decorators coming first take precedence
if not self.check_directive_scope(node.pos, name, 'function'): for directive in directives:
continue name, value = directive
if name in optdict: if name in optdict:
old_value = optdict[name] old_value = optdict[name]
# keywords and arg lists can be merged, everything # keywords and arg lists can be merged, everything
# else overrides completely # else overrides completely
if isinstance(old_value, dict): if isinstance(old_value, dict):
old_value.update(value) old_value.update(value)
elif isinstance(old_value, list): elif isinstance(old_value, list):
old_value.extend(value) old_value.extend(value)
else:
optdict[name] = value
else: else:
optdict[name] = value optdict[name] = value
body = StatListNode(node.pos, stats=[node]) else:
return self.visit_with_directives(body, optdict) optdict[name] = value
else: return optdict
return self.visit_Node(node)
def visit_CVarDefNode(self, node):
if node.decorators:
for dec in node.decorators:
for directive in self.try_to_parse_directives(dec.decorator) or []:
if directive is not None and directive[0] == u'locals':
node.directive_locals = directive[1]
else:
self.context.nonfatal_error(PostParseError(dec.pos,
"Cdef functions can only take cython.locals() decorator."))
return node
# Handle with statements # Handle with statements
def visit_WithStatNode(self, node): def visit_WithStatNode(self, node):
directive_dict = {} directive_dict = {}
......
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