@@ -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:
|
||||
|
@@ -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:
|
||||
|
@@ -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:
|
||||
|
@@ -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
|
||||
|
@@ -12,4 +12,4 @@ class FinderTestCase(TestCase):
|
||||
|
||||
def test_list_returns_empty_list(self):
|
||||
finder = CompressorFinder()
|
||||
self.assertEquals(finder.list([]), [])
|
||||
self.assertEqual(finder.list([]), [])
|
||||
|
@@ -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'
|
||||
|
Reference in New Issue
Block a user