Skip to content

Commit

Permalink
Use COMPRESS_OFFLINE_CONTEXT in offline templates discovery
Browse files Browse the repository at this point in the history
Test case for template inheritance with variable from context and super
Test case for template inheritance with variable from context
  • Loading branch information
grzegorzswica committed May 11, 2016
1 parent 512ecf2 commit c0010bf
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 50 deletions.
80 changes: 43 additions & 37 deletions compressor/management/commands/compress.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -154,6 +154,18 @@ def compress(self, log=None, **options):
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:
Expand All @@ -176,16 +188,22 @@ def compress(self, log=None, **options):
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(
Expand All @@ -198,36 +216,23 @@ def compress(self, log=None, **options):
"\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 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 template, nodes in compressor_nodes.items():
template._log = log
template._log_verbosity = verbosity

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)
Expand All @@ -247,6 +252,7 @@ def compress(self, log=None, **options):

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
Expand Down
16 changes: 6 additions & 10 deletions compressor/offline/django.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,12 @@ def render_nodelist(self, template, context, node):
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))
Expand All @@ -141,13 +137,13 @@ def get_nodelist(self, node, original):
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
2 changes: 1 addition & 1 deletion compressor/offline/jinja2.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def get_nodelist(self, node):

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
Expand Down
42 changes: 40 additions & 2 deletions compressor/tests/test_offline.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,14 @@ def tearDown(self):
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):
Expand Down Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
@@ -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 %}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{% extends parent_template %}
Original file line number Diff line number Diff line change
@@ -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 %}
Original file line number Diff line number Diff line change
@@ -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 %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{% extends parent_template %}
{% load compress %}

{% block js %}{% spaceless %}
{% compress js %}
{{ block.super }}
{% endcompress %}
{% endspaceless %}{% endblock %}
Original file line number Diff line number Diff line change
@@ -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 %}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{% extends parent_template %}

0 comments on commit c0010bf

Please sign in to comment.