:copyright: (c) 2010 by the Jinja Team.
:license: BSD, see LICENSE for more details.
"""
+import os
import sys
from jinja2 import nodes
from jinja2.defaults import *
return TemplateExpression(template, undefined_to_none)
def compile_templates(self, target, extensions=None, filter_func=None,
- zip=True, log_function=None):
+ zip='deflated', log_function=None,
+ ignore_errors=True, py_compile=False):
"""Compiles all the templates the loader can find, compiles them
- and stores them in `target`. If `zip` is true, a zipfile will be
- written, otherwise the templates are stored in a directory.
+ and stores them in `target`. If `zip` is `None`, instead of in a
+ zipfile, the templates will be will be stored in a directory.
+ By default a deflate zip algorithm is used, to switch to
+ the stored algorithm, `zip` can be set to ``'stored'``.
`extensions` and `filter_func` are passed to :meth:`list_templates`.
Each template returned will be compiled to the target folder or
zipfile.
+ By default template compilation errors are ignored. In case a
+ log function is provided, errors are logged. If you want template
+ syntax errors to abort the compilation you can set `ignore_errors`
+ to `False` and you will get an exception on syntax errors.
+
+ If `py_compile` is set to `True` .pyc files will be written to the
+ target instead of standard .py files.
+
.. versionadded:: 2.4
"""
from jinja2.loaders import ModuleLoader
+
if log_function is None:
log_function = lambda x: None
- if zip:
- from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED
- f = ZipFile(target, 'w', ZIP_DEFLATED)
+ if py_compile:
+ import imp, struct, marshal
+ py_header = imp.get_magic() + '\xff\xff\xff\xff'
+
+ def write_file(filename, data):
+ if zip:
+ info = ZipInfo(filename)
+ info.external_attr = 0755 << 16L
+ zip_file.writestr(info, data)
+ else:
+ f = open(os.path.join(target, filename), 'wb')
+ try:
+ f.write(data)
+ finally:
+ f.close()
+
+ if zip is not None:
+ from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED, ZIP_STORED
+ zip_file = ZipFile(target, 'w', dict(deflated=ZIP_DEFLATED,
+ stored=ZIP_STORED)[zip])
log_function('Compiling into Zip archive "%s"' % target)
else:
if not os.path.isdir(target):
try:
code = self.compile(source, name, filename, True, True)
except TemplateSyntaxError, e:
+ if not ignore_errors:
+ raise
log_function('Could not compile "%s": %s' % (name, e))
continue
- module = ModuleLoader.get_module_filename(name)
- if zip:
- info = ZipInfo(module)
- info.external_attr = 0755 << 16L
- f.writestr(info, code)
+
+ filename = ModuleLoader.get_module_filename(name)
+
+ if py_compile:
+ c = compile(code, _encode_filename(filename), 'exec')
+ write_file(filename + 'c', py_header + marshal.dumps(c))
+ log_function('Byte-compiled "%s" as %s' %
+ (name, filename + 'c'))
else:
- f = open(filename, 'w')
- try:
- f.write(code)
- finally:
- f.close()
- log_function('Compiled "%s" as %s' % (name, module))
+ write_file(filename, code)
+ log_function('Compiled "%s" as %s' % (name, filename))
finally:
if zip:
- f.close()
+ zip_file.close()
log_function('Finished compiling templates')
import sys
import time
import tempfile
+import shutil
import unittest
from jinja2.testsuite import JinjaTestCase, dict_loader, \
class ModuleLoaderTestCase(JinjaTestCase):
archive = None
- def setup(self):
+ def compile_down(self, zip='deflated', py_compile=False):
super(ModuleLoaderTestCase, self).setup()
+ log = []
self.reg_env = Environment(loader=prefix_loader)
- self.archive = tempfile.mkstemp(suffix='.zip')[1]
- self.reg_env.compile_templates(self.archive)
+ if zip is not None:
+ self.archive = tempfile.mkstemp(suffix='.zip')[1]
+ else:
+ self.archive = tempfile.mkdtemp()
+ self.reg_env.compile_templates(self.archive, zip=zip,
+ log_function=log.append,
+ py_compile=py_compile)
self.mod_env = Environment(loader=loaders.ModuleLoader(self.archive))
+ return ''.join(log)
def teardown(self):
super(ModuleLoaderTestCase, self).teardown()
- os.remove(self.archive)
- self.archive = None
-
- def test_module_loader(self):
+ if hasattr(self, 'mod_env'):
+ if os.path.isfile(self.archive):
+ os.remove(self.archive)
+ else:
+ shutil.rmtree(self.archive)
+ self.archive = None
+
+ def test_log(self):
+ log = self.compile_down()
+ assert 'Compiled "a/foo/test.html" as ' \
+ 'tmpl_a790caf9d669e39ea4d280d597ec891c4ef0404a' in log
+ assert 'Finished compiling templates' in log
+ assert 'Could not compile "a/syntaxerror.html": ' \
+ 'Encountered unknown tag \'endif\'' in log
+
+ def _test_common(self):
tmpl1 = self.reg_env.get_template('a/test.html')
tmpl2 = self.mod_env.get_template('a/test.html')
assert tmpl1.render() == tmpl2.render()
tmpl2 = self.mod_env.get_template('b/justdict.html')
assert tmpl1.render() == tmpl2.render()
+ def test_deflated_zip_compile(self):
+ self.compile_down(zip='deflated')
+ self._test_common()
+
+ def test_stored_zip_compile(self):
+ self.compile_down(zip='stored')
+ self._test_common()
+
+ def test_filesystem_compile(self):
+ self.compile_down(zip=None)
+ self._test_common()
+
def test_weak_references(self):
+ self.compile_down()
tmpl = self.mod_env.get_template('a/test.html')
key = loaders.ModuleLoader.get_template_key('a/test.html')
name = self.mod_env.loader.module.__name__
assert name not in sys.modules
+ def test_byte_compilation(self):
+ log = self.compile_down(py_compile=True)
+ assert 'Byte-compiled "a/test.html"' in log
+ tmpl1 = self.mod_env.get_template('a/test.html')
+ mod = self.mod_env.loader.module. \
+ tmpl_3c4ddf650c1a73df961a6d3d2ce2752f1b8fd490
+ assert mod.__file__.endswith('.pyc')
+
def suite():
suite = unittest.TestSuite()