Merge pull request #758 from grzegorzswica/support-inheritance-in-offline-compression-using-a-variable

#674 Support inheritance in offline compression using a variable
This commit is contained in:
Mathieu Pillard
2016-05-11 11:59:50 +02:00
11 changed files with 129 additions and 49 deletions

View File

@@ -2,7 +2,7 @@
import os
import sys
from collections import OrderedDict
from collections import OrderedDict, defaultdict
from fnmatch import fnmatch
from importlib import import_module
@@ -154,6 +154,18 @@ class Command(BaseCommand):
if verbosity > 1:
log.write("Found templates:\n\t" + "\n\t".join(templates) + "\n")
contexts = settings.COMPRESS_OFFLINE_CONTEXT
if isinstance(contexts, six.string_types):
try:
module, function = get_mod_func(contexts)
contexts = getattr(import_module(module), function)()
except (AttributeError, ImportError, TypeError) as e:
raise ImportError("Couldn't import offline context function %s: %s" %
(settings.COMPRESS_OFFLINE_CONTEXT, e))
elif not isinstance(contexts, (list, tuple)):
contexts = [contexts]
contexts = list(contexts) # evaluate generator
parser = self.__get_parser(engine)
compressor_nodes = OrderedDict()
for template_name in templates:
@@ -176,16 +188,22 @@ class Command(BaseCommand):
log.write("UnicodeDecodeError while trying to read "
"template %s\n" % template_name)
continue
try:
nodes = list(parser.walk_nodes(template))
except (TemplateDoesNotExist, TemplateSyntaxError) as e:
# Could be an error in some base template
if verbosity > 0:
log.write("Error parsing template %s: %s\n" % (template_name, e))
continue
if nodes:
template.template_name = template_name
compressor_nodes.setdefault(template, []).extend(nodes)
for context_dict in contexts:
context = parser.get_init_context(context_dict)
context = Context(context)
try:
nodes = list(parser.walk_nodes(template, context=context))
except (TemplateDoesNotExist, TemplateSyntaxError) as e:
# Could be an error in some base template
if verbosity > 0:
log.write("Error parsing template %s: %s\n" % (template_name, e))
continue
if nodes:
template.template_name = template_name
template_nodes = compressor_nodes.setdefault(template, OrderedDict())
for node in nodes:
template_nodes.setdefault(node, []).append(context)
if not compressor_nodes:
raise OfflineGenerationError(
@@ -198,36 +216,23 @@ class Command(BaseCommand):
"\n\t".join((t.template_name
for t in compressor_nodes.keys())) + "\n")
contexts = settings.COMPRESS_OFFLINE_CONTEXT
if isinstance(contexts, six.string_types):
try:
module, function = get_mod_func(contexts)
contexts = getattr(import_module(module), function)()
except (AttributeError, ImportError, TypeError) as e:
raise ImportError("Couldn't import offline context function %s: %s" %
(settings.COMPRESS_OFFLINE_CONTEXT, e))
elif not isinstance(contexts, (list, tuple)):
contexts = [contexts]
log.write("Compressing... ")
block_count = context_count = 0
block_count = 0
compressed_contexts = []
results = []
offline_manifest = OrderedDict()
for template, nodes in compressor_nodes.items():
template._log = log
template._log_verbosity = verbosity
for context_dict in contexts:
context_count += 1
init_context = parser.get_init_context(context_dict)
for template, nodes in compressor_nodes.items():
context = Context(init_context)
template._log = log
template._log_verbosity = verbosity
if not parser.process_template(template, context):
continue
for node in nodes:
for node, contexts in nodes.items():
for context in contexts:
if context not in compressed_contexts:
compressed_contexts.append(context)
context.push()
if not parser.process_template(template, context):
continue
parser.process_node(template, context, node)
rendered = parser.render_nodelist(template, context, node)
key = get_offline_hexdigest(rendered)
@@ -247,6 +252,7 @@ class Command(BaseCommand):
write_offline_manifest(offline_manifest)
context_count = len(compressed_contexts)
log.write("done\nCompressed %d block(s) from %d template(s) for %d context(s).\n" %
(block_count, len(compressor_nodes), context_count))
return block_count, results

View File

