aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2006-11-19 18:47:45 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2006-11-19 18:47:45 +0000
commitdf656c2ca7538fa165b8c93349c9fc763d0ea380 (patch)
tree6fa929175f433b2c7450e8962b7a12386b757e3b /lib
parent9a2c39f845c7b3e2a8ad428b381466d20ad0f475 (diff)
downloadexternal_python_mako-df656c2ca7538fa165b8c93349c9fc763d0ea380.tar.gz
external_python_mako-df656c2ca7538fa165b8c93349c9fc763d0ea380.tar.bz2
external_python_mako-df656c2ca7538fa165b8c93349c9fc763d0ea380.zip
nested components, codegen arch, lexer fixes
Diffstat (limited to 'lib')
-rw-r--r--lib/mako/codegen.py192
-rw-r--r--lib/mako/context.py16
-rw-r--r--lib/mako/lexer.py19
-rw-r--r--lib/mako/parsetree.py67
-rw-r--r--lib/mako/runtime.py46
-rw-r--r--lib/mako/template.py4
6 files changed, 257 insertions, 87 deletions
diff --git a/lib/mako/codegen.py b/lib/mako/codegen.py
index 8bfb1b9..537926a 100644
--- a/lib/mako/codegen.py
+++ b/lib/mako/codegen.py
@@ -1,9 +1,12 @@
"""provides the Compiler object for generating module source code."""
from StringIO import StringIO
+import time
from mako.pygen import PythonPrinter
from mako import util, ast
+MAGIC_NUMBER = 1
+
class Compiler(object):
def __init__(self, node, filename=None):
self.node = node
@@ -17,32 +20,36 @@ class Compiler(object):
f = FindPyDecls()
self.node.accept_visitor(f)
- components = []
- class FindTopLevelComponents(object):
- def visitComponentTag(self, node):
- components.append(node)
- self.node.accept_visitor(FindTopLevelComponents())
+ components = _find_top_level_components(self.node)
(module_declared, module_undeclared) = (util.Set(), util.Set())
buf = StringIO()
printer = PythonPrinter(buf)
+ # module-level names, python code
+ printer.writeline("from mako import runtime")
+ printer.writeline("_magic_number = %s" % repr(MAGIC_NUMBER))
+ printer.writeline("_modified_time = %s" % repr(time.time()))
printer.writeline("_template_filename=%s" % repr(self.filename))
+ buf.write("\n\n")
for n in module_code:
- (module_declared, module_undeclared) = self._get_declared([n], module_declared, module_undeclared)
+ (module_declared, module_undeclared) = _find_declared_identifiers([n], module_declared, module_undeclared)
printer.writeline("# SOURCE LINE %d" % n.lineno, is_comment=True)
printer.write_indented_block(n.text)
- (declared, undeclared) = self._get_declared(self.node.nodes, module_declared)
- self.node.accept_visitor(_GenerateRenderMethod(printer, undeclared))
+ # print main render() method
+ (declared, undeclared) = _find_declared_identifiers(self.node.nodes, module_declared)
+ self.node.accept_visitor(_GenerateRenderMethod(printer, declared, undeclared, components))
printer.writeline(None)
buf.write("\n\n")
+ # print render() for each top-level component
for node in components:
declared = util.Set(node.declared_identifiers()).union(module_declared)
- (declared, undeclared) = self._get_declared(node.nodes, declared)
- render = _GenerateRenderMethod(printer, undeclared, name="render_%s" % node.name, args=node.function_decl.get_argument_expressions())
+ (declared, undeclared) = _find_declared_identifiers(node.nodes, declared)
+ local_components = _find_top_level_components(node.nodes)
+ render = _GenerateRenderMethod(printer, declared, undeclared, components + local_components, name="render_%s" % node.name, args=node.function_decl.get_argument_expressions())
for n in node.nodes:
n.accept_visitor(render)
printer.writeline("return ''")
@@ -51,44 +58,9 @@ class Compiler(object):
return buf.getvalue()
- def _get_declared(self, nodes, declared=None, undeclared=None):
- if declared is None:
- declared = util.Set()
- else:
- declared = util.Set(declared)
- if undeclared is None:
- undeclared = util.Set()
- else:
- undeclared = util.Set(undeclared)
- def check_declared(node):
- for ident in node.declared_identifiers():
- declared.add(ident)
- for ident in node.undeclared_identifiers():
- if ident != 'context' and ident not in declared:
- undeclared.add(ident)
- class FindUndeclared(object):
- def visitExpression(self, node):
- check_declared(node)
- def visitControlLine(self, node):
- check_declared(node)
- def visitCode(self, node):
- if not node.ismodule:
- check_declared(node)
- def visitComponentTag(self, node):
- check_declared(node)
- def visitIncludeTag(self, node):
- # TODO: expressions for attributes
- pass
- def visitNamespaceTag(self, node):
- # TODO: expressions for attributes
- pass
- fd = FindUndeclared()
- for n in nodes:
- n.accept_visitor(FindUndeclared())
- return (declared, undeclared)
-
+
class _GenerateRenderMethod(object):
- def __init__(self, printer, undeclared, name='render', in_component=False, args=None):
+ def __init__(self, printer, declared, undeclared, components, name='render', in_component=False, args=None):
self.printer = printer
self.in_component = in_component
self.last_source_line = -1
@@ -97,43 +69,137 @@ class _GenerateRenderMethod(object):
else:
args = [a for a in ['context'] + args]
printer.writeline("def %s(%s):" % (name, ','.join(args)))
+
+ self.write_variable_declares(declared, undeclared, components)
+
+ def write_variable_declares(self, declared, undeclared, components):
+ comp_idents = dict([(c.name, c) for c in components])
for ident in undeclared:
- printer.writeline("%s = context.get(%s, None)" % (ident, repr(ident)))
- def writeSourceComment(self, node):
+ if ident in comp_idents:
+ comp = comp_idents[ident]
+ if comp.is_root():
+ self.write_component_decl(comp)
+ else:
+ self.write_inline_component(comp, declared.union(undeclared), None)
+ else:
+ self.printer.writeline("%s = context.get(%s, None)" % (ident, repr(ident)))
+
+ def write_source_comment(self, node):
if self.last_source_line != node.lineno:
self.printer.writeline("# SOURCE LINE %d" % node.lineno, is_comment=True)
self.last_source_line = node.lineno
+
+ def write_component_decl(self, node):
+ funcname = node.function_decl.funcname
+ namedecls = node.function_decl.get_argument_expressions()
+ nameargs = node.function_decl.get_argument_expressions(include_defaults=False)
+ nameargs.insert(0, 'context')
+ self.printer.writeline("def %s(%s):" % (funcname, ",".join(namedecls)))
+ self.printer.writeline("return render_%s(%s)" % (funcname, ",".join(nameargs)))
+ self.printer.writeline(None)
+
+ def write_inline_component(self, node, declared, undeclared):
+ namedecls = node.function_decl.get_argument_expressions()
+ self.printer.writeline("def %s(%s):" % (node.name, ",".join(namedecls)))
+ components = _find_top_level_components(node.nodes)
+ (declared, undeclared) = _find_declared_identifiers(node.nodes, declared, undeclared)
+ self.write_variable_declares(declared, undeclared, components)
+ for n in node.nodes:
+ n.accept_visitor(self)
+ self.printer.writeline("return ''")
+ self.printer.writeline(None)
+
def visitExpression(self, node):
- self.writeSourceComment(node)
+ self.write_source_comment(node)
self.printer.writeline("context.write(unicode(%s))" % node.text)
def visitControlLine(self, node):
if node.isend:
self.printer.writeline(None)
else:
- self.writeSourceComment(node)
+ self.write_source_comment(node)
self.printer.writeline(node.text)
def visitText(self, node):
- self.writeSourceComment(node)
+ self.write_source_comment(node)
self.printer.writeline("context.write(%s)" % repr(node.content))
def visitCode(self, node):
if not node.ismodule:
- self.writeSourceComment(node)
+ self.write_source_comment(node)
self.printer.write_indented_block(node.text)
def visitIncludeTag(self, node):
- self.writeSourceComment(node)
+ self.write_source_comment(node)
self.printer.writeline("context.include_file(%s, import=%s)" % (repr(node.attributes['file']), repr(node.attributes.get('import', False))))
def visitNamespaceTag(self, node):
- pass
- def visitComponentTag(self, node):
- self.writeSourceComment(node)
- funcname = node.function_decl.funcname
- namedecls = node.function_decl.get_argument_expressions()
- nameargs = node.function_decl.get_argument_expressions(include_defaults=False)
- nameargs.insert(0, 'context')
- self.printer.writeline("def %s(%s):" % (funcname, ",".join(namedecls)))
- self.printer.writeline("return render_%s(%s)" % (funcname, ",".join(nameargs)))
+ self.write_source_comment(node)
+ self.printer.writeline("def make_namespace():")
+ export = []
+ class NSComponentVisitor(object):
+ def visitComponentTag(s, node):
+ self.write_inline_component(node, None, None)
+ export.append(node.name)
+ vis = NSComponentVisitor()
+ for n in node.nodes:
+ n.accept_visitor(vis)
+ self.printer.writeline("return %s" % (repr(export)))
+ self.printer.writeline(None)
+ self.printer.writeline("class %sNamespace(runtime.Namespace):" % node.name)
+ self.printer.writeline("def __getattr__(self, key):")
+ self.printer.writeline("return self.contextual_callable(context, key)")
+ self.printer.writeline(None)
self.printer.writeline(None)
+ self.printer.writeline("%s = %sNamespace(%s, callables=make_namespace())" % (node.name, node.name))
+
+ def visitComponentTag(self, node):
+ pass
def visitCallTag(self, node):
pass
def visitInheritTag(self, node):
pass
+
+def _find_top_level_components(nodes):
+ components = []
+ class FindTopLevelComponents(object):
+ def visitComponentTag(self, node):
+ components.append(node)
+ ftl = FindTopLevelComponents()
+ if isinstance(nodes, list):
+ for n in nodes:
+ n.accept_visitor(ftl)
+ else:
+ nodes.accept_visitor(ftl)
+ return components
+
+def _find_declared_identifiers(nodes, declared=None, undeclared=None):
+ if declared is None:
+ declared = util.Set()
+ else:
+ declared = util.Set(declared)
+ if undeclared is None:
+ undeclared = util.Set()
+ else:
+ undeclared = util.Set(undeclared)
+ def check_declared(node):
+ for ident in node.declared_identifiers():
+ declared.add(ident)
+ for ident in node.undeclared_identifiers():
+ if ident != 'context' and ident not in declared:
+ undeclared.add(ident)
+ class FindUndeclared(object):
+ def visitExpression(self, node):
+ check_declared(node)
+ def visitControlLine(self, node):
+ check_declared(node)
+ def visitCode(self, node):
+ if not node.ismodule:
+ check_declared(node)
+ def visitComponentTag(self, node):
+ pass
+ #check_declared(node)
+ def visitIncludeTag(self, node):
+ # TODO: expressions for attributes
+ pass
+ def visitNamespaceTag(self, node):
+ check_declared(node)
+ fd = FindUndeclared()
+ for n in nodes:
+ n.accept_visitor(FindUndeclared())
+ return (declared, undeclared)
diff --git a/lib/mako/context.py b/lib/mako/context.py
deleted file mode 100644
index a9d8ece..0000000
--- a/lib/mako/context.py
+++ /dev/null
@@ -1,16 +0,0 @@
-"""provides the Context class, the runtime namespace for templates."""
-
-
-class Context(object):
- """provides runtime namespace and output buffer for templates."""
- def __init__(self, buffer, **data):
- self.buffer = buffer
- self.data = data
- # the Template instance currently rendering with this context.
- self.with_template = None
- def __getitem__(self, key):
- return self.data[key]
- def get(self, key, default=None):
- return self.data.get(key, default)
- def write(self, string):
- self.buffer.write(string) \ No newline at end of file
diff --git a/lib/mako/lexer.py b/lib/mako/lexer.py
index 81abdf9..2dfa5d1 100644
--- a/lib/mako/lexer.py
+++ b/lib/mako/lexer.py
@@ -93,7 +93,23 @@ class Lexer(object):
return self.template
def match_tag_start(self):
- match = self.match(r'''\<%(\w+)\s+(.+?["'])?\s*(/)?>''', re.I | re.S )
+ match = self.match(r'''
+ \<% # opening tag
+
+ (\w+) # keyword
+
+ \s+ # some space
+
+ ((?:\w+|=|".*?"|'.*?')*) # attrname, = sign, string expression
+
+ \s* # more whitespace
+
+ (/)?> # closing
+
+ ''',
+
+ re.I | re.S | re.X)
+
if match:
(keyword, attr, isend) = (match.group(1).lower(), match.group(2), match.group(3))
self.keyword = keyword
@@ -102,7 +118,6 @@ class Lexer(object):
for att in re.findall(r"\s*(\w+)\s*=\s*(?:'([^']*)'|\"([^\"]*)\")", attr):
(key, val1, val2) = att
attributes[key] = val1 or val2
-
self.append_node(parsetree.Tag, keyword, attributes)
if isend:
self.tag.pop()
diff --git a/lib/mako/parsetree.py b/lib/mako/parsetree.py
index 40e7807..34dbf77 100644
--- a/lib/mako/parsetree.py
+++ b/lib/mako/parsetree.py
@@ -140,7 +140,7 @@ class _TagMeta(type):
return type.__call__(cls, keyword, attributes, **kwargs)
class Tag(Node):
- """base class for tags.
+ """abstract base class for tags.
<%sometag/>
@@ -150,27 +150,76 @@ class Tag(Node):
"""
__metaclass__ = _TagMeta
__keyword__ = None
- def __init__(self, keyword, attributes, **kwargs):
+ def __init__(self, keyword, attributes, expressions, nonexpressions, required, **kwargs):
+ """construct a new Tag instance.
+
+ this constructor not called directly, and is only called by subclasses.
+
+ keyword - the tag keyword
+
+ attributes - raw dictionary of attribute key/value pairs
+
+ expressions - a util.Set of identifiers that are legal attributes, which can also contain embedded expressions
+
+ nonexpressions - a util.Set of identifiers that are legal attributes, which cannot contain embedded expressions
+
+ **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.lineno, self.pos)
self.parent = None
self.nodes = []
def is_root(self):
return self.parent is None
def get_children(self):
return self.nodes
+ def _parse_attributes(self, expressions, nonexpressions):
+ undeclared_identifiers = util.Set()
+ self.parsed_attributes = {}
+ for key in self.attributes:
+ if key in expressions:
+ expr = []
+ for x in re.split(r'(\${.+?})', self.attributes[key]):
+ m = re.match(r'^\${(.+?)}$', x)
+ if m:
+ code = ast.PythonCode(m.group(1), self.lineno, self.pos)
+ undeclared_identifiers = undeclared_identifiers.union(code.undeclared_identifiers)
+ expr.append(m.group(1))
+ else:
+ expr.append(repr(x))
+ self.parsed_attributes[key] = " + ".join(expr)
+ 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.lineno, self.pos)
+ self.parsed_attributes[key] = repr(self.attributes[key])
+ else:
+ raise exceptions.CompileException("Invalid attribute for tag '%s': '%s'" %(self.keyword, key), self.lineno, self.pos)
def __repr__(self):
return "%s(%s, %s, %s, %s)" % (self.__class__.__name__, repr(self.keyword), repr(self.attributes), repr((self.lineno, self.pos)), repr([repr(x) for x in self.nodes]))
class IncludeTag(Tag):
__keyword__ = 'include'
+ def __init__(self, keyword, attributes, **kwargs):
+ super(IncludeTag, self).__init__(keyword, attributes, util.Set(['file', 'import']), util.Set([]), util.Set(['file']), **kwargs)
+
class NamespaceTag(Tag):
__keyword__ = 'namespace'
+ def __init__(self, keyword, attributes, **kwargs):
+ super(NamespaceTag, self).__init__(keyword, attributes, util.Set(['file']), util.Set(['name']), util.Set(['name']), **kwargs)
+ self.name = attributes['name']
+ def declared_identifiers(self):
+ return [self.name]
+ def undeclared_identifiers(self):
+ return []
+
class ComponentTag(Tag):
__keyword__ = 'component'
def __init__(self, keyword, attributes, **kwargs):
- super(ComponentTag, self).__init__(keyword, attributes, **kwargs)
+ super(ComponentTag, self).__init__(keyword, attributes, util.Set([]), util.Set(['name']), util.Set(['name']), **kwargs)
name = attributes['name']
if re.match(r'^[\w_]+$',name):
name = name + "()"
@@ -183,9 +232,19 @@ class ComponentTag(Tag):
for c in self.function_decl.defaults:
res += list(ast.PythonCode(c, self.lineno, self.pos).undeclared_identifiers)
return res
+
class CallTag(Tag):
__keyword__ = 'call'
+ def __init__(self, keyword, attributes, **kwargs):
+ super(CallTag, self).__init__(keyword, attributes, util.Set([]), util.Set(['expr']), util.Set(['expr']), **kwargs)
+
class InheritTag(Tag):
__keyword__ = 'inherit'
+ def __init__(self, keyword, attributes, **kwargs):
+ super(InheritTag, self).__init__(keyword, attributes, util.Set(['file']), util.Set([]), util.Set(['file']), **kwargs)
+
class PageTag(Tag):
- __keyword__ = 'page' \ No newline at end of file
+ __keyword__ = 'page'
+ def __init__(self, keyword, attributes, **kwargs):
+ super(PageTag, self).__init__(keyword, attributes, util.Set([]), util.Set([]), util.Set([]), **kwargs)
+ \ No newline at end of file
diff --git a/lib/mako/runtime.py b/lib/mako/runtime.py
new file mode 100644
index 0000000..76936c8
--- /dev/null
+++ b/lib/mako/runtime.py
@@ -0,0 +1,46 @@
+"""provides the Context class, the runtime namespace for templates."""
+from mako import exceptions
+
+class Context(object):
+ """provides runtime namespace and output buffer for templates."""
+ def __init__(self, buffer, **data):
+ self.buffer = buffer
+ self.data = data
+ # the Template instance currently rendering with this context.
+ self.with_template = None
+ def __getitem__(self, key):
+ return self.data[key]
+ def get(self, key, default=None):
+ return self.data.get(key, default)
+ def write(self, string):
+ self.buffer.write(string)
+
+
+class Namespace(object):
+ """provides access to collections of rendering methods, which can be local, from other templates, or from imported modules"""
+ def __init__(self, name, module=None, template=None, callables=None):
+ self.module = module
+ self.template = template
+ self.callables = callables
+
+ def load(self, key):
+ if self.callables is not None:
+ try:
+ return self.callables[key]
+ except KeyError:
+ pass
+ if self.template is not None:
+ try:
+ return self.template.get_component(key)
+ except AttributeError:
+ pass
+ if self.module is not None:
+ try:
+ return getattr(self.module, key)
+ except AttributeError:
+ pass
+ raise exceptions.RuntimeException("Namespace '%s' has no member '%s'" % (self.name, key))
+
+ def contextual_callable(self, context, key):
+ return lambda context, *args, **kwargs:self.load(key)(context, *args, **kwargs)
+ \ No newline at end of file
diff --git a/lib/mako/template.py b/lib/mako/template.py
index 9df070f..08d925f 100644
--- a/lib/mako/template.py
+++ b/lib/mako/template.py
@@ -3,7 +3,7 @@ as well as template runtime operations."""
from mako.lexer import Lexer
from mako.codegen import Compiler
-from mako.context import Context
+from mako.runtime import Context
import imp, time, inspect, weakref, sys
from StringIO import StringIO
@@ -80,11 +80,11 @@ class ComponentTemplate(Template):
def _compile_text(text, identifier, filename):
node = Lexer(text).parse()
source = Compiler(node).render()
+ #print source
cid = identifier
module = imp.new_module(cid)
code = compile(source, filename or cid, 'exec')
exec code in module.__dict__, module.__dict__
- module._modified_time = time.time()
return (source, module)
def _render(template, callable_, *args, **data):