Only cache precompilers specified in COMPRESS_CACHEABLE_PRECOMPILERS setting, and refactor caching logic into new CachedCompilerFilter class

This commit is contained in:
Pindi Albert
2012-12-14 13:48:23 -08:00
committed by Johannes Linke
parent ad49d9901d
commit 3bfed73734
6 changed files with 49 additions and 18 deletions

View File

@@ -20,7 +20,7 @@ from compressor.cache import get_hexdigest, get_mtime
from compressor.conf import settings from compressor.conf import settings
from compressor.exceptions import (CompressorError, UncompressableFileError, from compressor.exceptions import (CompressorError, UncompressableFileError,
FilterDoesNotExist) FilterDoesNotExist)
from compressor.filters import CompilerFilter from compressor.filters import CachedCompilerFilter
from compressor.storage import compressor_file_storage from compressor.storage import compressor_file_storage
from compressor.signals import post_compress from compressor.signals import post_compress
from compressor.utils import get_class, get_mod_func, staticfiles from compressor.utils import get_class, get_mod_func, staticfiles
@@ -254,9 +254,9 @@ class Compressor(object):
try: try:
mod = import_module(mod_name) mod = import_module(mod_name)
except (ImportError, TypeError): except (ImportError, TypeError):
filter = CompilerFilter( filter = CachedCompilerFilter(
content, filter_type=self.type, filename=filename, content=content, filter_type=self.type, filename=filename,
charset=charset, command=filter_or_command) charset=charset, command=filter_or_command, mimetype=mimetype)
return True, filter.input(**kwargs) return True, filter.input(**kwargs)
try: try:
precompiler_class = getattr(mod, cls_name) precompiler_class = getattr(mod, cls_name)

View File

@@ -135,6 +135,10 @@ def get_hashed_content(filename, length=12):
return get_hexdigest(file.read(), length) return get_hexdigest(file.read(), length)
def get_precompiler_cachekey(command, contents):
return hashlib.sha1('precompiler.%s.%s' % (command, contents)).hexdigest()
def cache_get(key): def cache_get(key):
packed_val = cache.get(key) packed_val = cache.get(key)
if packed_val is None: if packed_val is None:

View File

@@ -35,6 +35,7 @@ class CompressorConf(AppConf):
# ('text/stylus', 'stylus < {infile} > {outfile}'), # ('text/stylus', 'stylus < {infile} > {outfile}'),
# ('text/x-scss', 'sass --scss {infile} {outfile}'), # ('text/x-scss', 'sass --scss {infile} {outfile}'),
) )
CACHEABLE_PRECOMPILERS = ()
CLOSURE_COMPILER_BINARY = 'java -jar compiler.jar' CLOSURE_COMPILER_BINARY = 'java -jar compiler.jar'
CLOSURE_COMPILER_ARGUMENTS = '' CLOSURE_COMPILER_ARGUMENTS = ''
CSSTIDY_BINARY = 'csstidy' CSSTIDY_BINARY = 'csstidy'

View File

@@ -1,3 +1,3 @@
# flake8: noqa # flake8: noqa
from compressor.filters.base import (FilterBase, CallbackOutputFilter, from compressor.filters.base import (FilterBase, CallbackOutputFilter,
CompilerFilter, FilterError) CompilerFilter, CachedCompilerFilter, FilterError)

View File

@@ -1,11 +1,9 @@
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
import io import io
import hashlib
import logging import logging
import subprocess import subprocess
from platform import system from platform import system
from django.core.cache import get_cache
if system() != "Windows": if system() != "Windows":
try: try:
@@ -29,6 +27,8 @@ except ImportError:
from django.utils.encoding import smart_text from django.utils.encoding import smart_text
from django.utils import six from django.utils import six
from compressor.cache import cache, get_precompiler_cachekey
from compressor.conf import settings from compressor.conf import settings
from compressor.exceptions import FilterError from compressor.exceptions import FilterError
from compressor.utils import get_mod_func from compressor.utils import get_mod_func
@@ -36,8 +36,6 @@ from compressor.utils import get_mod_func
logger = logging.getLogger("compressor.filters") logger = logging.getLogger("compressor.filters")
cache = get_cache(settings.COMPRESS_CACHE_BACKEND)
class FilterBase(object): class FilterBase(object):
""" """
@@ -144,10 +142,6 @@ class CompilerFilter(FilterBase):
self.infile = self.outfile = None self.infile = self.outfile = None
def input(self, **kwargs): def input(self, **kwargs):
content_hash = hashlib.sha1(self.command + self.content.encode('utf8')).hexdigest()
data = cache.get(content_hash)
if data:
return data
encoding = self.default_encoding encoding = self.default_encoding
options = dict(self.options) options = dict(self.options)
@@ -217,5 +211,26 @@ class CompilerFilter(FilterBase):
self.infile.close() self.infile.close()
if self.outfile is not None: if self.outfile is not None:
self.outfile.close() self.outfile.close()
cache.set(content_hash, filtered, settings.COMPRESS_REBUILD_TIMEOUT)
return smart_text(filtered) return smart_text(filtered)
class CachedCompilerFilter(CompilerFilter):
def __init__(self, mimetype, **kwargs):
self.mimetype = mimetype
super(CachedCompilerFilter, self).__init__(**kwargs)
def input(self, **kwargs):
if self.mimetype in settings.COMPRESS_CACHEABLE_PRECOMPILERS:
key = self.get_cache_key()
data = cache.get(key)
if data:
return data
filtered = super(CachedCompilerFilter, self).input(**kwargs)
cache.set(key, filtered, settings.COMPRESS_REBUILD_TIMEOUT)
return filtered
else:
return super(CachedCompilerFilter, self).input(**kwargs)
def get_cache_key(self):
return get_precompiler_cachekey(self.command, self.content.encode('utf8'))

