converted unit tests, started rewriting filters
[jinja2.git] / jinja2 / environment.py
1 # -*- coding: utf-8 -*-
2 """
3     jinja2.environment
4     ~~~~~~~~~~~~~~~~~~
5
6     Provides a class that holds runtime and parsing time options.
7
8     :copyright: 2007 by Armin Ronacher.
9     :license: BSD, see LICENSE for more details.
10 """
11 from jinja2.lexer import Lexer
12 from jinja2.parser import Parser
13 from jinja2.optimizer import optimize
14 from jinja2.compiler import generate
15 from jinja2.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
16
17
18 class Environment(object):
19     """The Jinja environment.
20
21     The core component of Jinja is the `Environment`. It contains
22     important shared variables like configuration, filters, tests,
23     globals and others.
24     """
25
26     def __init__(self,
27                  block_start_string='{%',
28                  block_end_string='%}',
29                  variable_start_string='{{',
30                  variable_end_string='}}',
31                  comment_start_string='{#',
32                  comment_end_string='#}',
33                  line_statement_prefix=None,
34                  trim_blocks=False,
35                  loader=None):
36         """Here the possible initialization parameters:
37
38         ========================= ============================================
39         `block_start_string`      the string marking the begin of a block.
40                                   this defaults to ``'{%'``.
41         `block_end_string`        the string marking the end of a block.
42                                   defaults to ``'%}'``.
43         `variable_start_string`   the string marking the begin of a print
44                                   statement. defaults to ``'{{'``.
45         `comment_start_string`    the string marking the begin of a
46                                   comment. defaults to ``'{#'``.
47         `comment_end_string`      the string marking the end of a comment.
48                                   defaults to ``'#}'``.
49         `line_statement_prefix`   If given and a string, this will be used as
50                                   prefix for line based statements.  See the
51                                   documentation for more details.
52         `trim_blocks`             If this is set to ``True`` the first newline
53                                   after a block is removed (block, not
54                                   variable tag!). Defaults to ``False``.
55         `loader`                  the loader which should be used.
56         ========================= ============================================
57         """
58
59         # lexer / parser information
60         self.block_start_string = block_start_string
61         self.block_end_string = block_end_string
62         self.variable_start_string = variable_start_string
63         self.variable_end_string = variable_end_string
64         self.comment_start_string = comment_start_string
65         self.comment_end_string = comment_end_string
66         self.line_statement_prefix = line_statement_prefix
67         self.trim_blocks = trim_blocks
68
69         # defaults
70         self.filters = DEFAULT_FILTERS.copy()
71         self.tests = DEFAULT_TESTS.copy()
72         self.globals = DEFAULT_NAMESPACE.copy()
73
74         # if no finalize function/method exists we default to unicode.  The
75         # compiler check if the finalize attribute *is* unicode, if yes no
76         # finalizaion is written where it can be avoided.
77         if not hasattr(self, 'finalize'):
78             self.finalize = unicode
79
80         # set the loader provided
81         self.loader = loader
82
83         # create lexer
84         self.lexer = Lexer(self)
85
86     def parse(self, source, filename=None):
87         """Parse the sourcecode and return the abstract syntax tree. This tree
88         of nodes is used by the compiler to convert the template into
89         executable source- or bytecode.
90         """
91         parser = Parser(self, source, filename)
92         return parser.parse()
93
94     def lex(self, source, filename=None):
95         """Lex the given sourcecode and return a generator that yields tokens.
96         The stream returned is not usable for Jinja but can be used if
97         Jinja templates should be processed by other tools (for example
98         syntax highlighting etc)
99
100         The tuples are returned in the form ``(lineno, token, value)``.
101         """
102         return self.lexer.tokeniter(source, filename)
103
104     def compile(self, source, filename=None, raw=False):
105         """Compile a node or source."""
106         if isinstance(source, basestring):
107             source = self.parse(source, filename)
108         node = optimize(source, self)
109         source = generate(node, self, filename)
110         if raw:
111             return source
112         if isinstance(filename, unicode):
113             filename = filename.encode('utf-8')
114         return compile(source, filename, 'exec')
115
116     def join_path(self, template, parent):
117         """Join a template with the parent.  By default all the lookups are
118         relative to the loader root, but if the paths should be relative this
119         function can be used to calculate the real filename."""
120         return template
121
122     def get_template(self, name, parent=None):
123         """Load a template."""
124         if self.loader is None:
125             raise TypeError('no loader for this environment specified')
126         if parent is not None:
127             name = self.join_path(name, parent)
128         return self.loader.load(self, name)
129
130     def from_string(self, source, filename='<string>'):
131         """Load a template from a string."""
132         return Template(self, self.compile(source, filename))
133
134
135 class Template(object):
136     """Represents a template."""
137
138     def __init__(self, environment, code):
139         namespace = {
140             'environment': environment
141         }
142         exec code in namespace
143         self.environment = environment
144         self.name = namespace['filename']
145         self.root_render_func = namespace['root']
146         self.blocks = namespace['blocks']
147
148     def render(self, *args, **kwargs):
149         return u''.join(self.stream(*args, **kwargs))
150
151     def stream(self, *args, **kwargs):
152         gen = self.root_render_func(dict(*args, **kwargs))
153         # skip the first item which is a reference to the stream
154         gen.next()
155         return gen