:license: BSD, see LICENSE for more details.
"""
from jinja.environment import Environment
-from jinja.template import Template
+from jinja.loaders import FileSystemLoader
-__all__ = ['Environment', 'Template']
+__all__ = ['Environment', 'FileSystemLoader']
Undefined = UndefinedType()
+class Deferred(object):
+ """
+ Object marking an deferred value. Deferred objects are
+ objects that are called first access in the context.
+ """
+
+ def __init__(self, factory):
+ self.factory = factory
+
+ def __call__(self, context, name):
+ return self.factory(context, name)
+
+
class Context(object):
"""
Dict like object.
raise TypeError('%r requires environment as first argument. '
'The rest of the arguments are forwarded to '
'the default dict constructor.')
- self._stack = [initial, {}]
- self.globals, self.current = self._stack
+ self._stack = [self.environment.globals, initial, {}]
+ self.globals, _, self.current = self._stack
def pop(self):
if len(self._stack) <= 2:
self._stack.append(data or {})
self.current = self._stack[-1]
+ def to_dict(self):
+ """
+ Convert the context into a dict. This skips the globals.
+ """
+ result = {}
+ for layer in self._stack[1:]:
+ for key, value in layer.iteritems():
+ result[key] = value
+ return result
+
def __getitem__(self, name):
# don't give access to jinja internal variables
if name.startswith('::'):
return Undefined
for d in _reversed(self._stack):
if name in d:
- return d[name]
+ rv = d[name]
+ if isinstance(rv, Deferred):
+ d[name] = rv = rv(self, name)
+ return rv
return Undefined
def __setitem__(self, name, value):
def __init__(self, generator):
self._generator = generator
self._pushed = []
- self.last = (0, 'initial', '')
+ self.last = (1, 'initial', '')
def __iter__(self):
return self
"""
from jinja.filters import FILTERS as DEFAULT_FILTERS
from jinja.tests import TESTS as DEFAULT_TESTS
+
+
+DEFAULT_NAMESPACE = {
+ 'range': xrange
+}
"""
from jinja.lexer import Lexer
from jinja.parser import Parser
+from jinja.loaders import LoaderWrapper
from jinja.datastructure import Undefined
from jinja.exceptions import FilterNotFound, TestNotFound
-from jinja.defaults import DEFAULT_FILTERS, DEFAULT_TESTS
+from jinja.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
class Environment(object):
comment_end_string='#}',
template_charset='utf-8',
charset='utf-8',
+ namespace=None,
loader=None,
filters=None,
tests=None):
self.template_charset = template_charset
self.charset = charset
self.loader = loader
- self.filters = filters or DEFAULT_FILTERS.copy()
- self.tests = filters or DEFAULT_TESTS.copy()
+ self.filters = filters is None and DEFAULT_FILTERS.copy() or filters
+ self.tests = tests is None and DEFAULT_TESTS.copy() or tests
+
+ # global namespace
+ self.globals = namespace is None and DEFAULT_NAMESPACE.copy() \
+ or namespace
# create lexer
self.lexer = Lexer(self)
+ def loader(self, value):
+ """
+ Get or set the template loader.
+ """
+ self._loader = LoaderWrapper(self, value)
+ loader = property(lambda s: s._loader, loader, loader.__doc__)
+
def parse(self, source):
"""Function that creates a new parser and parses the source."""
parser = Parser(self, source)
return parser.parse_page()
+ def from_string(self, source):
+ """Load a template from a string."""
+ from jinja.parser import Parser
+ from jinja.translators.python import PythonTranslator
+ return PythonTranslator.process(self, Parser(self, source).parse())
+
def to_unicode(self, value):
"""
Convert a value to unicode with the rules defined on the environment.
KeyError.__init__(self, message)
+class TemplateNotFound(IOError, TemplateError):
+ """
+ Raised if a template does not exist.
+ """
+
+ def __init__(self, message):
+ IOError.__init__(self, message)
+
+
class TemplateSyntaxError(SyntaxError, TemplateError):
"""
Raised to tell the user that there is a problem with the template.
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+ jinja.loaders
+ ~~~~~~~~~~~~~
+
+ Jinja loader classes.
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import codecs
+from os import path
+from jinja.parser import Parser
+from jinja.translators.python import PythonTranslator
+from jinja.exceptions import TemplateNotFound
+
+
+def get_template_filename(searchpath, name):
+ """
+ Return the filesystem filename wanted.
+ """
+ return path.join(searchpath, path.sep.join([p for p in name.split('/')
+ if p and p[0] != '.']))
+
+
+class LoaderWrapper(object):
+ """
+ Wraps a loader so that it's bound to an environment.
+ """
+
+ def __init__(self, environment, loader):
+ self.environment = environment
+ self.loader = loader
+
+ def get_source(self, name, parent=None):
+ """
+ Retrieve the sourcecode of a template.
+ """
+ return self.loader.get_source(self.environment, name, parent)
+
+ def parse(self, name, parent=None):
+ """
+ Retreive a template and parse it.
+ """
+ return self.loader.parse(self.environment, name, parent)
+
+ def load(self, name, translator=PythonTranslator):
+ """
+ Translate a template and return it. This must not necesarily
+ be a template class. The javascript translator for example
+ will just output a string with the translated code.
+ """
+ return self.loader.load(self.environment, name, translator)
+
+
+class FileSystemLoader(object):
+ """
+ Loads templates from the filesystem::
+
+ from jinja import Environment, FileSystemLoader
+ e = Environment(loader=FileSystemLoader('templates/'))
+ """
+
+ def __init__(self, searchpath, use_cache=False, cache_size=40):
+ self.searchpath = searchpath
+ self.use_cache = use_cache
+ self.cache_size = cache_size
+ self.cache = {}
+
+ def get_source(self, environment, name, parent):
+ filename = get_template_filename(self.searchpath, name)
+ if path.exists(filename):
+ f = codecs.open(filename, 'r', environment.template_charset)
+ try:
+ return f.read()
+ finally:
+ f.close()
+ else:
+ raise TemplateNotFound(name)
+
+ def parse(self, environment, name, parent):
+ source = self.get_source(environment, name, parent)
+ return Parser(environment, source, name).parse()
+
+ def load(self, environment, name, translator):
+ if self.use_cache:
+ key = (name, translator)
+ if key in self.cache:
+ return self.cache[key]
+ if len(self.cache) >= self.cache_size:
+ self.cache.clear()
+ rv = translator.process(environment, self.parse(environment, name, None))
+ if self.use_cache:
+ self.cache[key] = rv
+ return rv
jinja node.
"""
+ def get_items(self):
+ return []
+
def getChildren(self):
return self.get_items()
self.lineno = lineno
list.__init__(self, data or ())
- getChildren = getChildNodes = lambda s: list(s)
+ getChildren = getChildNodes = lambda s: list(s) + s.get_items()
def __repr__(self):
return '%s(%s)' % (
class Template(NodeList):
"""
- A template.
+ Node that represents a template.
"""
- def __init__(self, filename, node):
- if node.__class__ is not NodeList:
- node = (node,)
- NodeList.__init__(self, 0, node)
+ def __init__(self, filename, body, blocks, extends):
+ if body.__class__ is not NodeList:
+ body = (body,)
+ NodeList.__init__(self, 0, body)
+ self.blocks = blocks
+ self.extends = extends
set_filename(filename, self)
+ def get_items(self):
+ result = self.blocks.values()
+ if self.extends is not None:
+ result.append(self.extends)
+ return result
+
class ForLoop(Node):
"""
self.arguments,
self.body
)
+
+
+class Block(Node):
+ """
+ A node that represents a block.
+ """
+
+ def __init__(self, lineno, name, body):
+ self.lineno = lineno
+ self.name = name
+ self.body = body
+
+ def replace(self, node):
+ """
+ Replace the current data with the data of another block node.
+ """
+ assert node.__class__ is Block
+ self.__dict__.update(node.__dict__)
+
+ def get_items(self):
+ return [self.name, self.body]
+
+ def __repr__(self):
+ return 'Block(%r, %r)' % (
+ self.name,
+ self.body
+ )
+
+
+class Extends(Node):
+ """
+ A node that represents the extends tag.
+ """
+
+ def __init__(self, lineno, template):
+ self.lineno = lineno
+ self.template = template
+
+ def __repr__(self):
+ return 'Extends(%r)' % self.template
switch_if = lambda p, t, d: t == 'name' and d in ('else', 'elif', 'endif')
end_of_if = lambda p, t, d: t == 'name' and d == 'endif'
end_of_macro = lambda p, t, d: t == 'name' and d == 'endmacro'
+end_of_block_tag = lambda p, t, d: t == 'name' and d == 'endblock'
class Parser(object):
"""
The template parser class.
- Transforms sourcecode into an abstract syntax tree::
-
- >>> parse("{% for item in seq|reversed %}{{ item }}{% endfor %}")
- Document(ForLoop(AssignName('item'), Filter(Name('seq'), Name('reversed')),
- Print('item'), None))
- >>> parse("{% if true %}foo{% else %}bar{% endif %}")
- Document(IfCondition(Name('true'), Data('foo'), Data('bar')))
- >>> parse("{% if false %}...{% elif 0 > 1 %}...{% else %}...{% endif %}")
- Document(IfCondition(Name('false'), Data('...'),
- IfCondition(Compare('>', Const(0), Const(1)),
- Data('...'), Data('...'))))
+ Transforms sourcecode into an abstract syntax tree.
"""
def __init__(self, environment, source, filename=None):
self.source = source
self.filename = filename
self.tokenstream = environment.lexer.tokenize(source)
- self._parsed = False
+
+ self.extends = None
+ self.blocks = {}
self.directives = {
'for': self.handle_for_directive,
'if': self.handle_if_directive,
'cycle': self.handle_cycle_directive,
'print': self.handle_print_directive,
- 'macro': self.handle_macro_directive
+ 'macro': self.handle_macro_directive,
+ 'block': self.handle_block_directive,
+ 'extends': self.handle_extends_directive
}
def handle_for_directive(self, lineno, gen):
defaults = [None] * (len(ast.argnames) - len(ast.defaults)) + ast.defaults
return nodes.Macro(lineno, ast.name, zip(ast.argnames, defaults), body)
+ def handle_block_directive(self, lineno, gen):
+ """
+ Handle block directives used for inheritance.
+ """
+ tokens = list(gen)
+ if not tokens:
+ raise TemplateSyntaxError('block requires a name', lineno)
+ block_name = tokens.pop(0)
+ if block_name[1] != 'name':
+ raise TemplateSyntaxError('expected \'name\', got %r' %
+ block_name[1], lineno)
+ if tokens:
+ print tokens
+ raise TemplateSyntaxError('block got too many arguments, '
+ 'requires one.', lineno)
+
+ if block_name[2] in self.blocks:
+ raise TemplateSyntaxError('block %r defined twice' %
+ block_name[2], lineno)
+
+ body = self.subparse(end_of_block_tag, True)
+ self.close_remaining_block()
+ rv = nodes.Block(lineno, block_name[2], body)
+ self.blocks[block_name[2]] = rv
+ return rv
+
+ def handle_extends_directive(self, lineno, gen):
+ """
+ Handle extends directive used for inheritance.
+ """
+ tokens = list(gen)
+ if len(tokens) != 1 or tokens[0][1] != 'string':
+ raise TemplateSyntaxError('extends requires a string', lineno)
+ if self.extends is not None:
+ raise TemplateSyntaxError('extends called twice', lineno)
+ self.extends = nodes.Extends(lineno, tokens[0][2][1:-1])
+
def parse_python(self, lineno, gen, template='%s'):
"""
Convert the passed generator into a flat string representing
try:
ast = parse(source, 'exec')
except SyntaxError, e:
- raise TemplateSyntaxError(str(e), lineno + e.lineno - 1)
+ raise TemplateSyntaxError('invalid syntax', lineno + e.lineno)
assert len(ast.node.nodes) == 1, 'get %d nodes, 1 expected' % len(ast.node.nodes)
result = ast.node.nodes[0]
nodes.inc_lineno(lineno, result)
def parse(self):
"""
- Parse the template and return a Template.
+ Parse the template and return a Template node.
"""
- return nodes.Template(self.filename, self.subparse(None))
+ body = self.subparse(None)
+ return nodes.Template(self.filename, body, self.blocks, self.extends)
def subparse(self, test, drop_needle=False):
"""
node = self.directives[data](lineno, gen)
else:
raise TemplateSyntaxError('unknown directive %r' % data, lineno)
- result.append(node)
+ # some tags like the extends tag do not output nodes.
+ # so just skip that.
+ if node is not None:
+ result.append(node)
# here the only token we should get is "data". all other
# tokens just exist in block or variable sections. (if the
+++ /dev/null
-# -*- coding: utf-8 -*-
-"""
- jinja.template
- ~~~~~~~~~~~~~~
-
- Template class.
-
- :copyright: 2006 by Armin Ronacher.
- :license: BSD, see LICENSE for more details.
-
-"""
-from jinja.nodes import Node
-from jinja.datastructure import Context
-from jinja.translators.python import parse_and_translate, translate
-
-
-def evaluate_source(source):
- """
- Evaluate a sourcecode and return the generate function.
- """
- ns = {}
- exec source in ns
- return ns['generate']
-
-
-class Template(object):
- """
- Represents a finished template.
- """
-
- def __init__(self, environment, source):
- if isinstance(source, basestring):
- self.source = parse_and_translate(environment, source)
- elif isinstance(source, Node):
- self.source = translate(environment, source)
- else:
- raise TypeError('unsupported source type %r' %
- source.__class__.__name__)
- self.environment = environment
- self.generate_func = None
-
- def render(self, *args, **kwargs):
- """
- Render a template.
- """
- if self.generate_func is None:
- self.generate_func = evaluate_source(self.source)
- result = []
- ctx = Context(self.environment, *args, **kwargs)
- self.generate_func(ctx, result.append)
- return u''.join(result)
:copyright: 2006 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
+
+
+class Translator(object):
+ """
+ Base class of all translators.
+ """
+
+ def process(environment, tree):
+ """
+ Process the given ast with the rules defined in
+ environment and return a translated version of it.
+ The translated object can be anything. The python
+ translator for example outputs Template instances,
+ a javascript translator would probably output strings.
+
+ This is a static function.
+ """
+ pass
+ process = staticmethod(process)
from compiler import ast
from jinja import nodes
from jinja.parser import Parser
+from jinja.datastructure import Context
from jinja.exceptions import TemplateSyntaxError
+from jinja.translators import Translator
-class PythonTranslator(object):
+class Template(object):
+ """
+ Represents a finished template.
+ """
+
+ def __init__(self, environment, generate_func):
+ self.environment = environment
+ self.generate_func = generate_func
+
+ def render(self, *args, **kwargs):
+ """
+ Render a template.
+ """
+ result = []
+ ctx = Context(self.environment, *args, **kwargs)
+ self.generate_func(ctx, result.append)
+ return u''.join(result)
+
+
+class PythonTranslator(Translator):
"""
Pass this translator a ast tree to get valid python code.
"""
nodes.Cycle: self.handle_cycle,
nodes.Print: self.handle_print,
nodes.Macro: self.handle_macro,
+ nodes.Block: self.handle_block,
# used python nodes
ast.Name: self.handle_name,
ast.AssName: self.handle_name,
ast.GenExpr: 'generator expressions'
})
- self.reset()
+ # -- public methods
+
+ def process(environment, node):
+ translator = PythonTranslator(environment, node)
+ source = translator.translate()
+ ns = {}
+ exec source in ns
+ return Template(environment, ns['generate'])
+ process = staticmethod(process)
+
+ # -- private methods
def indent(self, text):
"""
def handle_template(self, node):
"""
- Handle a template node. Basically do nothing but calling the
- handle_node_list function.
- """
- return self.handle_node_list(node)
+ Handle the overall template node. This node is the first node and ensures
+ that we get the bootstrapping code. It also knows about inheritance
+ information. It only occours as outer node, never in the tree itself.
+
+ Nevertheless we call indent here to simplify futur changes.
+ """
+ # if there is a parent template we parse the parent template and
+ # update the blocks there. Once this is done we drop the current
+ # template in favor of the new one. Do that until we found the
+ # root template.
+ while node.extends is not None:
+ tmpl = self.environment.loader.parse(node.extends.template,
+ node.filename)
+ for block in tmpl.blocks.itervalues():
+ if block.name in node.blocks:
+ block.replace(node.blocks[block.name])
+ node = tmpl
+
+ lines = [self.indent(
+ 'from jinja.datastructure import Undefined, LoopContext, CycleContext\n'
+ 'def generate(context, write, write_var=None):\n'
+ ' environment = context.environment\n'
+ ' if write_var is None:\n'
+ ' write_var = lambda x: write(environment.finish_var(x))'
+ )]
+ write = lambda x: lines.append(self.indent(x))
+ self.indention += 1
+ lines.append(self.handle_node_list(node))
+
+ return '\n'.join(lines)
def handle_template_text(self, node):
"""
return '\n'.join(buf)
+ def handle_block(self, node):
+ """
+ Handle blocks in the sourcecode. We only use them to
+ call the current block implementation that is stored somewhere
+ else.
+ """
+ rv = self.handle_node(node.body)
+ if not rv:
+ return self.indent('# empty block from %r, line %s' % (
+ node.filename or '?',
+ node.lineno
+ ))
+
+ buf = []
+ write = lambda x: buf.append(self.indent(x))
+
+ write('# block from %r, line %s' % (
+ node.filename or '?',
+ node.lineno
+ ))
+ write('context.push()')
+ buf.append(self.handle_node(node.body))
+ write('context.pop()')
+ buf.append(self.indent('# end of block'))
+ return '\n'.join(buf)
+
# -- python nodes
def handle_name(self, node):
return '[%s]' % ':'.join(args)
def reset(self):
- self.indention = 1
+ self.indention = 0
self.last_cycle_id = 0
def translate(self):
- return (
- 'from jinja.datastructure import Undefined, LoopContext, CycleContext\n'
- 'def generate(context, write, write_var=None):\n'
- ' environment = context.environment\n'
- ' if write_var is None:\n'
- ' write_var = lambda x: write(environment.finish_var(x))\n'
- ) + self.handle_node(self.node)
-
-
-def translate(environment, node):
- """
- Do the translation to python.
- """
- return PythonTranslator(environment, node).translate()
-
-
-def parse_and_translate(environment, source, filename=None):
- """
- Parse sourcecode and translate it to python
- """
- node = Parser(environment, source, filename).parse()
- return PythonTranslator(environment, node).translate()
+ self.reset()
+ return self.handle_node(self.node)
+++ /dev/null
-Jinja 1.0 Syntax
-================
-
-<ul>
-{% for char in my_string|upper|replace(" ", "") %}
- <li>{{ loop.index }} - {{ char|e }}</li>
-{% endfor %}
-</ul>
-
-{{ variable|strip(" ")|escape|replace("a", "b") }}
-
-{% if item == 42 %}
- ...
-{% endif %}
-
-{% if item|odd? %}
- applies the odd? filter which returns true if the
- item in is odd.
-{% endif %}
-
-{{ item|e(true) }} -- escape the variable for attributes
-
-<ul>
-{% for item in seq %}
- <li>{{ item.current|e }}
- {% if item.items %}<ul>{% recurse item.items %}</ul>{% endif %}
- </li>
-{% endfor %}
-</ul>
-
-How a Filter Looks Like
-=======================
-
-def replace(search, repl):
- def wrapped(env, value):
- return env.to_unicode(value).replace(search, repl)
- return wrapped
-
-
-def escape(attr=False):
- def wrapped(env, value):
- return cgi.escape(env.to_unicode(value), attr)
- return wrapped
-
-
-def odd():
- return lambda env, value: value % 2 == 1
-odd.__name__ = 'odd?'
-
-
-@stringfilter
-def replace(value, search, repl):
- return value.replace(search, repl)
-
-
-def stringfilter(f):
- def decorator(*args):
- def wrapped(env, value):
- return f(env.to_unicode(value), *args)
- return wrapped
- return decorator
+++ /dev/null
-For Loops
----------
-
-Simple Syntax::
-
- {% for <item> in <seq> %}
- <data>
- {% endfor %}
-
-Item Unpacking::
-
- {% for <item1>, <item2> in <seq> %}
- <data>
- {% endfor %}
-
-With else block::
-
- {% for <item> in <seq> %}
- <data>
- {% else %}
- <data>
- {% endfor %}
+++ /dev/null
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Frameset//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <title>{% block "page_title" %}Untitled{% endblock %} | My Webpage</title>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <link rel="stylesheet" href="screen.css" type="text/css" media="screen" />
- <link rel="stylesheet" href="print.css" type="text/css" media="print" />
- {% block html_head %}{% endblock %}
- </head>
- <body>
- <div id="header">
- <h1>My Webpage</h1>
- <ul id="navigation">
- {% for item in page_navigation %}
- <li><a href="{{ item.url|e }}"{% if item.active %}
- class="active"{% endif %}>{{ item.caption|e }}</a></li>
- {% endfor %}
- </ul>
- </div>
- <div id="body">
- {% block body %}
- content goes here.
- {% endblock %}
- </div>
- <div id="sidebar">
- <h2>Who is online?</h2>
- <ul>
- {% for user in online_users %}
- <li class="row_{% cycle 'even', 'odd' %}"><a href="{{ user.url|e }}">{{ user.username|e }}</a></li>
- {% endfor %}
- </ul>
- <h2>Sitemap</h2>
- {% macro draw_sitemap(items) %}
- <ul>
- {% for item in items %}
- <li>{{ item.name|e }}{% if item.items %}{{ draw_sitemap(item.items) }}{% endif %}</li>
- {% endfor %}
- </ul>
- {% endmacro %}
- {{ draw_sitemap(sitemap) }}
- <!-- alternative solution -->
- <ul>
- {% for item in items %}
- <li>{{ item.name|e }}{% if item.items %}<ul>{% recurse item.items %}</ul>{% endif %}</li>
- {% endfor %}
- </ul>
- </div>
- <div id="footer">
- © Copyrigh by yourself.
- </div>
- </body>
-</html>
+++ /dev/null
-from jinja.environment import Environment
-
-e = Environment()
-
-def test_lexer(x):
- for pos, token, data in e.lexer.tokenize(x):
- print '%-8d%-30r%-40r' % (pos, token, data)
-
-
-def test_parser(x):
- from jinja.parser import Parser
- from jinja.translators.python import translate
- print translate(e, Parser(e, x).parse())
-
-
-def load_template(x):
- from jinja.template import Template
- return Template(e, x)
--- /dev/null
+from jinja import Environment, FileSystemLoader
+e = Environment(loader=FileSystemLoader('templates'))
+
+from jinja.parser import Parser
+from jinja.translators.python import PythonTranslator
+
+print PythonTranslator(e, e.loader.parse('index.html')).translate()
+++ /dev/null
-source index.html::
-
- {% page extends='layout.html', defaultfilter='autoescape',
- charset='utf-8' %}
-
- {% def title %}Index | {{ super.title }}{% enddef %}
- {% def body %}
- <h1>Index</h1>
- <p>
- This is just a mockup template so that you can see how the new
- jinja syntax looks like. It also shows the python output of this
- template when compiled.
- </p>
- <ul>
- {% for user in users %}
- <li class="{% cycle 'row1', 'row2' %}">{{ user.username }}</li>
- {% endfor %}
- </ul>
- {% enddef %}
-
-source layout.html::
-
- {% page defaultfilter='autoescape', charset='utf-8' %}
- <html>
- <head>
- <title>{% block title %}My Webpage{% endblock %}</title>
- </head>
- <body>
- <div class="header">
- <h1>My Webpage</h1>
- <h2>{{ self.title }}</h2>
- </div>
- <div class="body">
- {{ self.body }}
- </div>
- <div class="footer">
- {% block footer %}
- Copyright 2007 by the Pocoo Team.
- {% endblock %}
- </div>
- </body>
- </html>
-
-generated python code::
-
- from jinja.runtime import Template, Markup
-
- class Layer1Template(Template):
-
- defaultfilter = 'autoescape'
-
- def p_title(self):
- yield Markup(u'My Webpage')
-
- def p_footer(self):
- yield Markup(u'\n Copyright 2007 by the Pocoo Team.\n ')
-
- def p_outer_body(self):
- yield Markup(u'<html>\n <head>\n <title>\n')
- for i in self.resolve('self.title'):
- yield i
- yield Markup(u'</title>\n </head>\n <body>\n <div class="header">\n'
- u' <h1>My Webpage</h1>\n <h2>')
- for i in self.resolve('self.title'):
- yield i
- yield Markup(u'</h2>\n </div>\n <div class="body">\n ')
- for i in self.resolve('self.body'):
- yield i
- yield Markup(u'\n </div>\n <div class="footer">\n ')
- for i in self.resolve('self.footer'):
- yield i
- yield Markup(u'\n </div>\n </body>\n</html')
-
- class Layer2Template(Layer1Template):
-
- defaultfilter = 'autoescape'
-
- def p_title(self):
- yield Markup(u'Index | ')
- for i in self.resolve('super.title'):
- yield i
-
- def p_body(self):
- yield Markup(u'\n <h1>Index</h1>\n <p>\n This is just a mockup '
- u'template so that you can see how the new jinja syntax '
- u'looks like. It also shows the python output of this '
- u'template when compiled.\n </p>\n <ul>\n ')
- iteration_1 = self.start_iteration(['user'], 'users')
- for _ in iteration_1:
- yield Markup(u'\n <li class="')
- for i in self.cycle(u'row1', u'row2'):
- yield i
- yield Markup(u'">')
- for i in self.resolve('user.username'):
- yield i
- yield Markup(u'</li>\n ')
- iteration_1.close()
-
- def get_template(context):
- return Layer2Template(context)
-
-generated javascript code::
-
- (function(c) {
- var m = function(x) { return new Jinja.Markup(x) };
-
- var l1 = function() {};
- l1.defaultfilter = 'autoescape';
- l1.prototype.p_title = function() {
- this.yield(m('My Webpage'));
- };
- l1.prototype.p_footer = function() {
- this.yield(m('\n Copyright 2007 by the Pocoo Team.\n '));
- };
- l1.prototype.p_outer_body = function() {
- this.yield(m('<html>\n <head>\n <title>\n'));
- this.resolve('self.title');
- this.yield(m('</title>\n </head>\n <body>\n <div class="header">\n'
- ' <h1>My Webpage</h1>\n <h2>'));
- this.resolve('self.title');
- this.yield(m('</h2>\n </div>\n <div class="body">\n '));
- this.resolve('self.body');
- this.yield(m('\n </div>\n <div class="footer">\n '));
- this.resolve('self.footer');
- this.yield(m('\n </div>\n </body>\n</html'));
- };
-
- var l2 = function() {};
- l2.defaultfilter = 'autoescape';
- l2.prototype.p_title = function() {
- this.yield(m('Index | '));
- this.resolve('super.title');
- };
- l2.prototype.p_body = function() {
- this.yield(m('\n <h1>Index</h1>\n <p>\n This is just a mockup '
- 'template so that you can see how the new jinja syntax '
- 'looks like. It also shows the python output of this '
- 'template when compiled.\n </p>\n <ul>\n '));
- var iteration_1 = this.startIteration(['user'], 'users');
- while (iteration_1.next()) {
- this.yield(m('\n <li class="'));
- this.cycle('row1', 'row2');
- this.yield(m('">'));
- this.resolve('user.username');
- this.yield('</li>\n ');
- };
- iteration_1.close();
- };
-
- return function(context) {
- return new Jinja.Template(context, l1, l2);
- };
- })();
+++ /dev/null
-from jinja import Environment, FileSystemLoader
-
-env = Environment(
- loader=FileSystemLoader(['.'], suffix='')
-)
-env.filters['odd?'] = lambda c, x: x % 2 == 1
-env.filters['even?'] = lambda x, x: x % 2 == 0
-
-t = env.get_template('index.html')
-print t.render(
- users=[
- dict(username='foo', user_id=1),
- dict(username='bar', user_id=2)
- ]
-)
--- /dev/null
+{% extends "layout.html" %}
+{% block page_title %}Index{% endblock %}
+{% block body %}
+ This is the index page.
+{% endblock %}
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Frameset//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
+<html>
+ <head>
+ <title>{% block page_title %}Untitled{% endblock %} | My Webpage</title>
+ {% block page_head %}{% endblock %}
+ </head>
+ <body>
+ <div id="header">
+ <h1>My Webpage</h1>
+ <ul id="navigation">
+ {% for item in navigation_items %}
+ <li><a href="{{ item.url|escape }}">{{ item.caption|escape }}</a></li>
+ {% endfor %}
+ </ul>
+ </div>
+ <div id="body">
+ {% block body %}
+ content goes here.
+ {% endblock %}
+ </div>
+ </body>
+</html>