updated jinja2 offline compression

- changed jinja2 settings
- added jingo and coffin tests.
- changed the way "forced" setting is set
This commit is contained in:
Lucas Tan
2014-03-13 22:32:32 +08:00
parent 4499460df3
commit d40b31dece
8 changed files with 139 additions and 66 deletions

View File

@@ -68,18 +68,12 @@ class CompressorConf(AppConf):
OFFLINE_MANIFEST = 'manifest.json'
# The Context to be used when TemplateFilter is used
TEMPLATE_FILTER_CONTEXT = {}
# Whether to force rendering in the jinja2 compress extension.
# Function that returns the Jinja2 environment to use in offline compression.
def JINJA2_GET_ENVIRONMENT():
import jinja2
return jinja2.Environment()
# Whether to force offline generation.
JINJA2_FORCED = False
# Jinja2 extensions needed, not restricted to those in compress blocks.
JINJA2_EXTENSIONS = []
# Jinja2 template loader; use this to config where to find Jinja2 templates.
JINJA2_LOADER = None
# Jinja2 template global variables and functions.
JINJA2_GLOBALS = {}
# Jinja2 template global filters.
JINJA2_FILTERS = {}
# Jinja2 environment options.
JINJA2_OPTIONS = {}
class Meta:
prefix = 'compress'

View File

@@ -97,14 +97,8 @@ class Command(NoArgsCommand):
def __get_parser(self, engine):
if engine == "jinja2":
from compressor.parser.jinja2 import Jinja2Parser
parser = Jinja2Parser(
charset=settings.FILE_CHARSET,
extensions=settings.COMPRESS_JINJA2_EXTENSIONS,
loader=settings.COMPRESS_JINJA2_LOADER,
globals=settings.COMPRESS_JINJA2_GLOBALS,
filters=settings.COMPRESS_JINJA2_FILTERS,
options=settings.COMPRESS_JINJA2_OPTIONS,
)
env = settings.COMPRESS_JINJA2_GET_ENVIRONMENT()
parser = Jinja2Parser(charset=settings.FILE_CHARSET, env=env)
elif engine == "django":
from compressor.parser.dj import DjangoParser
parser = DjangoParser(charset=settings.FILE_CHARSET)
@@ -208,6 +202,9 @@ class Command(NoArgsCommand):
count = 0
results = []
offline_manifest = SortedDict()
old_forced = settings.COMPRESS_JINJA2_FORCED
settings.COMPRESS_JINJA2_FORCED = True
for template, nodes in compressor_nodes.items():
context = Context(settings.COMPRESS_OFFLINE_CONTEXT)
template._log = log
@@ -231,6 +228,7 @@ class Command(NoArgsCommand):
results.append(result)
count += 1
settings.COMPRESS_JINJA2_FORCED = old_forced
write_offline_manifest(offline_manifest)
log.write("done\nCompressed %d block(s) from %d template(s).\n" %

View File

@@ -61,15 +61,9 @@ def url_for(mod, filename):
class Jinja2Parser(object):
COMPRESSOR_ID = 'compressor.contrib.jinja2ext.CompressorExtension'
def __init__(self, charset, extensions, loader, filters, globals, options):
self.env = jinja2.Environment(
extensions=extensions,
loader=loader,
**options
)
self.env.globals.update(globals)
self.env.filters.update(filters)
def __init__(self, charset, env):
self.charset = charset
self.env = env
def parse(self, template_name):
with io.open(template_name, mode='rb') as file:
@@ -86,8 +80,10 @@ class Jinja2Parser(object):
return True
def process_node(self, template, context, node):
# Don't need to add filters and tests to the context, as Jinja2 will
# automatically look for them in self.env.filters and self.env.tests
# This is tested by test_complex and test_templatetag.
context.update(self.env.globals)
context.update(self.env.filters)
def render_nodelist(self, template, context, node):
compiled_node = self.env.compile(jinja2.nodes.Template(node.body))
@@ -114,6 +110,7 @@ class Jinja2Parser(object):
isinstance(node.call, Call) and
isinstance(node.call.node, ExtensionAttribute) and
node.call.node.identifier == self.COMPRESSOR_ID):
node.call.node._compress_forced = True
yield node
else:
for node in self.walk_nodes(node, block_name=block_name):

View File

