aboutsummaryrefslogtreecommitdiffstats
path: root/mako/parsetree.py
diff options
context:
space:
mode:
Diffstat (limited to 'mako/parsetree.py')
-rw-r--r--mako/parsetree.py262
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
-
-