From 9997f96d9028753192b8a88a519268ba7082eb9d Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 28 Feb 2007 20:37:51 +0100 Subject: [PATCH] [svn] checked in today's jinja changes. implemented template loading and inheritance --HG-- branch : trunk --- jinja/__init__.py | 4 +- jinja/datastructure.py | 34 +++++++- jinja/defaults.py | 5 ++ jinja/environment.py | 25 +++++- jinja/exceptions.py | 9 ++ jinja/loaders.py | 96 +++++++++++++++++++++ jinja/nodes.py | 63 ++++++++++++-- jinja/parser.py | 70 ++++++++++++---- jinja/template.py | 51 ------------ jinja/translators/__init__.py | 19 +++++ jinja/translators/python.py | 122 ++++++++++++++++++++------- syntax.html | 61 -------------- syntax.txt | 22 ----- test.html | 53 ------------ test.py | 18 ---- tests/inheritance.py | 7 ++ tests/mockup.txt | 153 ---------------------------------- tests/run.py | 15 ---- tests/templates/index.html | 5 ++ tests/templates/layout.html | 23 +++++ 20 files changed, 421 insertions(+), 434 deletions(-) create mode 100644 jinja/loaders.py delete mode 100644 jinja/template.py delete mode 100644 syntax.html delete mode 100644 syntax.txt delete mode 100644 test.html delete mode 100644 test.py create mode 100644 tests/inheritance.py delete mode 100644 tests/mockup.txt delete mode 100644 tests/run.py create mode 100644 tests/templates/index.html create mode 100644 tests/templates/layout.html diff --git a/jinja/__init__.py b/jinja/__init__.py index 6550d76..f65931c 100644 --- a/jinja/__init__.py +++ b/jinja/__init__.py @@ -7,6 +7,6 @@ :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'] diff --git a/jinja/datastructure.py b/jinja/datastructure.py index c5fe44d..67a4b89 100644 --- a/jinja/datastructure.py +++ b/jinja/datastructure.py @@ -64,6 +64,19 @@ class UndefinedType(object): 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. @@ -78,8 +91,8 @@ class Context(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: @@ -92,13 +105,26 @@ class Context(object): 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): @@ -193,7 +219,7 @@ class TokenStream(object): def __init__(self, generator): self._generator = generator self._pushed = [] - self.last = (0, 'initial', '') + self.last = (1, 'initial', '') def __iter__(self): return self diff --git a/jinja/defaults.py b/jinja/defaults.py index 9cb5291..8c7c949 100644 --- a/jinja/defaults.py +++ b/jinja/defaults.py @@ -10,3 +10,8 @@ """ from jinja.filters import FILTERS as DEFAULT_FILTERS from jinja.tests import TESTS as DEFAULT_TESTS + + +DEFAULT_NAMESPACE = { + 'range': xrange +} diff --git a/jinja/environment.py b/jinja/environment.py index 46cede0..92a7618 100644 --- a/jinja/environment.py +++ b/jinja/environment.py @@ -10,9 +10,10 @@ """ 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): @@ -29,6 +30,7 @@ class Environment(object): comment_end_string='#}', template_charset='utf-8', charset='utf-8', + namespace=None, loader=None, filters=None, tests=None): @@ -45,17 +47,34 @@ class Environment(object): 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. diff --git a/jinja/exceptions.py b/jinja/exceptions.py index ec052ea..d5f2af7 100644 --- a/jinja/exceptions.py +++ b/jinja/exceptions.py @@ -32,6 +32,15 @@ class TestNotFound(KeyError, TemplateError): 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. diff --git a/jinja/loaders.py b/jinja/loaders.py new file mode 100644 index 0000000..d61e671 --- /dev/null +++ b/jinja/loaders.py @@ -0,0 +1,96 @@ +# -*- 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 diff --git a/jinja/nodes.py b/jinja/nodes.py index 2109b67..ebca43a 100644 --- a/jinja/nodes.py +++ b/jinja/nodes.py @@ -31,6 +31,9 @@ class Node(ast.Node): jinja node. """ + def get_items(self): + return [] + def getChildren(self): return self.get_items() @@ -63,7 +66,7 @@ class NodeList(list, Node): 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)' % ( @@ -74,15 +77,23 @@ class NodeList(list, Node): 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): """ @@ -190,3 +201,43 @@ class Macro(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 diff --git a/jinja/parser.py b/jinja/parser.py index 49f0fa2..c4a4728 100644 --- a/jinja/parser.py +++ b/jinja/parser.py @@ -24,23 +24,14 @@ end_of_for = lambda p, t, d: t == 'name' and d == 'endfor' 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): @@ -50,14 +41,18 @@ class Parser(object): 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): @@ -163,6 +158,43 @@ class Parser(object): 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 @@ -179,7 +211,7 @@ class Parser(object): 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) @@ -187,9 +219,10 @@ class Parser(object): 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): """ @@ -247,7 +280,10 @@ class Parser(object): 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 diff --git a/jinja/template.py b/jinja/template.py deleted file mode 100644 index 780caaa..0000000 --- a/jinja/template.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- 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) diff --git a/jinja/translators/__init__.py b/jinja/translators/__init__.py index b92b79e..f8576bc 100644 --- a/jinja/translators/__init__.py +++ b/jinja/translators/__init__.py @@ -9,3 +9,22 @@ :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) diff --git a/jinja/translators/python.py b/jinja/translators/python.py index ed312a8..dde6364 100644 --- a/jinja/translators/python.py +++ b/jinja/translators/python.py @@ -11,10 +11,31 @@ 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. """ @@ -40,6 +61,7 @@ class PythonTranslator(object): 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, @@ -78,7 +100,17 @@ class PythonTranslator(object): 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): """ @@ -104,10 +136,36 @@ class PythonTranslator(object): 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): """ @@ -252,6 +310,32 @@ class PythonTranslator(object): 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): @@ -532,29 +616,9 @@ class PythonTranslator(object): 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) diff --git a/syntax.html b/syntax.html deleted file mode 100644 index bc10e08..0000000 --- a/syntax.html +++ /dev/null @@ -1,61 +0,0 @@ -Jinja 1.0 Syntax -================ - - - -{{ 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 - - - -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 diff --git a/syntax.txt b/syntax.txt deleted file mode 100644 index 381ec74..0000000 --- a/syntax.txt +++ /dev/null @@ -1,22 +0,0 @@ -For Loops ---------- - -Simple Syntax:: - - {% for in %} - - {% endfor %} - -Item Unpacking:: - - {% for , in %} - - {% endfor %} - -With else block:: - - {% for in %} - - {% else %} - - {% endfor %} diff --git a/test.html b/test.html deleted file mode 100644 index f846b06..0000000 --- a/test.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - {% block "page_title" %}Untitled{% endblock %} | My Webpage - - - - {% block html_head %}{% endblock %} - - - -
- {% block body %} - content goes here. - {% endblock %} -
- - - - diff --git a/test.py b/test.py deleted file mode 100644 index eb079f4..0000000 --- a/test.py +++ /dev/null @@ -1,18 +0,0 @@ -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) diff --git a/tests/inheritance.py b/tests/inheritance.py new file mode 100644 index 0000000..1c56168 --- /dev/null +++ b/tests/inheritance.py @@ -0,0 +1,7 @@ +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() diff --git a/tests/mockup.txt b/tests/mockup.txt deleted file mode 100644 index ff7e5b2..0000000 --- a/tests/mockup.txt +++ /dev/null @@ -1,153 +0,0 @@ -source index.html:: - - {% page extends='layout.html', defaultfilter='autoescape', - charset='utf-8' %} - - {% def title %}Index | {{ super.title }}{% enddef %} - {% def body %} -