@@ -14,6 +14,8 @@ DATABASES = {
INSTALLED_APPS = [
'compressor',
'coffin',
'jingo',
]
STATIC_URL = '/static/'

View File

@@ -30,6 +30,8 @@ class OfflineTestCaseMixin(object):
# Change this for each test class
templates_dir = ""
expected_hash = ""
# Engines to test
engines = ("django", "jinja2")
def setUp(self):
self._old_compress = settings.COMPRESS_ENABLED
@@ -41,18 +43,37 @@ class OfflineTestCaseMixin(object):
# Reset template dirs, because it enables us to force compress to
# consider only a specific directory (helps us make true,
# independant unit tests).
# Specify both Jinja2 and Django template locations. When the wrong engine
# is used to parse a template, the TemplateSyntaxError will cause the
# template to be skipped over.
settings.TEMPLATE_DIRS = (
os.path.join(settings.TEST_DIR, 'test_templates', self.templates_dir),
os.path.join(settings.TEST_DIR, 'test_templates_jinja2', self.templates_dir),
)
# Enable offline compress
settings.COMPRESS_ENABLED = True
settings.COMPRESS_OFFLINE = True
self.template_path = os.path.join(settings.TEMPLATE_DIRS[0], self.template_name)
with io.open(self.template_path, encoding=settings.FILE_CHARSET) as file:
self.template = Template(file.read())
if "django" in self.engines:
self.template_path = os.path.join(settings.TEMPLATE_DIRS[0], self.template_name)
with io.open(self.template_path, encoding=settings.FILE_CHARSET) as file:
self.template = Template(file.read())
self._old_jinja2_get_environment = settings.COMPRESS_JINJA2_GET_ENVIRONMENT
if "jinja2" in self.engines:
# Setup Jinja2 settings.
settings.COMPRESS_JINJA2_GET_ENVIRONMENT = lambda: self._get_jinja2_env()
jinja2_env = settings.COMPRESS_JINJA2_GET_ENVIRONMENT()
self.template_path_jinja2 = os.path.join(settings.TEMPLATE_DIRS[1], self.template_name)
with io.open(self.template_path_jinja2, encoding=settings.FILE_CHARSET) as file:
self.template_jinja2 = jinja2_env.from_string(file.read())
def tearDown(self):
settings.COMPRESS_JINJA2_GET_ENVIRONMENT = self._old_jinja2_get_environment
settings.COMPRESS_ENABLED = self._old_compress
settings.COMPRESS_OFFLINE = self._old_compress_offline
settings.TEMPLATE_DIRS = self._old_template_dirs
@@ -60,68 +81,72 @@ class OfflineTestCaseMixin(object):
if default_storage.exists(manifest_path):
default_storage.delete(manifest_path)
def _render_template(self, engine):
if engine == "django":
return self.template.render(Context(settings.COMPRESS_OFFLINE_CONTEXT))
elif engine == "jinja2":
return self.template_jinja2.render(settings.COMPRESS_OFFLINE_CONTEXT) + "\n"
else:
return None
def _test_offline(self, engine):
count, result = CompressCommand().compress(log=self.log, verbosity=self.verbosity, engine=engine)
self.assertEqual(1, count)
self.assertEqual([
'<script type="text/javascript" src="/static/CACHE/js/%s.js"></script>' % (self.expected_hash, ),
], result)
rendered_template = self.template.render(Context(settings.COMPRESS_OFFLINE_CONTEXT))
rendered_template = self._render_template(engine)
self.assertEqual(rendered_template, "".join(result) + "\n")
def test_offline_django(self):
settings.TEMPLATE_DIRS = (
os.path.join(settings.TEST_DIR, 'test_templates', self.templates_dir),
)
self._test_offline(engine="django")
def test_offline(self):
for engine in self.engines:
self._test_offline(engine=engine)
def test_offline_jinja2(self):
def _get_jinja2_env(self):
import jinja2
import jinja2.ext
from compressor.parser.jinja2 import url_for, SpacelessExtension
from compressor.contrib.jinja2ext import CompressorExtension
settings.TEMPLATE_DIRS = (
os.path.join(settings.TEST_DIR, 'test_templates_jinja2', self.templates_dir),
)
old_jinja2_forced = settings.COMPRESS_JINJA2_FORCED
settings.COMPRESS_JINJA2_FORCED = True
# Extensions needed for the test cases only.
settings.COMPRESS_JINJA2_EXTENSIONS = [
extensions = [
CompressorExtension,
SpacelessExtension,
jinja2.ext.with_,
jinja2.ext.do,
]
settings.COMPRESS_JINJA2_LOADER = jinja2.FileSystemLoader(settings.TEMPLATE_DIRS, encoding=settings.FILE_CHARSET)
settings.COMPRESS_JINJA2_GLOBALS = {"url_for": url_for}
settings.COMPRESS_JINJA2_FILTERS = {}
settings.COMPRESS_JINJA2_OPTIONS = {}
self._test_offline(engine="jinja2")
settings.COMPRESS_JINJA2_FORCED = old_jinja2_forced
loader = self._get_jinja2_loader()
env = jinja2.Environment(extensions=extensions, loader=loader)
env.globals['url_for'] = url_for
return env
def _get_jinja2_loader(self):
import jinja2
loader = jinja2.FileSystemLoader(settings.TEMPLATE_DIRS, encoding=settings.FILE_CHARSET)
return loader
class OfflineGenerationBlockSuperTestCase(OfflineTestCaseMixin, TestCase):
templates_dir = "test_block_super"
expected_hash = "7c02d201f69d"
def test_offline_jinja2(self):
# Block.super not supported for Jinja2 yet.
pass
# Block.super not supported for Jinja2 yet.
engines = ("django",)
class OfflineGenerationBlockSuperMultipleTestCase(OfflineTestCaseMixin, TestCase):
templates_dir = "test_block_super_multiple"
expected_hash = "2f6ef61c488e"
def test_offline_jinja2(self):
# Block.super not supported for Jinja2 yet.
pass
# Block.super not supported for Jinja2 yet.
engines = ("django",)
class OfflineGenerationBlockSuperMultipleWithCachedLoaderTestCase(OfflineTestCaseMixin, TestCase):
templates_dir = "test_block_super_multiple_cached"
expected_hash = "2f6ef61c488e"
# Block.super not supported for Jinja2 yet.
engines = ("django",)
def setUp(self):
self._old_template_loaders = settings.TEMPLATE_LOADERS
@@ -137,13 +162,11 @@ class OfflineGenerationBlockSuperMultipleWithCachedLoaderTestCase(OfflineTestCas
super(OfflineGenerationBlockSuperMultipleWithCachedLoaderTestCase, self).tearDown()
settings.TEMPLATE_LOADERS = self._old_template_loaders
def test_offline_jinja2(self):
# Block.super not supported for Jinja2 yet.
pass
class OfflineGenerationBlockSuperTestCaseWithExtraContent(OfflineTestCaseMixin, TestCase):
templates_dir = "test_block_super_extra"
# Block.super not supported for Jinja2 yet.
engines = ("django",)
def _test_offline(self, engine):
count, result = CompressCommand().compress(log=self.log, verbosity=self.verbosity, engine=engine)
@@ -155,10 +178,6 @@ class OfflineGenerationBlockSuperTestCaseWithExtraContent(OfflineTestCaseMixin,
rendered_template = self.template.render(Context(settings.COMPRESS_OFFLINE_CONTEXT))
self.assertEqual(rendered_template, "".join(result) + "\n")
def test_offline_jinja2(self):
# Block.super not supported for Jinja2 yet.
pass
class OfflineGenerationConditionTestCase(OfflineTestCaseMixin, TestCase):
templates_dir = "test_condition"
@@ -350,3 +369,42 @@ class OfflineGenerationComplexTestCase(OfflineTestCaseMixin, TestCase):
rendered_template = self.template.render(Context(settings.COMPRESS_OFFLINE_CONTEXT))
result = (result[0], result[2])
self.assertEqual(rendered_template, "".join(result) + "\n")
class OfflineGenerationCoffinTestCase(OfflineTestCaseMixin, TestCase):
templates_dir = "test_coffin"
expected_hash = "32c8281e3346"
engines = ("jinja2",)
def _get_jinja2_env(self):
import jinja2
from coffin.common import env
from compressor.contrib.jinja2ext import CompressorExtension
# Could have used the env.add_extension method, but it's only available
# in Jinja2 v2.5
new_env = jinja2.Environment(extensions=[CompressorExtension])
env.extensions.update(new_env.extensions)
return env
class OfflineGenerationJingoTestCase(OfflineTestCaseMixin, TestCase):
templates_dir = "test_jingo"
expected_hash = "61ec584468eb"
engines = ("jinja2",)
def _get_jinja2_env(self):
import jinja2
import jinja2.ext
from jingo import env
from compressor.contrib.jinja2ext import CompressorExtension
from compressor.parser.jinja2 import SpacelessExtension, url_for
# Could have used the env.add_extension method, but it's only available
# in Jinja2 v2.5
new_env = jinja2.Environment(extensions=[CompressorExtension, SpacelessExtension, jinja2.ext.with_])
env.extensions.update(new_env.extensions)
env.globals['url_for'] = url_for
return env

View File

@@ -0,0 +1,11 @@
{%- load compress -%}
{% spaceless %}
{% compress js%}
<script type="text/javascript">alert("{{ condition|default("yellow") }}");
var ok = "{% if (25*4) is divisibleby 50 %}ok{% endif %}";
</script>
{% with "js/one.js" as name -%}
<script type="text/javascript" src="{% static name %}"></script>
{%- endwith %}
{% endcompress %}
{% endspaceless %}

View File

@@ -0,0 +1,11 @@
{% spaceless %}
{% compress js%}
<script type="text/javascript">alert("{{ condition|default("yellow") }}");
var ok = "{% if (25*4) is divisibleby 50 %}ok{% endif %}";
var text = "{{"hello\nworld"|nl2br}}";
</script>
{% with name="js/one.js" -%}
<script type="text/javascript" src="{{ 8|ifeq(2*4, url_for('static', name)) }}"></script>
{%- endwith %}
{% endcompress %}
{% endspaceless %}

View File

@@ -6,3 +6,5 @@ jinja2
lxml
BeautifulSoup
unittest2
coffin
jingo