diff --git a/compressor/management/commands/compress.py b/compressor/management/commands/compress.py
index a664822..230ecba 100644
--- a/compressor/management/commands/compress.py
+++ b/compressor/management/commands/compress.py
@@ -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
diff --git a/compressor/offline/django.py b/compressor/offline/django.py
index 0cae086..620e75c 100644
--- a/compressor/offline/django.py
+++ b/compressor/offline/django.py
@@ -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
diff --git a/compressor/offline/jinja2.py b/compressor/offline/jinja2.py
index feee818..eb896ba 100644
--- a/compressor/offline/jinja2.py
+++ b/compressor/offline/jinja2.py
@@ -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
diff --git a/compressor/tests/test_offline.py b/compressor/tests/test_offline.py
index e5728dc..4620685 100644
--- a/compressor/tests/test_offline.py
+++ b/compressor/tests/test_offline.py
@@ -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([''], 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'
diff --git a/compressor/tests/test_templates/test_with_context_variable_inheritance/base.html b/compressor/tests/test_templates/test_with_context_variable_inheritance/base.html
new file mode 100644
index 0000000..27fc3a6
--- /dev/null
+++ b/compressor/tests/test_templates/test_with_context_variable_inheritance/base.html
@@ -0,0 +1,8 @@
+{% load compress %}
+{% spaceless %}
+ {% compress js %}
+
+ {% endcompress %}
+{% endspaceless %}
diff --git a/compressor/tests/test_templates/test_with_context_variable_inheritance/test_compressor_offline.html b/compressor/tests/test_templates/test_with_context_variable_inheritance/test_compressor_offline.html
new file mode 100644
index 0000000..9879c65
--- /dev/null
+++ b/compressor/tests/test_templates/test_with_context_variable_inheritance/test_compressor_offline.html
@@ -0,0 +1 @@
+{% extends parent_template %}
diff --git a/compressor/tests/test_templates/test_with_context_variable_inheritance_super/base1.html b/compressor/tests/test_templates/test_with_context_variable_inheritance_super/base1.html
new file mode 100644
index 0000000..ca0748a
--- /dev/null
+++ b/compressor/tests/test_templates/test_with_context_variable_inheritance_super/base1.html
@@ -0,0 +1,7 @@
+{% spaceless %}
+{% block js %}
+
+{% endblock %}
+{% endspaceless %}
diff --git a/compressor/tests/test_templates/test_with_context_variable_inheritance_super/base2.html b/compressor/tests/test_templates/test_with_context_variable_inheritance_super/base2.html
new file mode 100644
index 0000000..23434c1
--- /dev/null
+++ b/compressor/tests/test_templates/test_with_context_variable_inheritance_super/base2.html
@@ -0,0 +1,7 @@
+{% spaceless %}
+{% block js %}
+
+{% endblock %}
+{% endspaceless %}
diff --git a/compressor/tests/test_templates/test_with_context_variable_inheritance_super/test_compressor_offline.html b/compressor/tests/test_templates/test_with_context_variable_inheritance_super/test_compressor_offline.html
new file mode 100644
index 0000000..179ddd3
--- /dev/null
+++ b/compressor/tests/test_templates/test_with_context_variable_inheritance_super/test_compressor_offline.html
@@ -0,0 +1,8 @@
+{% extends parent_template %}
+{% load compress %}
+
+{% block js %}{% spaceless %}
+ {% compress js %}
+ {{ block.super }}
+ {% endcompress %}
+{% endspaceless %}{% endblock %}
diff --git a/compressor/tests/test_templates_jinja2/test_with_context_variable_inheritance/base.html b/compressor/tests/test_templates_jinja2/test_with_context_variable_inheritance/base.html
new file mode 100644
index 0000000..ef17a57
--- /dev/null
+++ b/compressor/tests/test_templates_jinja2/test_with_context_variable_inheritance/base.html
@@ -0,0 +1,8 @@
+
+{% spaceless %}
+ {% compress js %}
+
+ {% endcompress %}
+{% endspaceless %}
diff --git a/compressor/tests/test_templates_jinja2/test_with_context_variable_inheritance/test_compressor_offline.html b/compressor/tests/test_templates_jinja2/test_with_context_variable_inheritance/test_compressor_offline.html
new file mode 100644
index 0000000..9879c65
--- /dev/null
+++ b/compressor/tests/test_templates_jinja2/test_with_context_variable_inheritance/test_compressor_offline.html
@@ -0,0 +1 @@
+{% extends parent_template %}