Index

-

- 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. -

-
    - {% for user in users %} -
  • {{ user.username }}
  • - {% endfor %} -
- {% enddef %} - -source layout.html:: - - {% page defaultfilter='autoescape', charset='utf-8' %} - - - {% block title %}My Webpage{% endblock %} - - -
-

My Webpage

-

{{ self.title }}

-
-
- {{ self.body }} -
- - - - -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'\n \n \n') - for i in self.resolve('self.title'): - yield i - yield Markup(u'\n \n \n
\n' - u'

My Webpage

\n

') - for i in self.resolve('self.title'): - yield i - yield Markup(u'

\n
\n
\n ') - for i in self.resolve('self.body'): - yield i - yield Markup(u'\n
\n \n \nIndex\n

\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

\n
    \n ') - iteration_1 = self.start_iteration(['user'], 'users') - for _ in iteration_1: - yield Markup(u'\n
  • ') - for i in self.resolve('user.username'): - yield i - yield Markup(u'
  • \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('\n \n \n')); - this.resolve('self.title'); - this.yield(m('\n \n \n
    \n' - '

    My Webpage

    \n

    ')); - this.resolve('self.title'); - this.yield(m('

    \n
    \n
    \n ')); - this.resolve('self.body'); - this.yield(m('\n
    \n \n \nIndex\n

    \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

    \n
      \n ')); - var iteration_1 = this.startIteration(['user'], 'users'); - while (iteration_1.next()) { - this.yield(m('\n
    • ')); - this.resolve('user.username'); - this.yield('
    • \n '); - }; - iteration_1.close(); - }; - - return function(context) { - return new Jinja.Template(context, l1, l2); - }; - })(); diff --git a/tests/run.py b/tests/run.py deleted file mode 100644 index b323b64..0000000 --- a/tests/run.py +++ /dev/null @@ -1,15 +0,0 @@ -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) - ] -) diff --git a/tests/templates/index.html b/tests/templates/index.html new file mode 100644 index 0000000..1f8cb7a --- /dev/null +++ b/tests/templates/index.html @@ -0,0 +1,5 @@ +{% extends "layout.html" %} +{% block page_title %}Index{% endblock %} +{% block body %} + This is the index page. +{% endblock %} diff --git a/tests/templates/layout.html b/tests/templates/layout.html new file mode 100644 index 0000000..bf7b5f0 --- /dev/null +++ b/tests/templates/layout.html @@ -0,0 +1,23 @@ + + + + {% block page_title %}Untitled{% endblock %} | My Webpage + {% block page_head %}{% endblock %} + + + +
      + {% block body %} + content goes here. + {% endblock %} +
      + + -- 2.26.2