def __init__(self, keyword, attributes, **kwargs): expressions = ["buffered", "cached", "args"] + [ c for c in attributes if c.startswith("cache_") ] super(BlockTag, self).__init__( keyword, attributes, expressions, ("name", "filter", "decorator"), (), **kwargs ) name = attributes.get("name") if name and not re.match(r"^[\w_]+$", name): raise exceptions.CompileException( "%block may not specify an argument signature", **self.exception_kwargs ) if not name and attributes.get("args", None): raise exceptions.CompileException( "Only named %blocks may specify args", **self.exception_kwargs ) self.body_decl = ast.FunctionArgs( attributes.get("args", ""), **self.exception_kwargs ) self.name = name self.decorator = attributes.get("decorator", "") self.filter_args = ast.ArgumentList( attributes.get("filter", ""), **self.exception_kwargs )
def visitBlockTag(self, node): if node is not self.node and not node.is_anonymous: if isinstance(self.node, parsetree.DefTag): raise exceptions.CompileException( "Named block '%s' not allowed inside of def '%s'" % (node.name, self.node.name), **node.exception_kwargs) elif isinstance(self.node, (parsetree.CallTag, parsetree.CallNamespaceTag)): raise exceptions.CompileException( "Named block '%s' not allowed inside of <%%call> tag" % (node.name, ), **node.exception_kwargs) for ident in node.undeclared_identifiers(): if ident != "context" and ident not in self.declared.union( self.locally_declared): self.undeclared.add(ident) if not node.is_anonymous: self._check_name_exists(self.topleveldefs, node) self.undeclared.add(node.funcname) elif node is not self.node: self._check_name_exists(self.closuredefs, node) for ident in node.declared_identifiers(): self.argument_declared.add(ident) for n in node.nodes: n.accept_visitor(self)
def __init__(self, code, allow_kwargs=True, **exception_kwargs): self.code = code expr = pyparser.parse(code, "exec", **exception_kwargs) f = pyparser.ParseFunc(self, **exception_kwargs) f.visit(expr) if not hasattr(self, "funcname"): raise exceptions.CompileException( "Code '%s' is not a function declaration" % code, **exception_kwargs) if not allow_kwargs and self.kwargs: raise exceptions.CompileException( "'**%s' keyword argument not allowed here" % self.kwargnames[-1], **exception_kwargs)
def __init__(self, keyword, attributes, **kwargs): expressions = ["buffered", "cached"] + [ c for c in attributes if c.startswith("cache_") ] super(DefTag, self).__init__( keyword, attributes, expressions, ("name", "filter", "decorator"), ("name",), **kwargs ) name = attributes["name"] if re.match(r"^[\w_]+$", name): raise exceptions.CompileException( "Missing parenthesis in %def", **self.exception_kwargs ) self.function_decl = ast.FunctionDecl( "def " + name + ":pass", **self.exception_kwargs ) self.name = self.function_decl.funcname self.decorator = attributes.get("decorator", "") self.filter_args = ast.ArgumentList( attributes.get("filter", ""), **self.exception_kwargs )
def visitDefOrBase(s, node): if node.is_anonymous: raise exceptions.CompileException( "Can't put anonymous blocks inside " "<%namespace>", **node.exception_kwargs) self.write_inline_def(node, identifiers, nested=False) export.append(node.funcname)
def decode_raw_stream(self, text, decode_raw, known_encoding, filename): """given string/unicode or bytes/string, determine encoding from magic encoding comment, return body as unicode or raw if decode_raw=False """ if isinstance(text, compat.text_type): m = self._coding_re.match(text) encoding = m and m.group(1) or known_encoding or "utf-8" return encoding, text if text.startswith(codecs.BOM_UTF8): text = text[len(codecs.BOM_UTF8):] parsed_encoding = "utf-8" m = self._coding_re.match(text.decode("utf-8", "ignore")) if m is not None and m.group(1) != "utf-8": raise exceptions.CompileException( "Found utf-8 BOM in file, with conflicting " "magic encoding comment of '%s'" % m.group(1), text.decode("utf-8", "ignore"), 0, 0, filename, ) else: m = self._coding_re.match(text.decode("utf-8", "ignore")) if m: parsed_encoding = m.group(1) else: parsed_encoding = known_encoding or "utf-8" if decode_raw: try: text = text.decode(parsed_encoding) except UnicodeDecodeError: raise exceptions.CompileException( "Unicode decode operation of encoding '%s' failed" % parsed_encoding, text.decode("utf-8", "ignore"), 0, 0, filename, ) return parsed_encoding, text
def _check_name_exists(self, collection, node): existing = collection.get(node.funcname) collection[node.funcname] = node if (existing is not None and existing is not node and (node.is_block or existing.is_block)): raise exceptions.CompileException( "%%def or %%block named '%s' already " "exists in this template." % node.funcname, **node.exception_kwargs)
def _parse_attributes(self, expressions, nonexpressions): undeclared_identifiers = set() self.parsed_attributes = {} for key in self.attributes: if key in expressions: expr = [] for x in re.compile(r"(\${.+?})", re.S).split( self.attributes[key] ): m = re.compile(r"^\${(.+?)}$", re.S).match(x) if m: code = ast.PythonCode( m.group(1).rstrip(), **self.exception_kwargs ) # we aren't discarding "declared_identifiers" here, # which we do so that list comprehension-declared # variables aren't counted. As yet can't find a # condition that requires it here. undeclared_identifiers = undeclared_identifiers.union( code.undeclared_identifiers ) expr.append("(%s)" % m.group(1)) else: if x: expr.append(repr(x)) self.parsed_attributes[key] = " + ".join(expr) or repr("") elif key in nonexpressions: if re.search(r"\${.+?}", self.attributes[key]): raise exceptions.CompileException( "Attibute '%s' in tag '%s' does not allow embedded " "expressions" % (key, self.keyword), **self.exception_kwargs ) self.parsed_attributes[key] = repr(self.attributes[key]) else: raise exceptions.CompileException( "Invalid attribute for tag '%s': '%s'" % (self.keyword, key), **self.exception_kwargs ) self.expression_undeclared_identifiers = undeclared_identifiers
def parse(self): self.encoding, self.text = self.decode_raw_stream( self.text, not self.disable_unicode, self.encoding, self.filename) for preproc in self.preprocessor: self.text = preproc(self.text) # push the match marker past the # encoding comment. self.match_reg(self._coding_re) self.textlength = len(self.text) while True: if self.match_position > self.textlength: break if self.match_end(): break if self.match_expression(): continue if self.match_control_line(): continue if self.match_comment(): continue if self.match_tag_start(): continue if self.match_tag_end(): continue if self.match_python_block(): continue if self.match_text(): continue if self.match_position > self.textlength: break raise exceptions.CompileException("assertion failed") if len(self.tag): raise exceptions.SyntaxException( "Unclosed tag: <%%%s>" % self.tag[-1].keyword, **self.exception_kwargs) if len(self.control_line): raise exceptions.SyntaxException( "Unterminated control keyword: '%s'" % self.control_line[-1].keyword, self.text, self.control_line[-1].lineno, self.control_line[-1].pos, self.filename, ) return self.template
def __init__(self, keyword, attributes, **kwargs): super(NamespaceTag, self).__init__( keyword, attributes, ("file",), ("name", "inheritable", "import", "module"), (), **kwargs ) self.name = attributes.get("name", "__anon_%s" % hex(abs(id(self)))) if "name" not in attributes and "import" not in attributes: raise exceptions.CompileException( "'name' and/or 'import' attributes are required " "for <%namespace>", **self.exception_kwargs ) if "file" in attributes and "module" in attributes: raise exceptions.CompileException( "<%namespace> may only have one of 'file' or 'module'", **self.exception_kwargs )
def visit_ImportFrom(self, node): for name in node.names: if name.asname is not None: self._add_declared(name.asname) else: if name.name == "*": raise exceptions.CompileException( "'import *' is not supported, since all identifier " "names must be explicitly declared. Please use the " "form 'from <modulename> import <name1>, <name2>, " "...' instead.", **self.exception_kwargs ) self._add_declared(name.name)
def __init__(self, code, **exception_kwargs): m = re.match(r"^(\w+)(?:\s+(.*?))?:\s*(#|$)", code.strip(), re.S) if not m: raise exceptions.CompileException( "Fragment '%s' is not a partial control statement" % code, **exception_kwargs) if m.group(3): code = code[:m.start(3)] (keyword, expr) = m.group(1, 2) if keyword in ["for", "if", "while"]: code = code + "pass" elif keyword == "try": code = code + "pass\nexcept:pass" elif keyword == "elif" or keyword == "else": code = "if False:pass\n" + code + "pass" elif keyword == "except": code = "try:pass\n" + code + "pass" elif keyword == "with": code = code + "pass" else: raise exceptions.CompileException( "Unsupported control keyword: '%s'" % keyword, **exception_kwargs) super(PythonFragment, self).__init__(code, **exception_kwargs)
def __call__(cls, keyword, attributes, **kwargs): if ":" in keyword: ns, defname = keyword.split(":") return type.__call__( CallNamespaceTag, ns, defname, attributes, **kwargs ) try: cls = _TagMeta._classmap[keyword] except KeyError: raise exceptions.CompileException( "No such tag: '%s'" % keyword, source=kwargs["source"], lineno=kwargs["lineno"], pos=kwargs["pos"], filename=kwargs["filename"], ) return type.__call__(cls, keyword, attributes, **kwargs)
def __init__( self, keyword, attributes, expressions, nonexpressions, required, **kwargs ): r"""construct a new Tag instance. this constructor not called directly, and is only called by subclasses. :param keyword: the tag keyword :param attributes: raw dictionary of attribute key/value pairs :param expressions: a set of identifiers that are legal attributes, which can also contain embedded expressions :param nonexpressions: a set of identifiers that are legal attributes, which cannot contain embedded expressions :param \**kwargs: other arguments passed to the Node superclass (lineno, pos) """ super(Tag, self).__init__(**kwargs) self.keyword = keyword self.attributes = attributes self._parse_attributes(expressions, nonexpressions) missing = [r for r in required if r not in self.parsed_attributes] if len(missing): raise exceptions.CompileException( "Missing attribute(s): %s" % ",".join([repr(m) for m in missing]), **self.exception_kwargs ) self.parent = None self.nodes = []