Functionality for only allowing directives in certain areas
authorDag Sverre Seljebotn <dagss@student.matnat.uio.no>
Thu, 1 Oct 2009 09:35:14 +0000 (11:35 +0200)
committerDag Sverre Seljebotn <dagss@student.matnat.uio.no>
Thu, 1 Oct 2009 09:35:14 +0000 (11:35 +0200)
Cython/Compiler/DebugFlags.py
Cython/Compiler/Options.py
Cython/Compiler/ParseTreeTransforms.py
tests/errors/e_doctesthack.pyx [new file with mode: 0644]

index d6d52189437a03b80c76443274e963ec4a9fb1cb..f7f5a0eb20590069d9ea74584d94538cf67249d8 100644 (file)
@@ -10,7 +10,7 @@ debug_temp_code_comments = 0
 debug_trace_code_generation = 0
 
 # Do not replace exceptions with user-friendly error messages
-debug_no_exception_intercept = 0
+debug_no_exception_intercept = 1
 
 # Print a message each time a new stage in the pipeline is entered
 debug_verbose_pipeline = 0
index 68eb6966617fcae2abf6b749807fd3cb769a7b4b..0739f2498012a383c949be401bdc2f6fd1faa7ba 100644 (file)
@@ -68,6 +68,7 @@ option_defaults = {
     'c99_complex' : False, # Don't use macro wrappers for complex arith, not sure what to name this...
     'callspec' : "",
     'profile': False,
+    'doctesthack': False
 }
 
 # Override types possibilities above, if needed
@@ -77,6 +78,11 @@ for key, val in option_defaults.items():
     if key not in option_types:
         option_types[key] = type(val)
 
+option_scopes = { # defaults to available everywhere
+    # 'module', 'function', 'class', 'with statement'
+    'doctesthack' : ('module',)
+}
+
 def parse_option_value(name, value):
     """
     Parses value as an option value for the given name and returns
index 5dad37861a784fadd3b84e965ed16dd5064b9aa1..131423963f35f333719ba1e6115a0d3cb53407e4 100644 (file)
@@ -338,14 +338,26 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
         self.cython_module_names = set()
         self.option_names = {}
 
+    def check_directive_scope(self, pos, directive, scope):
+        legal_scopes = Options.option_scopes.get(directive, None)
+        if legal_scopes and scope not in legal_scopes:
+            self.context.nonfatal_error(PostParseError(pos, 'The %s compiler directive '
+                                        'is not allowed in %s scope' % (directive, scope)))
+            return False
+        else:
+            return True
+        
     # Set up processing and handle the cython: comments.
     def visit_ModuleNode(self, node):
         options = copy.copy(Options.option_defaults)
         for key, value in self.compilation_option_overrides.iteritems():
+            if not self.check_directive_scope(node.pos, key, 'module'):
+                self.wrong_scope_error(node.pos, key, 'module')
+                del self.compilation_option_overrides[key]
+                continue
             if key in node.option_comments and node.option_comments[key] != value:
                 warning(node.pos, "Compiler directive differs between environment and file header; this will change "
                         "in Cython 0.12. See http://article.gmane.org/gmane.comp.python.cython.devel/5233", 2)
-                break
         options.update(node.option_comments)
         options.update(self.compilation_option_overrides)
         self.options = options
@@ -465,7 +477,6 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
     # Handle decorators
     def visit_FuncDefNode(self, node):
         options = []
-        
         if node.decorators:
             # Split the decorators into two lists -- real decorators and options
             realdecs = []
@@ -485,6 +496,9 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
             options.reverse() # Decorators coming first take precedence
             for option in options:
                 name, value = option
+                legal_scopes = Options.option_scopes.get(name, None)
+                if not self.check_directive_scope(node.pos, name, 'function'):
+                    continue
                 if name in optdict and isinstance(optdict[name], dict):
                     # only keywords can be merged, everything else
                     # overrides completely
@@ -503,7 +517,9 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
                 if option is not None and option[0] == u'locals':
                     node.directive_locals = option[1]
                 else:
-                    raise PostParseError(dec.pos, "Cdef functions can only take cython.locals() decorator.")
+                    self.context.nonfatal_error(PostParseError(dec.pos,
+                        "Cdef functions can only take cython.locals() decorator."))
+                    continue
         return node
                                    
     # Handle with statements
@@ -511,11 +527,13 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
         option = self.try_to_parse_option(node.manager)
         if option is not None:
             if node.target is not None:
-                raise PostParseError(node.pos, "Compiler option with statements cannot contain 'as'")
-            name, value = option
-            return self.visit_with_options(node.body, {name:value})
-        else:
-            return self.visit_Node(node)
+                self.context.nonfatal_error(
+                    PostParseError(node.pos, "Compiler option with statements cannot contain 'as'"))
+            else:
+                name, value = option
+                if self.check_directive_scope(node.pos, name, 'with statement'):
+                    return self.visit_with_options(node.body, {name:value})
+        return self.visit_Node(node)
 
 class WithTransform(CythonTransform, SkipDeclarations):
 
diff --git a/tests/errors/e_doctesthack.pyx b/tests/errors/e_doctesthack.pyx
new file mode 100644 (file)
index 0000000..56c4df0
--- /dev/null
@@ -0,0 +1,9 @@
+cimport cython
+
+@cython.doctesthack(False)
+def foo():
+    pass
+
+_ERRORS = u"""
+4:0: The doctesthack compiler directive is not allowed in function scope
+"""