diff options
Diffstat (limited to 'mako/parsetree.py')
-rw-r--r-- | mako/parsetree.py | 262 |
1 files changed, 142 insertions, 120 deletions
diff --git a/mako/parsetree.py b/mako/parsetree.py index 49ec4e0..e7af4bc 100644 --- a/mako/parsetree.py +++ b/mako/parsetree.py @@ -9,7 +9,9 @@ from mako import exceptions, ast, util, filters, compat import re + class Node(object): + """base class for a Node in the parse tree.""" def __init__(self, source, lineno, pos, filename): @@ -34,7 +36,9 @@ class Node(object): method = getattr(visitor, "visit" + self.__class__.__name__, traverse) method(self) + class TemplateNode(Node): + """a 'container' node that stores the overall collection of nodes.""" def __init__(self, filename): @@ -47,10 +51,12 @@ class TemplateNode(Node): def __repr__(self): return "TemplateNode(%s, %r)" % ( - util.sorted_dict_repr(self.page_attributes), - self.nodes) + util.sorted_dict_repr(self.page_attributes), + self.nodes) + class ControlLine(Node): + """defines a control line, a line-oriented python line or end tag. e.g.:: @@ -92,9 +98,9 @@ class ControlLine(Node): for this ControlLine""" return keyword in { - 'if':set(['else', 'elif']), - 'try':set(['except', 'finally']), - 'for':set(['else']) + 'if': set(['else', 'elif']), + 'try': set(['except', 'finally']), + 'for': set(['else']) }.get(self.keyword, []) def __repr__(self): @@ -105,7 +111,9 @@ class ControlLine(Node): (self.lineno, self.pos) ) + class Text(Node): + """defines plain text in the template.""" def __init__(self, content, **kwargs): @@ -115,7 +123,9 @@ class Text(Node): def __repr__(self): return "Text(%r, %r)" % (self.content, (self.lineno, self.pos)) + class Code(Node): + """defines a Python code block, either inline or module level. e.g.:: @@ -151,7 +161,9 @@ class Code(Node): (self.lineno, self.pos) ) + class Comment(Node): + """defines a comment line. # this is a comment @@ -165,7 +177,9 @@ class Comment(Node): def __repr__(self): return "Comment(%r, %r)" % (self.text, (self.lineno, self.pos)) + class Expression(Node): + """defines an inline expression. ${x+y} @@ -185,10 +199,10 @@ class Expression(Node): def undeclared_identifiers(self): # TODO: make the "filter" shortcut list configurable at parse/gen time return self.code.undeclared_identifiers.union( - self.escapes_code.undeclared_identifiers.difference( - set(filters.DEFAULT_ESCAPES.keys()) - ) - ).difference(self.code.declared_identifiers) + self.escapes_code.undeclared_identifiers.difference( + set(filters.DEFAULT_ESCAPES.keys()) + ) + ).difference(self.code.declared_identifiers) def __repr__(self): return "Expression(%r, %r, %r)" % ( @@ -197,7 +211,9 @@ class Expression(Node): (self.lineno, self.pos) ) + class _TagMeta(type): + """metaclass to allow Tag to produce a subclass according to its keyword""" @@ -212,7 +228,7 @@ class _TagMeta(type): if ":" in keyword: ns, defname = keyword.split(':') return type.__call__(CallNamespaceTag, ns, defname, - attributes, **kwargs) + attributes, **kwargs) try: cls = _TagMeta._classmap[keyword] @@ -226,7 +242,9 @@ class _TagMeta(type): ) return type.__call__(cls, keyword, attributes, **kwargs) + class Tag(compat.with_metaclass(_TagMeta, Node)): + """abstract base class for tags. <%sometag/> @@ -239,7 +257,7 @@ class Tag(compat.with_metaclass(_TagMeta, Node)): __keyword__ = None def __init__(self, keyword, attributes, expressions, - nonexpressions, required, **kwargs): + nonexpressions, required, **kwargs): """construct a new Tag instance. this constructor not called directly, and is only called @@ -267,7 +285,7 @@ class Tag(compat.with_metaclass(_TagMeta, Node)): if len(missing): raise exceptions.CompileException( "Missing attribute(s): %s" % - ",".join([repr(m) for m in missing]), + ",".join([repr(m) for m in missing]), **self.exception_kwargs) self.parent = None self.nodes = [] @@ -289,14 +307,14 @@ class Tag(compat.with_metaclass(_TagMeta, Node)): m = re.compile(r'^\${(.+?)}$', re.S).match(x) if m: code = ast.PythonCode(m.group(1).rstrip(), - **self.exception_kwargs) + **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) + code.undeclared_identifiers) expr.append('(%s)' % m.group(1)) else: if x: @@ -305,15 +323,15 @@ class Tag(compat.with_metaclass(_TagMeta, Node)): 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) + "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) + "Invalid attribute for tag '%s': '%s'" % + (self.keyword, key), + **self.exception_kwargs) self.expression_undeclared_identifiers = undeclared_identifiers def declared_identifiers(self): @@ -324,48 +342,50 @@ class Tag(compat.with_metaclass(_TagMeta, Node)): def __repr__(self): return "%s(%r, %s, %r, %r)" % (self.__class__.__name__, - self.keyword, - util.sorted_dict_repr(self.attributes), - (self.lineno, self.pos), - self.nodes - ) + self.keyword, + util.sorted_dict_repr(self.attributes), + (self.lineno, self.pos), + self.nodes + ) + class IncludeTag(Tag): __keyword__ = 'include' def __init__(self, keyword, attributes, **kwargs): super(IncludeTag, self).__init__( - keyword, - attributes, - ('file', 'import', 'args'), - (), ('file',), **kwargs) + keyword, + attributes, + ('file', 'import', 'args'), + (), ('file',), **kwargs) self.page_args = ast.PythonCode( - "__DUMMY(%s)" % attributes.get('args', ''), - **self.exception_kwargs) + "__DUMMY(%s)" % attributes.get('args', ''), + **self.exception_kwargs) def declared_identifiers(self): return [] def undeclared_identifiers(self): identifiers = self.page_args.undeclared_identifiers.\ - difference(set(["__DUMMY"])).\ - difference(self.page_args.declared_identifiers) + difference(set(["__DUMMY"])).\ + difference(self.page_args.declared_identifiers) return identifiers.union(super(IncludeTag, self). - undeclared_identifiers()) + undeclared_identifiers()) + class NamespaceTag(Tag): __keyword__ = 'namespace' def __init__(self, keyword, attributes, **kwargs): super(NamespaceTag, self).__init__( - keyword, attributes, - ('file',), - ('name','inheritable', - 'import','module'), - (), **kwargs) + keyword, attributes, + ('file',), + ('name', 'inheritable', + 'import', 'module'), + (), **kwargs) self.name = attributes.get('name', '__anon_%s' % hex(abs(id(self)))) - if not 'name' in attributes and not 'import' in attributes: + if 'name' not in attributes and 'import' not in attributes: raise exceptions.CompileException( "'name' and/or 'import' attributes are required " "for <%namespace>", @@ -379,52 +399,53 @@ class NamespaceTag(Tag): def declared_identifiers(self): return [] + class TextTag(Tag): __keyword__ = 'text' def __init__(self, keyword, attributes, **kwargs): super(TextTag, self).__init__( - keyword, - attributes, (), - ('filter'), (), **kwargs) + keyword, + attributes, (), + ('filter'), (), **kwargs) self.filter_args = ast.ArgumentList( - attributes.get('filter', ''), - **self.exception_kwargs) + attributes.get('filter', ''), + **self.exception_kwargs) def undeclared_identifiers(self): return self.filter_args.\ - undeclared_identifiers.\ - difference(filters.DEFAULT_ESCAPES.keys()).union( - self.expression_undeclared_identifiers - ) + undeclared_identifiers.\ + difference(filters.DEFAULT_ESCAPES.keys()).union( + self.expression_undeclared_identifiers + ) + class DefTag(Tag): __keyword__ = 'def' def __init__(self, keyword, attributes, **kwargs): expressions = ['buffered', 'cached'] + [ - c for c in attributes if c.startswith('cache_')] - + c for c in attributes if c.startswith('cache_')] super(DefTag, self).__init__( - keyword, - attributes, - expressions, - ('name', 'filter', 'decorator'), - ('name',), - **kwargs) + 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) + "Missing parenthesis in %def", + **self.exception_kwargs) self.function_decl = ast.FunctionDecl("def " + name + ":pass", - **self.exception_kwargs) + **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) + attributes.get('filter', ''), + **self.exception_kwargs) is_anonymous = False is_block = False @@ -443,50 +464,50 @@ class DefTag(Tag): res = [] for c in self.function_decl.defaults: res += list(ast.PythonCode(c, **self.exception_kwargs). - undeclared_identifiers) + undeclared_identifiers) return set(res).union( - self.filter_args.\ - undeclared_identifiers.\ - difference(filters.DEFAULT_ESCAPES.keys()) + self.filter_args. + undeclared_identifiers. + difference(filters.DEFAULT_ESCAPES.keys()) ).union( self.expression_undeclared_identifiers ).difference( self.function_decl.allargnames ) + class BlockTag(Tag): __keyword__ = 'block' def __init__(self, keyword, attributes, **kwargs): expressions = ['buffered', 'cached', 'args'] + [ - c for c in attributes if c.startswith('cache_')] + c for c in attributes if c.startswith('cache_')] super(BlockTag, self).__init__( - keyword, - attributes, - expressions, - ('name','filter', 'decorator'), - (), - **kwargs) + keyword, + attributes, + expressions, + ('name', 'filter', 'decorator'), + (), + **kwargs) name = attributes.get('name') - if name and not re.match(r'^[\w_]+$',name): + if name and not re.match(r'^[\w_]+$', name): raise exceptions.CompileException( - "%block may not specify an argument signature", - **self.exception_kwargs) + "%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 - ) + "Only named %blocks may specify args", + **self.exception_kwargs + ) self.body_decl = ast.FunctionArgs(attributes.get('args', ''), - **self.exception_kwargs) + **self.exception_kwargs) self.name = name self.decorator = attributes.get('decorator', '') self.filter_args = ast.ArgumentList( - attributes.get('filter', ''), - **self.exception_kwargs) - + attributes.get('filter', ''), + **self.exception_kwargs) is_block = True @@ -505,90 +526,91 @@ class BlockTag(Tag): return self.body_decl.allargnames def undeclared_identifiers(self): - return (self.filter_args.\ - undeclared_identifiers.\ - difference(filters.DEFAULT_ESCAPES.keys()) + return (self.filter_args. + undeclared_identifiers. + difference(filters.DEFAULT_ESCAPES.keys()) ).union(self.expression_undeclared_identifiers) - class CallTag(Tag): __keyword__ = 'call' def __init__(self, keyword, attributes, **kwargs): super(CallTag, self).__init__(keyword, attributes, - ('args'), ('expr',), ('expr',), **kwargs) + ('args'), ('expr',), ('expr',), **kwargs) self.expression = attributes['expr'] self.code = ast.PythonCode(self.expression, **self.exception_kwargs) self.body_decl = ast.FunctionArgs(attributes.get('args', ''), - **self.exception_kwargs) + **self.exception_kwargs) def declared_identifiers(self): return self.code.declared_identifiers.union(self.body_decl.allargnames) def undeclared_identifiers(self): return self.code.undeclared_identifiers.\ - difference(self.code.declared_identifiers) + difference(self.code.declared_identifiers) + class CallNamespaceTag(Tag): def __init__(self, namespace, defname, attributes, **kwargs): super(CallNamespaceTag, self).__init__( - namespace + ":" + defname, - attributes, - tuple(attributes.keys()) + ('args', ), - (), - (), - **kwargs) + namespace + ":" + defname, + attributes, + tuple(attributes.keys()) + ('args', ), + (), + (), + **kwargs) self.expression = "%s.%s(%s)" % ( - namespace, - defname, - ",".join(["%s=%s" % (k, v) for k, v in - self.parsed_attributes.items() - if k != 'args']) - ) + namespace, + defname, + ",".join(["%s=%s" % (k, v) for k, v in + self.parsed_attributes.items() + if k != 'args']) + ) self.code = ast.PythonCode(self.expression, **self.exception_kwargs) self.body_decl = ast.FunctionArgs( - attributes.get('args', ''), - **self.exception_kwargs) + attributes.get('args', ''), + **self.exception_kwargs) def declared_identifiers(self): return self.code.declared_identifiers.union(self.body_decl.allargnames) def undeclared_identifiers(self): return self.code.undeclared_identifiers.\ - difference(self.code.declared_identifiers) + difference(self.code.declared_identifiers) + class InheritTag(Tag): __keyword__ = 'inherit' def __init__(self, keyword, attributes, **kwargs): super(InheritTag, self).__init__( - keyword, attributes, - ('file',), (), ('file',), **kwargs) + keyword, attributes, + ('file',), (), ('file',), **kwargs) + class PageTag(Tag): __keyword__ = 'page' def __init__(self, keyword, attributes, **kwargs): - expressions = ['cached', 'args', 'expression_filter', 'enable_loop'] + [ - c for c in attributes if c.startswith('cache_')] + expressions = \ + ['cached', 'args', 'expression_filter', 'enable_loop'] + \ + [c for c in attributes if c.startswith('cache_')] super(PageTag, self).__init__( - keyword, - attributes, - expressions, - (), - (), - **kwargs) + keyword, + attributes, + expressions, + (), + (), + **kwargs) self.body_decl = ast.FunctionArgs(attributes.get('args', ''), - **self.exception_kwargs) + **self.exception_kwargs) self.filter_args = ast.ArgumentList( - attributes.get('expression_filter', ''), - **self.exception_kwargs) + attributes.get('expression_filter', ''), + **self.exception_kwargs) def declared_identifiers(self): return self.body_decl.allargnames - - |