clean up directive decorator parsing, support boolean directives as plain names,...
authorStefan Behnel <scoder@users.berlios.de>
Sat, 30 Oct 2010 13:24:04 +0000 (15:24 +0200)
committerStefan Behnel <scoder@users.berlios.de>
Sat, 30 Oct 2010 13:24:04 +0000 (15:24 +0200)
Cython/Compiler/ParseTreeTransforms.py

index 9e16924715c2e1239cde30eb6dd7bee00fba1391..3e6a398bf799b3218b808d0c9ab913fdb062f56b 100644 (file)
@@ -727,7 +727,18 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
                             return directives
                     directives.append(self.try_to_parse_directive(optname, args, kwds, node.function.pos))
                     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
 
     def try_to_parse_directive(self, optname, args, kwds, pos):
@@ -771,57 +782,66 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
  
     # Handle decorators
     def visit_FuncDefNode(self, node):
-        directives = []
-        if node.decorators:
-            # Split the decorators into two lists -- real decorators and directives
-            realdecs = []
-            for dec in node.decorators:
-                new_directives = self.try_to_parse_directives(dec.decorator)
-                if new_directives is not None:
-                    directives.extend(new_directives)
+        directives = self._extract_directives(node, 'function')
+        if not directives:
+            return self.visit_Node(node)
+        body = StatListNode(node.pos, stats=[node])
+        return self.visit_with_directives(body, directives)
+
+    def visit_CVarDefNode(self, node):
+        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:
-                    realdecs.append(dec)
-            if realdecs and isinstance(node, CFuncDefNode):
-                raise PostParseError(realdecs[0].pos, "Cdef functions cannot take arbitrary decorators.")
+                    self.context.nonfatal_error(PostParseError(dec.pos,
+                        "Cdef functions can only take cython.locals() decorator."))
+        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:
-                node.decorators = realdecs
-        
-        if directives:
-            optdict = {}
-            directives.reverse() # Decorators coming first take precedence
-            for directive in directives:
-                name, value = directive
-                legal_scopes = Options.directive_scopes.get(name, None)
-                if not self.check_directive_scope(node.pos, name, 'function'):
-                    continue
-                if name in optdict:
-                    old_value = optdict[name]
-                    # keywords and arg lists can be merged, everything
-                    # else overrides completely
-                    if isinstance(old_value, dict):
-                        old_value.update(value)
-                    elif isinstance(old_value, list):
-                        old_value.extend(value)
-                    else:
-                        optdict[name] = value
+                realdecs.append(dec)
+        if realdecs and isinstance(node, (CFuncDefNode, CClassDefNode)):
+            raise PostParseError(realdecs[0].pos, "Cdef functions/classes cannot take arbitrary decorators.")
+        else:
+            node.decorators = realdecs
+        # merge or override repeated directives
+        optdict = {}
+        directives.reverse() # Decorators coming first take precedence
+        for directive in directives:
+            name, value = directive
+            if name in optdict:
+                old_value = optdict[name]
+                # keywords and arg lists can be merged, everything
+                # else overrides completely
+                if isinstance(old_value, dict):
+                    old_value.update(value)
+                elif isinstance(old_value, list):
+                    old_value.extend(value)
                 else:
                     optdict[name] = value
-            body = StatListNode(node.pos, stats=[node])
-            return self.visit_with_directives(body, optdict)
-        else:
-            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
-                                   
+            else:
+                optdict[name] = value
+        return optdict
+
     # Handle with statements
     def visit_WithStatNode(self, node):
         directive_dict = {}