diff --git a/compressor/base.py b/compressor/base.py index f7672a3..44c07ed 100644 --- a/compressor/base.py +++ b/compressor/base.py @@ -36,17 +36,19 @@ class Compressor(object): Base compressor object to be subclassed for content type depending implementations details. """ - type = None - def __init__(self, content=None, output_prefix=None, context=None, *args, **kwargs): + def __init__(self, content=None, output_prefix=None, + context=None, filters=None, *args, **kwargs): self.content = content or "" # rendered contents of {% compress %} tag self.output_prefix = output_prefix or "compressed" self.output_dir = settings.COMPRESS_OUTPUT_DIR.strip('/') self.charset = settings.DEFAULT_CHARSET self.split_content = [] self.context = context or {} + self.type = output_prefix or "" + self.filters = filters or [] self.extra_context = {} - self.all_mimetypes = dict(settings.COMPRESS_PRECOMPILERS) + self.precompiler_mimetypes = dict(settings.COMPRESS_PRECOMPILERS) self.finders = staticfiles.finders self._storage = None @@ -205,7 +207,7 @@ class Compressor(object): options = dict(options, filename=value) value = self.get_filecontent(value, charset) - if self.all_mimetypes: + if self.precompiler_mimetypes: precompiled, value = self.precompile(value, **options) if enabled: @@ -245,34 +247,33 @@ class Compressor(object): return False, content attrs = self.parser.elem_attribs(elem) mimetype = attrs.get("type", None) - if mimetype: - filter_or_command = self.all_mimetypes.get(mimetype) - if filter_or_command is None: - if mimetype not in ("text/css", "text/javascript"): - raise CompressorError("Couldn't find any precompiler in " - "COMPRESS_PRECOMPILERS setting for " - "mimetype '%s'." % mimetype) - else: - mod_name, cls_name = get_mod_func(filter_or_command) - try: - mod = import_module(mod_name) - except (ImportError, TypeError): - filter = CachedCompilerFilter( - content=content, filter_type=self.type, filename=filename, - charset=charset, command=filter_or_command, mimetype=mimetype) - return True, filter.input(**kwargs) - try: - precompiler_class = getattr(mod, cls_name) - except AttributeError: - raise FilterDoesNotExist('Could not find "%s".' % - filter_or_command) - else: - filter = precompiler_class( - content, attrs, filter_type=self.type, charset=charset, - filename=filename) - return True, filter.input(**kwargs) + if mimetype is None: + return False, content - return False, content + filter_or_command = self.precompiler_mimetypes.get(mimetype) + if filter_or_command is None: + if mimetype in ("text/css", "text/javascript"): + return False, content + raise CompressorError("Couldn't find any precompiler in " + "COMPRESS_PRECOMPILERS setting for " + "mimetype '%s'." % mimetype) + + mod_name, cls_name = get_mod_func(filter_or_command) + try: + mod = import_module(mod_name) + except (ImportError, TypeError): + filter = CachedCompilerFilter( + content=content, filter_type=self.type, filename=filename, + charset=charset, command=filter_or_command, mimetype=mimetype) + return True, filter.input(**kwargs) + try: + precompiler_class = getattr(mod, cls_name) + except AttributeError: + raise FilterDoesNotExist('Could not find "%s".' % filter_or_command) + filter = precompiler_class( + content, attrs, filter_type=self.type, charset=charset, + filename=filename) + return True, filter.input(**kwargs) def filter(self, content, method, **kwargs): for filter_cls in self.cached_filters: diff --git a/compressor/css.py b/compressor/css.py index 45cdcd2..bd3e091 100644 --- a/compressor/css.py +++ b/compressor/css.py @@ -5,10 +5,8 @@ from compressor.conf import settings class CssCompressor(Compressor): def __init__(self, content=None, output_prefix="css", context=None): - super(CssCompressor, self).__init__(content=content, - output_prefix=output_prefix, context=context) - self.filters = list(settings.COMPRESS_CSS_FILTERS) - self.type = output_prefix + filters = list(settings.COMPRESS_CSS_FILTERS) + super(CssCompressor, self).__init__(content, output_prefix, context, filters) def split_contents(self): if self.split_content: diff --git a/compressor/js.py b/compressor/js.py index f60cc7e..651300f 100644 --- a/compressor/js.py +++ b/compressor/js.py @@ -5,9 +5,8 @@ from compressor.base import Compressor, SOURCE_HUNK, SOURCE_FILE class JsCompressor(Compressor): def __init__(self, content=None, output_prefix="js", context=None): - super(JsCompressor, self).__init__(content, output_prefix, context) - self.filters = list(settings.COMPRESS_JS_FILTERS) - self.type = output_prefix + filters = list(settings.COMPRESS_JS_FILTERS) + super(JsCompressor, self).__init__(content, output_prefix, context, filters) def split_contents(self): if self.split_content: diff --git a/compressor/templatetags/compress.py b/compressor/templatetags/compress.py index a409e20..f92d2c7 100644 --- a/compressor/templatetags/compress.py +++ b/compressor/templatetags/compress.py @@ -40,8 +40,7 @@ class CompressorMixin(object): def debug_mode(self, context): if settings.COMPRESS_DEBUG_TOGGLE: - # Only check for the debug parameter - # if a RequestContext was used + # Only check for the debug parameter if a RequestContext was used request = context.get('request', None) if request is not None: return settings.COMPRESS_DEBUG_TOGGLE in request.GET @@ -57,61 +56,56 @@ class CompressorMixin(object): return (settings.COMPRESS_ENABLED and settings.COMPRESS_OFFLINE) or forced - def render_offline(self, context, forced): + def render_offline(self, context): """ If enabled and in offline mode, and not forced check the offline cache and return the result if given """ - if self.is_offline_compression_enabled(forced) and not forced: - key = get_offline_hexdigest(self.get_original_content(context)) - offline_manifest = get_offline_manifest() - if key in offline_manifest: - return offline_manifest[key] - else: - raise OfflineGenerationError('You have offline compression ' - 'enabled but key "%s" is missing from offline manifest. ' - 'You may need to run "python manage.py compress".' % key) + key = get_offline_hexdigest(self.get_original_content(context)) + offline_manifest = get_offline_manifest() + if key in offline_manifest: + return offline_manifest[key] + else: + raise OfflineGenerationError('You have offline compression ' + 'enabled but key "%s" is missing from offline manifest. ' + 'You may need to run "python manage.py compress".' % key) - def render_cached(self, compressor, kind, mode, forced=False): + def render_cached(self, compressor, kind, mode): """ If enabled checks the cache for the given compressor's cache key and return a tuple of cache key and output """ - if settings.COMPRESS_ENABLED and not forced: - cache_key = get_templatetag_cachekey(compressor, mode, kind) - cache_content = cache_get(cache_key) - return cache_key, cache_content - return None, None + cache_key = get_templatetag_cachekey(compressor, mode, kind) + cache_content = cache_get(cache_key) + return cache_key, cache_content def render_compressed(self, context, kind, mode, forced=False): # See if it has been rendered offline - cached_offline = self.render_offline(context, forced=forced) - if cached_offline: - return cached_offline + if self.is_offline_compression_enabled(forced) and not forced: + return self.render_offline(context) # Take a shortcut if we really don't have anything to do - if ((not settings.COMPRESS_ENABLED and - not settings.COMPRESS_PRECOMPILERS) and not forced): + if (not settings.COMPRESS_ENABLED and + not settings.COMPRESS_PRECOMPILERS and not forced): return self.get_original_content(context) context['compressed'] = {'name': getattr(self, 'name', None)} compressor = self.get_compressor(context, kind) - # Prepare the actual compressor and check cache - cache_key, cache_content = self.render_cached(compressor, kind, mode, forced=forced) - if cache_content is not None: - return cache_content + # Check cache + cache_key = None + if settings.COMPRESS_ENABLED and not forced: + cache_key, cache_content = self.render_cached(compressor, kind, mode) + if cache_content is not None: + return cache_content - rendered_output = self.render_output(compressor, mode, forced=forced) + rendered_output = compressor.output(mode, forced=forced) assert isinstance(rendered_output, six.string_types) if cache_key: cache_set(cache_key, rendered_output) return rendered_output - def render_output(self, compressor, mode, forced=False): - return compressor.output(mode, forced=forced) - class CompressorNode(CompressorMixin, template.Node): @@ -124,14 +118,6 @@ class CompressorNode(CompressorMixin, template.Node): def get_original_content(self, context): return self.nodelist.render(context) - def debug_mode(self, context): - if settings.COMPRESS_DEBUG_TOGGLE: - # Only check for the debug parameter - # if a RequestContext was used - request = context.get('request', None) - if request is not None: - return settings.COMPRESS_DEBUG_TOGGLE in request.GET - def render(self, context, forced=False): # Check if in debug mode diff --git a/compressor/tests/test_finder.py b/compressor/tests/test_finder.py index 9cd8673..0420cd4 100644 --- a/compressor/tests/test_finder.py +++ b/compressor/tests/test_finder.py @@ -12,4 +12,4 @@ class FinderTestCase(TestCase): def test_list_returns_empty_list(self): finder = CompressorFinder() - self.assertEquals(finder.list([]), []) + self.assertEqual(finder.list([]), []) diff --git a/compressor/tests/test_offline.py b/compressor/tests/test_offline.py index 23ad134..71f91c0 100644 --- a/compressor/tests/test_offline.py +++ b/compressor/tests/test_offline.py @@ -358,6 +358,24 @@ class OfflineGenerationTestCase(OfflineTestCaseMixin, TestCase): settings.TEMPLATE_LOADERS = old_loaders +class OfflineGenerationEmptyTag(OfflineTestCaseMixin, TestCase): + """ + In case of a compress template tag with no content, an entry + will be added to the manifest with an empty string as value. + This test makes sure there is no recompression happening when + compressor encounters such an emptystring in the manifest. + """ + templates_dir = "basic" + expected_hash = "f5e179b8eca4" + engines = ("django",) + + def _test_offline(self, engine): + count, result = CompressCommand().compress(log=self.log, verbosity=self.verbosity, engine=engine) + manifest = get_offline_manifest() + manifest[list(manifest)[0]] = "" + self.assertEqual(self._render_template(engine), "\n") + + class OfflineGenerationBlockSuperBaseCompressed(OfflineTestCaseMixin, TestCase): template_names = ["base.html", "base2.html", "test_compressor_offline.html"] templates_dir = 'test_block_super_base_compressed'