Commit 143113fc authored by Robert Bradshaw's avatar Robert Bradshaw

#479, sub-directives via keywords

parent 5553fc00
......@@ -64,6 +64,9 @@ directive_defaults = {
'profile': False,
'infer_types': False,
'autotestdict': True,
'warn': None,
'warn.undeclared': False,
# test support
'test_assert_path_exists' : [],
......
......@@ -427,55 +427,74 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
node.cython_attribute = self.directive_names.get(node.name)
return node
def try_to_parse_directive(self, node):
def try_to_parse_directives(self, node):
# If node is the contents of an directive (in a with statement or
# decorator), returns (directivename, value).
# decorator), returns a list of (directivename, value) pairs.
# Otherwise, returns None
optname = None
if isinstance(node, CallNode):
self.visit(node.function)
optname = node.function.as_cython_attribute()
if optname:
directivetype = Options.directive_types.get(optname)
if directivetype:
args, kwds = node.explicit_args_kwds()
if optname == 'infer_types':
if kwds is not None or len(args) != 1:
raise PostParseError(node.function.pos,
'The %s directive takes one compile-time boolean argument' % optname)
elif isinstance(args[0], BoolNode):
return (optname, args[0].value)
elif isinstance(args[0], NoneNode):
return (optname, None)
else:
raise PostParseError(node.function.pos,
'The %s directive takes one compile-time boolean argument' % optname)
elif directivetype is bool:
if kwds is not None or len(args) != 1 or not isinstance(args[0], BoolNode):
raise PostParseError(node.function.pos,
'The %s directive takes one compile-time boolean argument' % optname)
return (optname, args[0].value)
elif directivetype is str:
if kwds is not None or len(args) != 1 or not isinstance(args[0], (StringNode, UnicodeNode)):
raise PostParseError(node.function.pos,
'The %s directive takes one compile-time string argument' % optname)
return (optname, str(args[0].value))
elif directivetype is dict:
if len(args) != 0:
raise PostParseError(node.function.pos,
'The %s directive takes no prepositional arguments' % optname)
return optname, dict([(key.value, value) for key, value in kwds.key_value_pairs])
elif directivetype is list:
if kwds and len(kwds) != 0:
raise PostParseError(node.function.pos,
'The %s directive takes no keyword arguments' % optname)
return optname, [ str(arg.value) for arg in args ]
else:
assert False
if optname:
directivetype = Options.directive_types.get(optname)
if directivetype:
args, kwds = node.explicit_args_kwds()
directives = []
key_value_pairs = []
if kwds is not None and directivetype is not dict:
for keyvalue in kwds.key_value_pairs:
key, value = keyvalue
sub_optname = "%s.%s" % (optname, key.value)
if Options.directive_types.get(sub_optname):
directives.append(self.try_to_parse_directive(sub_optname, [value], None, keyvalue.pos))
else:
key_value_pairs.append(keyvalue)
if not key_value_pairs:
kwds = None
else:
kwds.key_value_pairs = key_value_pairs
if directives and not kwds and not args:
return directives
directives.append(self.try_to_parse_directive(optname, args, kwds, node.function.pos))
return directives
return None
def try_to_parse_directive(self, optname, args, kwds, pos):
directivetype = Options.directive_types.get(optname)
if optname == 'infer_types':
if kwds is not None or len(args) != 1:
raise PostParseError(pos,
'The %s directive takes one compile-time boolean argument' % optname)
elif isinstance(args[0], BoolNode):
return (optname, args[0].value)
elif isinstance(args[0], NoneNode):
return (optname, None)
else:
raise PostParseError(pos,
'The %s directive takes one compile-time boolean argument' % optname)
elif directivetype is bool:
if kwds is not None or len(args) != 1 or not isinstance(args[0], BoolNode):
raise PostParseError(pos,
'The %s directive takes one compile-time boolean argument' % optname)
return (optname, args[0].value)
elif directivetype is str:
if kwds is not None or len(args) != 1 or not isinstance(args[0], (StringNode, UnicodeNode)):
raise PostParseError(pos,
'The %s directive takes one compile-time string argument' % optname)
return (optname, str(args[0].value))
elif directivetype is dict:
if len(args) != 0:
raise PostParseError(pos,
'The %s directive takes no prepositional arguments' % optname)
return optname, dict([(key.value, value) for key, value in kwds.key_value_pairs])
elif directivetype is list:
if kwds and len(kwds) != 0:
raise PostParseError(pos,
'The %s directive takes no keyword arguments' % optname)
return optname, [ str(arg.value) for arg in args ]
else:
assert False
def visit_with_directives(self, body, directives):
olddirectives = self.directives
newdirectives = copy.copy(olddirectives)
......@@ -495,9 +514,9 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
# Split the decorators into two lists -- real decorators and directives
realdecs = []
for dec in node.decorators:
directive = self.try_to_parse_directive(dec.decorator)
if directive is not None:
directives.append(directive)
new_directives = self.try_to_parse_directives(dec.decorator)
if new_directives is not None:
directives.extend(new_directives)
else:
realdecs.append(dec)
if realdecs and isinstance(node, CFuncDefNode):
......@@ -533,26 +552,28 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
def visit_CVarDefNode(self, node):
if node.decorators:
for dec in node.decorators:
directive = self.try_to_parse_directive(dec.decorator)
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."))
continue
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
def visit_WithStatNode(self, node):
directive = self.try_to_parse_directive(node.manager)
if directive is not None:
if node.target is not None:
self.context.nonfatal_error(
PostParseError(node.pos, "Compiler directive with statements cannot contain 'as'"))
else:
name, value = directive
if self.check_directive_scope(node.pos, name, 'with statement'):
return self.visit_with_directives(node.body, {name:value})
directive_dict = {}
for directive in self.try_to_parse_directives(node.manager) or []:
if directive is not None:
if node.target is not None:
self.context.nonfatal_error(
PostParseError(node.pos, "Compiler directive with statements cannot contain 'as'"))
else:
name, value = directive
if self.check_directive_scope(node.pos, name, 'with statement'):
directive_dict[name] = value
if directive_dict:
return self.visit_with_directives(node.body, directive_dict)
return self.visit_Node(node)
class WithTransform(CythonTransform, SkipDeclarations):
......
......@@ -2579,7 +2579,7 @@ def p_code(s, level=None):
repr(s.sy), repr(s.systring)))
return body
COMPILER_DIRECTIVE_COMMENT_RE = re.compile(r"^#\s*cython:\s*(\w+\s*=.*)$")
COMPILER_DIRECTIVE_COMMENT_RE = re.compile(r"^#\s*cython:\s*((\w|[.])+\s*=.*)$")
def p_compiler_directive_comments(s):
result = {}
......
# cython: boundscheck = False
# cython: ignoreme = OK
# cython: warn.undeclared = False
# This testcase is most useful if you inspect the generated C file
......@@ -32,3 +33,8 @@ def i(object[int] buf):
with bc(True):
print buf[3] # bs
from cython cimport warn as my_warn
@my_warn(undeclared=True)
def j():
pass
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