work on tha runtime
--HG--
branch : trunk
diff --git a/jdebug.py b/jdebug.py
deleted file mode 100644
index f5463a6..0000000
--- a/jdebug.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jdebug
- ~~~~~~
-
- Helper module to simplify jinja debugging. Use
-
- :copyright: 2006 by Armin Ronacher.
- :license: BSD, see LICENSE for more details.
-"""
-import os
-import sys
-import gc
-from jinja import Environment
-from jinja.parser import Parser
-from jinja.lexer import Lexer
-from jinja.translators.python import PythonTranslator
-
-
-__all__ = ['e', 't', 'p', 'l', 'm']
-
-
-_global_frame = sys._getframe()
-e = Environment()
-t = e.from_string
-
-
-def p(x=None, f=None):
- if x is None and f is not None:
- x = e.loader.get_source(f)
- print PythonTranslator(e, Parser(e, x, f).parse(), None).translate()
-
-def l(x):
- for item in e.lexer.tokenize(x):
- print '%5s %-20s %r' % (item.lineno,
- item.type,
- item.value)
-
-class MemoryGuard(object):
-
- def __init__(self):
- self.guarded_objects = {}
- self.clear = self.guarded_objects.clear
-
- def freeze(self):
- self.clear()
- for obj in gc.get_objects():
- self.guarded_objects[id(obj)] = True
-
- def get_delta(self):
- frm = sys._getframe()
- result = []
- for obj in gc.get_objects():
- if id(obj) not in self.guarded_objects and \
- obj is not frm and obj is not result:
- result.append(obj)
- return result
-
-
-m = MemoryGuard()
-
-
-if __name__ == '__main__':
- if len(sys.argv) > 1:
- from jinja import FileSystemLoader
- e.loader = FileSystemLoader(sys.argv[1])
- if len(sys.argv) > 2:
- p(f=sys.argv[2])
- else:
- p(sys.stdin.read())
diff --git a/jinja2/compiler.py b/jinja2/compiler.py
index 66af667..6175916 100644
--- a/jinja2/compiler.py
+++ b/jinja2/compiler.py
@@ -30,6 +30,7 @@
def generate(node, environment, filename, stream=None):
+ """Generate the python source for a node tree."""
is_child = node.find(nodes.Extends) is not None
generator = CodeGenerator(environment, is_child, filename, stream)
generator.visit(node)
@@ -341,14 +342,14 @@
def visit_Block(self, node, frame):
"""Call a block and register it for the template."""
- # if we know that we are a child template, there is no need to
- # check if we are one
- if self.has_known_extends and frame.toplevel:
- return
if frame.toplevel:
+ # if we know that we are a child template, there is no need to
+ # check if we are one
+ if self.has_known_extends:
+ return
self.writeline('if parent_root is None:')
self.indent()
- self.writeline('for event in context.blocks[-1][%r](context):' % node.name)
+ self.writeline('for event in context.blocks[0][%r](context):' % node.name)
self.indent()
self.writeline('yield event')
self.outdent(1 + frame.toplevel)
@@ -383,7 +384,7 @@
self.writeline('parent_root = extends(', node, 1)
self.visit(node.template, frame)
- self.write(', context)')
+ self.write(', context, environment)')
# if this extends statement was in the root level we can take
# advantage of that information and simplify the generated code
diff --git a/jinja2/environment.py b/jinja2/environment.py
index 8ba4fce..a350620 100644
--- a/jinja2/environment.py
+++ b/jinja2/environment.py
@@ -10,6 +10,8 @@
"""
from jinja2.lexer import Lexer
from jinja2.parser import Parser
+from jinja2.optimizer import optimize
+from jinja2.compiler import generate
from jinja2.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
@@ -29,7 +31,8 @@
comment_start_string='{#',
comment_end_string='#}',
trim_blocks=False,
- template_charset='utf-8'):
+ template_charset='utf-8',
+ loader=None):
"""Here the possible initialization parameters:
========================= ============================================
@@ -47,6 +50,7 @@
after a block is removed (block, not
variable tag!). Defaults to ``False``.
`template_charset` the charset of the templates.
+ `loader` the loader which should be used.
========================= ============================================
"""
@@ -71,6 +75,9 @@
if not hasattr(self, 'finalize'):
self.finalize = unicode
+ # set the loader provided
+ self.loader = loader
+
# create lexer
self.lexer = Lexer(self)
@@ -91,3 +98,47 @@
The tuples are returned in the form ``(lineno, token, value)``.
"""
return self.lexer.tokeniter(source, filename)
+
+ def compile(self, source, filename=None, raw=False):
+ """Compile a node or source."""
+ if isinstance(source, basestring):
+ source = self.parse(source, filename)
+ node = optimize(source, self)
+ source = generate(node, self)
+ if raw:
+ return source
+ if isinstance(filename, unicode):
+ filename = filename.encode('utf-8')
+ return compile(source, filename, 'exec')
+
+ def join_path(self, template, parent):
+ """Join a template with the parent. By default all the lookups are
+ relative to the loader root, but if the paths should be relative this
+ function can be used to calculate the real filename."""
+ return template
+
+ def get_template(self, name, parent=None):
+ """Load a template."""
+ if self.loader is None:
+ raise TypeError('no loader for this environment specified')
+ if parent is not None:
+ name = self.join_path(name, parent)
+ return self.loader.load(self, name)
+
+
+class Template(object):
+ """Represents a template."""
+
+ def __init__(self, environment, code):
+ namespace = {'environment': environment}
+ exec code in namespace
+ self.environment = environment
+ self.root_render_func = namespace['root']
+ self.blocks = namespace['blocks']
+
+ def render(self, *args, **kwargs):
+ return u''.join(self.stream(*args, **kwargs))
+
+ def stream(self, *args, **kwargs):
+ context = dict(*args, **kwargs)
+ return self.root_render_func(context)
diff --git a/jinja2/lexer.py b/jinja2/lexer.py
index 6e9fc89..b19e4ca 100644
--- a/jinja2/lexer.py
+++ b/jinja2/lexer.py
@@ -21,9 +21,6 @@
from weakref import WeakValueDictionary
-__all__ = ['Lexer', 'Failure', 'keywords']
-
-
# cache for the lexers. Exists in order to be able to have multiple
# environments with the same lexer
_lexer_cache = WeakValueDictionary()
diff --git a/jinja2/loaders.py b/jinja2/loaders.py
index 40bc1d7..796b49f 100644
--- a/jinja2/loaders.py
+++ b/jinja2/loaders.py
@@ -1,10 +1,47 @@
# -*- coding: utf-8 -*-
"""
- jinja.loaders
- ~~~~~~~~~~~~~
+ jinja2.loaders
+ ~~~~~~~~~~~~~~
Jinja loader classes.
- :copyright: 2007 by Armin Ronacher, Bryan McLemore.
+ :copyright: 2008 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
+from os import path
+from jinja2.exceptions import TemplateNotFound
+from jinja2.environment import Template
+
+
+class BaseLoader(object):
+
+ def get_source(self, environment, template):
+ raise TemplateNotFound()
+
+ def load(self, environment, template):
+ source, filename = self.get_source(environment, template)
+ code = environment.compile(source, filename)
+ return Template(environment, code)
+
+
+class FileSystemLoader(BaseLoader):
+
+ def __init__(self, path, encoding='utf-8'):
+ self.path = path
+ self.encoding = encoding
+
+ def get_source(self, environment, template):
+ pieces = []
+ for piece in template.split('/'):
+ if piece == '..':
+ raise TemplateNotFound()
+ elif piece != '.':
+ pieces.append(piece)
+ filename = path.join(self.path, *pieces)
+ if not path.isfile(filename):
+ raise TemplateNotFound(template)
+ f = file(filename)
+ try:
+ return f.read().decode(self.encoding)
+ finally:
+ f.close()
diff --git a/jinja2/optimizer.py b/jinja2/optimizer.py
index 167f6eb..bd97fa0 100644
--- a/jinja2/optimizer.py
+++ b/jinja2/optimizer.py
@@ -24,6 +24,13 @@
from jinja2.runtime import subscribe, LoopContext
+def optimize(node, environment, context_hint=None):
+ """The context hint can be used to perform an static optimization
+ based on the context given."""
+ optimizer = Optimizer(environment)
+ return optimizer.visit(node, ContextStack(context_hint))
+
+
class ContextStack(object):
"""Simple compile time context implementation."""
undefined = object()
@@ -206,10 +213,3 @@
visit_Not = visit_Compare = visit_Subscript = visit_Call = \
visit_Filter = visit_Test = fold
del fold
-
-
-def optimize(node, environment, context_hint=None):
- """The context hint can be used to perform an static optimization
- based on the context given."""
- optimizer = Optimizer(environment)
- return optimizer.visit(node, ContextStack(context_hint))
diff --git a/jinja2/parser.py b/jinja2/parser.py
index 74cc421..8dc5d58 100644
--- a/jinja2/parser.py
+++ b/jinja2/parser.py
@@ -12,8 +12,6 @@
from jinja2.exceptions import TemplateSyntaxError
-__all__ = ['Parser']
-
_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print',
'macro', 'include'])
_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq', 'in'])
diff --git a/jinja2/runtime.py b/jinja2/runtime.py
index 097b9e4..82acd6c 100644
--- a/jinja2/runtime.py
+++ b/jinja2/runtime.py
@@ -18,9 +18,12 @@
'TemplateContext', 'Macro', 'Undefined']
-def extends(template, context):
+def extends(template_name, context, environment):
"""This loads a template (and evaluates it) and replaces the blocks."""
- context.stream_muted = True
+ template = environment.get_template(template_name, context.filename)
+ for name, block in template.blocks.iteritems():
+ context.blocks.setdefault(name, []).append(block)
+ return template.root_render_func
def subscribe(obj, argument):
diff --git a/test.py b/test.py
deleted file mode 100644
index 4b0b218..0000000
--- a/test.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import sys
-from jinja2 import Environment
-from jinja2.compiler import generate
-
-
-env = Environment()
-ast = env.parse("""
-{% extends master_layout or "foo.html" %}
-
-{% baz = [1, 2, 3, 4] %}
-{% macro foo() %}
- blah
-{% endmacro %}
-{% blah = 42 %}
-
-{% block body %}
- Das ist ein Test
-{% endblock %}
-""")
-source = generate(ast, env, "foo.html")
-print source
diff --git a/test_optimizer.py b/test_optimizer.py
deleted file mode 100644
index d0d8551..0000000
--- a/test_optimizer.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from jinja2 import Environment
-from jinja2.compiler import generate
-from jinja2.optimizer import optimize
-
-
-env = Environment()
-forums = [
- {'id': 1, 'name': u'Example'},
- {'id': 2, 'name': u'Foobar'},
- {'id': 3, 'name': u'<42>'}
-]
-ast = env.parse("""
- Hi {{ "<blub>"|e }},
- how are you?
-
- {% for forum in forums %}
- {{ readstatus(forum.id) }} {{ forum.id|e }} {{ forum.name|e }}
- {% endfor %}
-
- {% navigation = [('#foo', 'Foo'), ('#bar', 'Bar'), ('#baz', 42 * 2 + 23)] %}
- <ul>
- {% for key, value in navigation %}
- <li>{{ loop.index }}: <a href="{{ key[1:].upper()|e }}">{{ value|e }}</a></li>
- {% endfor %}
- </ul>
-""")
-print ast
-print
-print generate(ast, env, "foo.html")
-print
-ast = optimize(ast, env, context_hint={'forums': forums})
-print ast
-print
-print generate(ast, env, "foo.html")