[svn] checked in today's jinja changes which i forgot to commit
[jinja2.git] / jinja / environment.py
1 # -*- coding: utf-8 -*-
2 """
3     jinja.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 import re
12 from jinja.lexer import Lexer
13 from jinja.parser import Parser
14 from jinja.loaders import LoaderWrapper
15 from jinja.datastructure import Undefined, Markup, Context, FakeTranslator
16 from jinja.utils import escape, collect_translations, get_attribute
17 from jinja.exceptions import FilterNotFound, TestNotFound, \
18      SecurityException, TemplateSyntaxError
19 from jinja.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
20
21
22 class Environment(object):
23     """
24     The jinja environment.
25     """
26
27     def __init__(self,
28                  block_start_string='{%',
29                  block_end_string='%}',
30                  variable_start_string='{{',
31                  variable_end_string='}}',
32                  comment_start_string='{#',
33                  comment_end_string='#}',
34                  trim_blocks=False,
35                  auto_escape=False,
36                  default_filters=None,
37                  template_charset='utf-8',
38                  charset='utf-8',
39                  namespace=None,
40                  loader=None,
41                  filters=None,
42                  tests=None,
43                  context_class=Context):
44
45         # lexer / parser information
46         self.block_start_string = block_start_string
47         self.block_end_string = block_end_string
48         self.variable_start_string = variable_start_string
49         self.variable_end_string = variable_end_string
50         self.comment_start_string = comment_start_string
51         self.comment_end_string = comment_end_string
52         self.trim_blocks = trim_blocks
53
54         # other stuff
55         self.template_charset = template_charset
56         self.charset = charset
57         self.loader = loader
58         self.filters = filters is None and DEFAULT_FILTERS.copy() or filters
59         self.tests = tests is None and DEFAULT_TESTS.copy() or tests
60         self.default_filters = default_filters or []
61         self.context_class = context_class
62
63         # global namespace
64         self.globals = namespace is None and DEFAULT_NAMESPACE.copy() \
65                        or namespace
66
67         # jinja 1.0 compatibility
68         if auto_escape:
69             self.default_filters.append(('escape', (True,)))
70             self.globals['Markup'] = Markup
71
72         # create lexer
73         self.lexer = Lexer(self)
74
75     def loader(self, value):
76         """
77         Get or set the template loader.
78         """
79         self._loader = LoaderWrapper(self, value)
80     loader = property(lambda s: s._loader, loader, loader.__doc__)
81
82     def parse(self, source, filename=None):
83         """Function that creates a new parser and parses the source."""
84         parser = Parser(self, source, filename)
85         return parser.parse()
86
87     def from_string(self, source):
88         """Load a template from a string."""
89         from jinja.parser import Parser
90         from jinja.translators.python import PythonTranslator
91         try:
92             rv = PythonTranslator.process(self, Parser(self, source).parse())
93         except TemplateSyntaxError, e:
94             # if errors occour raise a better traceback
95             from jinja.utils import raise_syntax_error
96             __traceback_hide__ = True
97             raise_syntax_error(e, self, source)
98         else:
99             # everything went well. attach the source and return it
100             # attach the source for debugging
101             rv._source = source
102             return rv
103
104     def get_template(self, filename):
105         """Load a template from a filename. Only works
106         if a proper loader is set."""
107         return self._loader.load(filename)
108
109     def to_unicode(self, value):
110         """
111         Convert a value to unicode with the rules defined on the environment.
112         """
113         if value in (None, Undefined):
114             return u''
115         elif isinstance(value, unicode):
116             return value
117         else:
118             try:
119                 return unicode(value)
120             except UnicodeError:
121                 return str(value).decode(self.charset, 'ignore')
122
123     def get_translator(self, context):
124         """
125         Return the translator for i18n.
126
127         A translator is an object that provides the two functions
128         ``gettext(string)`` and ``ngettext(singular, plural, n)``. Note
129         that both of them have to return unicode!
130         """
131         return FakeTranslator()
132
133     def get_translations(self, name):
134         """
135         Load template `name` and return all translatable strings (note that
136         that it really just returns the strings form this template, not from
137         the parent or any included templates!)
138         """
139         return collect_translations(self.loader.parse(name))
140
141     def apply_filters(self, value, context, filters):
142         """
143         Apply a list of filters on the variable.
144         """
145         for key in filters:
146             if key in context.cache:
147                 func = context.cache[key]
148             else:
149                 filtername, args = key
150                 if filtername not in self.filters:
151                     raise FilterNotFound(filtername)
152                 context.cache[key] = func = self.filters[filtername](*args)
153             value = func(self, context, value)
154         return value
155
156     def perform_test(self, context, testname, args, value, invert):
157         """
158         Perform a test on a variable.
159         """
160         key = (testname, args)
161         if key in context.cache:
162             func = context.cache[key]
163         else:
164             if testname not in self.tests:
165                 raise TestNotFound(testname)
166             context.cache[key] = func = self.tests[testname](*args)
167         rv = func(self, context, value)
168         if invert:
169             return not rv
170         return bool(rv)
171
172     def get_attribute(self, obj, name):
173         """
174         Get one attribute from an object.
175         """
176         try:
177             return obj[name]
178         except (TypeError, KeyError, IndexError):
179             try:
180                 return get_attribute(obj, name)
181             except (AttributeError, SecurityException):
182                 pass
183         return Undefined
184
185     def get_attributes(self, obj, attributes):
186         """
187         Get some attributes from an object. If attributes is an
188         empty sequence the object is returned as it.
189         """
190         get = self.get_attribute
191         for name in attributes:
192             obj = get(obj, name)
193         return obj
194
195     def call_function(self, f, context, args, kwargs, dyn_args, dyn_kwargs):
196         """
197         Function call helper. Called for all functions that are passed
198         any arguments.
199         """
200         if dyn_args is not None:
201             args += tuple(dyn_args)
202         elif dyn_kwargs is not None:
203             kwargs.update(dyn_kwargs)
204         if getattr(f, 'jinja_unsafe_call', False) or \
205            getattr(f, 'alters_data', False):
206             return Undefined
207         if getattr(f, 'jinja_context_callable', False):
208             args = (self, context) + args
209         return f(*args, **kwargs)
210
211     def call_function_simple(self, f, context):
212         """
213         Function call without arguments. Because of the smaller signature and
214         fewer logic here we have a bit of redundant code.
215         """
216         if getattr(f, 'jinja_unsafe_call', False) or \
217            getattr(f, 'alters_data', False):
218             return Undefined
219         if getattr(f, 'jinja_context_callable', False):
220             return f(self, context)
221         return f()
222
223     def finish_var(self, value, ctx):
224         """
225         As long as no write_var function is passed to the template
226         evaluator the source generated by the python translator will
227         call this function for all variables.
228         """
229         if value is Undefined or value is None:
230             return u''
231         val = self.to_unicode(value)
232         if self.default_filters:
233             val = self.apply_filters(val, ctx, self.default_filters)
234         return val