View File

@@ -14,7 +14,7 @@ from compressor.cache import get_hashed_mtime, get_hashed_content
from compressor.conf import settings from compressor.conf import settings
from compressor.css import CssCompressor from compressor.css import CssCompressor
from compressor.utils import find_command from compressor.utils import find_command
from compressor.filters.base import CompilerFilter from compressor.filters.base import CompilerFilter, CachedCompilerFilter
from compressor.filters.cssmin import CSSMinFilter, rCSSMinFilter from compressor.filters.cssmin import CSSMinFilter, rCSSMinFilter
from compressor.filters.css_default import CssAbsoluteFilter from compressor.filters.css_default import CssAbsoluteFilter
from compressor.filters.jsmin import JSMinFilter from compressor.filters.jsmin import JSMinFilter
@@ -51,6 +51,7 @@ class PrecompilerTestCase(TestCase):
def setUp(self): def setUp(self):
self.test_precompiler = os.path.join(test_dir, 'precompiler.py') self.test_precompiler = os.path.join(test_dir, 'precompiler.py')
self.setup_infile() self.setup_infile()
settings.COMPRESS_CACHEABLE_PRECOMPILERS = ('text/css',)
def setup_infile(self, filename='static/css/one.css'): def setup_infile(self, filename='static/css/one.css'):
self.filename = os.path.join(test_dir, filename) self.filename = os.path.join(test_dir, filename)
@@ -104,18 +105,28 @@ class PrecompilerTestCase(TestCase):
def test_precompiler_cache(self): def test_precompiler_cache(self):
command = '%s %s -f {infile} -o {outfile}' % (sys.executable, self.test_precompiler) command = '%s %s -f {infile} -o {outfile}' % (sys.executable, self.test_precompiler)
compiler = CompilerFilter(content=self.content, filename=self.filename, command=command) compiler = CachedCompilerFilter(content=self.content, filename=self.filename, command=command, mimetype='text/css')
self.assertEqual(u"body { color:#990; }", compiler.input()) self.assertEqual(u"body { color:#990; }", compiler.input())
# We tell whether the precompiler actually ran by inspecting compiler.infile. If not None, the compiler had to # We tell whether the precompiler actually ran by inspecting compiler.infile. If not None, the compiler had to
# write the input out to the file for the external command. If None, it was in the cache and thus skipped. # write the input out to the file for the external command. If None, it was in the cache and thus skipped.
self.assertIsNotNone(compiler.infile) # Not cached self.assertIsNotNone(compiler.infile) # Not cached
compiler = CompilerFilter(content=self.content, filename=self.filename, command=command) compiler = CachedCompilerFilter(content=self.content, filename=self.filename, command=command, mimetype='text/css')
self.assertEqual(u"body { color:#990; }", compiler.input()) self.assertEqual(u"body { color:#990; }", compiler.input())
self.assertIsNone(compiler.infile) # Cached self.assertIsNone(compiler.infile) # Cached
self.content += ' ' # Invalidate cache by slightly changing content self.content += ' ' # Invalidate cache by slightly changing content
compiler = CompilerFilter(content=self.content, filename=self.filename, command=command) compiler = CachedCompilerFilter(content=self.content, filename=self.filename, command=command, mimetype='text/css')
self.assertEqual(u"body { color:#990; }", compiler.input())
self.assertIsNotNone(compiler.infile) # Not cached
def test_precompiler_not_cacheable(self):
command = '%s %s -f {infile} -o {outfile}' % (sys.executable, self.test_precompiler)
compiler = CachedCompilerFilter(content=self.content, filename=self.filename, command=command, mimetype='text/different')
self.assertEqual(u"body { color:#990; }", compiler.input())
self.assertIsNotNone(compiler.infile) # Not cached
compiler = CachedCompilerFilter(content=self.content, filename=self.filename, command=command, mimetype='text/different')
self.assertEqual(u"body { color:#990; }", compiler.input()) self.assertEqual(u"body { color:#990; }", compiler.input())
self.assertIsNotNone(compiler.infile) # Not cached self.assertIsNotNone(compiler.infile) # Not cached