From: Robert Bradshaw Date: Fri, 22 Jan 2010 00:17:53 +0000 (-0800) Subject: #479, sub-directives via keywords X-Git-Tag: 0.12.1~19 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=143113fc380420b11ef96000a8f80a9e814a4570;p=cython.git #479, sub-directives via keywords --- diff --git a/Cython/Compiler/Options.py b/Cython/Compiler/Options.py index 9fccdf74..6019c6ca 100644 --- a/Cython/Compiler/Options.py +++ b/Cython/Compiler/Options.py @@ -64,6 +64,9 @@ directive_defaults = { 'profile': False, 'infer_types': False, 'autotestdict': True, + + 'warn': None, + 'warn.undeclared': False, # test support 'test_assert_path_exists' : [], diff --git a/Cython/Compiler/ParseTreeTransforms.py b/Cython/Compiler/ParseTreeTransforms.py index 3e00106b..c302210a 100644 --- a/Cython/Compiler/ParseTreeTransforms.py +++ b/Cython/Compiler/ParseTreeTransforms.py @@ -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): diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py index d459b4b2..a83bcc05 100644 --- a/Cython/Compiler/Parsing.py +++ b/Cython/Compiler/Parsing.py @@ -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 = {} diff --git a/tests/compile/c_directives.pyx b/tests/compile/c_directives.pyx index f6158976..942ba90e 100644 --- a/tests/compile/c_directives.pyx +++ b/tests/compile/c_directives.pyx @@ -1,5 +1,6 @@ # 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