Commit d12a8228 authored by Dag Sverre Seljebotn's avatar Dag Sverre Seljebotn

Functionality for only allowing directives in certain areas

parent d36f3b8a
...@@ -10,7 +10,7 @@ debug_temp_code_comments = 0 ...@@ -10,7 +10,7 @@ debug_temp_code_comments = 0
debug_trace_code_generation = 0 debug_trace_code_generation = 0
# Do not replace exceptions with user-friendly error messages # 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 # Print a message each time a new stage in the pipeline is entered
debug_verbose_pipeline = 0 debug_verbose_pipeline = 0
...@@ -68,6 +68,7 @@ option_defaults = { ...@@ -68,6 +68,7 @@ option_defaults = {
'c99_complex' : False, # Don't use macro wrappers for complex arith, not sure what to name this... 'c99_complex' : False, # Don't use macro wrappers for complex arith, not sure what to name this...
'callspec' : "", 'callspec' : "",
'profile': False, 'profile': False,
'doctesthack': False
} }
# Override types possibilities above, if needed # Override types possibilities above, if needed
...@@ -77,6 +78,11 @@ for key, val in option_defaults.items(): ...@@ -77,6 +78,11 @@ for key, val in option_defaults.items():
if key not in option_types: if key not in option_types:
option_types[key] = type(val) option_types[key] = type(val)
option_scopes = { # defaults to available everywhere
# 'module', 'function', 'class', 'with statement'
'doctesthack' : ('module',)
}
def parse_option_value(name, value): def parse_option_value(name, value):
""" """
Parses value as an option value for the given name and returns Parses value as an option value for the given name and returns
......
...@@ -338,14 +338,26 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations): ...@@ -338,14 +338,26 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
self.cython_module_names = set() self.cython_module_names = set()
self.option_names = {} 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. # Set up processing and handle the cython: comments.
def visit_ModuleNode(self, node): def visit_ModuleNode(self, node):
options = copy.copy(Options.option_defaults) options = copy.copy(Options.option_defaults)
for key, value in self.compilation_option_overrides.iteritems(): 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: 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 " 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) "in Cython 0.12. See http://article.gmane.org/gmane.comp.python.cython.devel/5233", 2)
break
options.update(node.option_comments) options.update(node.option_comments)
options.update(self.compilation_option_overrides) options.update(self.compilation_option_overrides)
self.options = options self.options = options
...@@ -465,7 +477,6 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations): ...@@ -465,7 +477,6 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
# Handle decorators # Handle decorators
def visit_FuncDefNode(self, node): def visit_FuncDefNode(self, node):
options = [] options = []
if node.decorators: if node.decorators:
# Split the decorators into two lists -- real decorators and options # Split the decorators into two lists -- real decorators and options
realdecs = [] realdecs = []
...@@ -485,6 +496,9 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations): ...@@ -485,6 +496,9 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
options.reverse() # Decorators coming first take precedence options.reverse() # Decorators coming first take precedence
for option in options: for option in options:
name, value = option 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): if name in optdict and isinstance(optdict[name], dict):
# only keywords can be merged, everything else # only keywords can be merged, everything else
# overrides completely # overrides completely
...@@ -503,7 +517,9 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations): ...@@ -503,7 +517,9 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
if option is not None and option[0] == u'locals': if option is not None and option[0] == u'locals':
node.directive_locals = option[1] node.directive_locals = option[1]
else: 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 return node
# Handle with statements # Handle with statements
...@@ -511,11 +527,13 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations): ...@@ -511,11 +527,13 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
option = self.try_to_parse_option(node.manager) option = self.try_to_parse_option(node.manager)
if option is not None: if option is not None:
if node.target is not None: if node.target is not None:
raise PostParseError(node.pos, "Compiler option with statements cannot contain 'as'") self.context.nonfatal_error(
name, value = option PostParseError(node.pos, "Compiler option with statements cannot contain 'as'"))
return self.visit_with_options(node.body, {name:value}) else:
else: name, value = option
return self.visit_Node(node) 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): class WithTransform(CythonTransform, SkipDeclarations):
......
cimport cython
@cython.doctesthack(False)
def foo():
pass
_ERRORS = u"""
4:0: The doctesthack compiler directive is not allowed in function scope
"""
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