@@ -116,16 +116,12 @@ class DjangoParser(object):
def render_node(self, template, context, node):
return node.render(context, forced=True)
def get_nodelist(self, node, original):
def get_nodelist(self, node, original, context):
if isinstance(node, ExtendsNode):
try:
context = Context()
if context is None:
context = Context()
context.template = original
# TODO: We are passing an empty context when finding base
# templates. This does not work when extending using
# variables ({% extends template_var %}).
# A refactor might be needed to support that use-case with
# multiple offline contexts.
return handle_extendsnode(node, context)
except template.TemplateSyntaxError as e:
raise TemplateSyntaxError(str(e))
@@ -141,13 +137,13 @@ class DjangoParser(object):
nodelist = getattr(node, 'nodelist', [])
return nodelist
def walk_nodes(self, node, original=None):
def walk_nodes(self, node, original=None, context=None):
if original is None:
original = node
for node in self.get_nodelist(node, original):
for node in self.get_nodelist(node, original, context):
if isinstance(node, CompressorNode) \
and node.is_offline_compression_enabled(forced=True):
yield node
else:
for node in self.walk_nodes(node, original):
for node in self.walk_nodes(node, original, context):
yield node

View File

@@ -112,7 +112,7 @@ class Jinja2Parser(object):
return body
def walk_nodes(self, node, block_name=None):
def walk_nodes(self, node, block_name=None, context=None):
for node in self.get_nodelist(node):
if (isinstance(node, CallBlock) and
isinstance(node.call, Call) and

View File

@@ -120,10 +120,14 @@ class OfflineTestCaseMixin(object):
default_storage.delete(manifest_path)
def _prepare_contexts(self, engine):
contexts = settings.COMPRESS_OFFLINE_CONTEXT
if not isinstance(contexts, (list, tuple)):
contexts = [contexts]
if engine == 'django':
return [Context(settings.COMPRESS_OFFLINE_CONTEXT)]
return [Context(c) for c in contexts]
if engine == 'jinja2':
return [settings.COMPRESS_OFFLINE_CONTEXT]
return contexts
return None
def _render_template(self, engine):
@@ -433,6 +437,40 @@ class OfflineCompressTestCaseWithContextGeneratorSuper(
engines = ('django',)
class OfflineCompressTestCaseWithContextVariableInheritance(
OfflineTestCaseMixin, TestCase):
templates_dir = 'test_with_context_variable_inheritance'
additional_test_settings = {
'COMPRESS_OFFLINE_CONTEXT': {
'parent_template': 'base.html',
}
}
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/'
'ea3267f3e9dd.js"></script>'], result)
rendered_template = self._render_template(engine)
self.assertEqual(rendered_template, '\n' + result[0] + '\n')
class OfflineCompressTestCaseWithContextVariableInheritanceSuper(
OfflineTestCaseMixin, TestCase):
templates_dir = 'test_with_context_variable_inheritance_super'
additional_test_settings = {
'COMPRESS_OFFLINE_CONTEXT': [{
'parent_template': 'base1.html',
}, {
'parent_template': 'base2.html',
}]
}
expected_hash = ['7d1416cab12e', 'a31eb23d0157']
# Block.super not supported for Jinja2 yet.
engines = ('django',)
class OfflineCompressTestCaseWithContextGeneratorImportError(
OfflineTestCaseMixin, TestCase):
templates_dir = 'test_with_context'

View File

@@ -0,0 +1,8 @@
{% load compress %}
{% spaceless %}
{% compress js %}
<script type="text/javascript">
alert("test using template extension passed in variable parent=base.html");
</script>
{% endcompress %}
{% endspaceless %}

View File

@@ -0,0 +1 @@
{% extends parent_template %}

View File

@@ -0,0 +1,7 @@
{% spaceless %}
{% block js %}
<script type="text/javascript">
alert("test using template extension passed in variable parent=base1.html");
</script>
{% endblock %}
{% endspaceless %}

View File

@@ -0,0 +1,7 @@
{% spaceless %}
{% block js %}
<script type="text/javascript">
alert("test using template extension passed in variable parent=base2.html");
</script>
{% endblock %}
{% endspaceless %}

View File

@@ -0,0 +1,8 @@
{% extends parent_template %}
{% load compress %}
{% block js %}{% spaceless %}
{% compress js %}
{{ block.super }}
{% endcompress %}
{% endspaceless %}{% endblock %}

View File

@@ -0,0 +1,8 @@
{% spaceless %}
{% compress js %}
<script type="text/javascript">
alert("test using template extension passed in variable parent=base.html");
</script>
{% endcompress %}
{% endspaceless %}

View File

@@ -0,0 +1 @@
{% extends parent_template %}