Adopted AppSettings and moved a few things from utils to the cache module where they belong.

This commit is contained in:
Jannis Leidel
2011-02-09 04:51:36 +01:00
parent fe840cede0
commit 4b916124a2
23 changed files with 279 additions and 281 deletions

View File

@@ -1,13 +1,13 @@
import os
from django.conf import settings as django_settings
from django.template.loader import render_to_string
from django.core.files.base import ContentFile
from compressor.conf import settings
from compressor import filters
from compressor.cache import get_hexdigest, get_mtime
from compressor.conf import settings
from compressor.exceptions import UncompressableFileError
from compressor.utils import get_hexdigest, get_mtime, get_class
from compressor.utils import get_class
class Compressor(object):
@@ -25,12 +25,12 @@ class Compressor(object):
try:
base_url = self.storage.base_url
except AttributeError:
base_url = settings.URL
base_url = settings.COMPRESS_URL
if not url.startswith(base_url):
raise UncompressableFileError('"%s" is not in COMPRESS_URL ("%s") and can not be compressed' % (url, base_url))
basename = url.replace(base_url, "", 1)
filename = os.path.join(settings.ROOT, basename)
filename = os.path.join(settings.COMPRESS_ROOT, basename)
if not os.path.exists(filename):
raise UncompressableFileError('"%s" does not exist' % filename)
return filename
@@ -38,7 +38,7 @@ class Compressor(object):
def _get_parser(self):
if self._parser:
return self._parser
parser_cls = get_class(settings.PARSER)
parser_cls = get_class(settings.COMPRESS_PARSER)
self._parser = parser_cls(self.content)
return self._parser
@@ -54,7 +54,7 @@ class Compressor(object):
def cachekey(self):
cachebits = [self.content]
cachebits.extend([str(m) for m in self.mtimes])
cachestr = "".join(cachebits).encode(django_settings.DEFAULT_CHARSET)
cachestr = "".join(cachebits).encode(settings.DEFAULT_CHARSET)
return "django_compressor.%s" % get_hexdigest(cachestr)[:12]
@property
@@ -82,7 +82,7 @@ class Compressor(object):
input = fd.read()
if self.filters:
input = self.filter(input, 'input', filename=v, elem=elem)
charset = attribs.get('charset', django_settings.DEFAULT_CHARSET)
charset = attribs.get('charset', settings.DEFAULT_CHARSET)
self._hunks.append(unicode(input, charset))
fd.close()
return self._hunks
@@ -91,7 +91,7 @@ class Compressor(object):
# Design decision needed: either everything should be unicode up to
# here or we encode strings as soon as we acquire them. Currently
# concat() expects all hunks to be unicode and does the encoding
return "\n".join([hunk.encode(django_settings.DEFAULT_CHARSET) for hunk in self.hunks])
return "\n".join([hunk.encode(settings.DEFAULT_CHARSET) for hunk in self.hunks])
def filter(self, content, method, **kwargs):
for f in self.filters:
@@ -121,7 +121,7 @@ class Compressor(object):
def new_filepath(self):
filename = "".join([self.hash, self.extension])
return os.path.join(
settings.OUTPUT_DIR.strip(os.sep), self.output_prefix, filename)
settings.COMPRESS_OUTPUT_DIR.strip(os.sep), self.output_prefix, filename)
def save_file(self):
if self.storage.exists(self.new_filepath):
@@ -130,7 +130,7 @@ class Compressor(object):
return True
def output(self):
if not settings.ENABLED:
if not settings.COMPRESS_ENABLED:
return self.content
self.save_file()
context = getattr(self, 'extra_context', {})
@@ -138,7 +138,7 @@ class Compressor(object):
return render_to_string(self.template_name, context)
def output_inline(self):
context = {'content': settings.ENABLED and self.combined or self.concat()}
context = {'content': settings.COMPRESS_ENABLED and self.combined or self.concat()}
if hasattr(self, 'extra_context'):
context.update(self.extra_context)
return render_to_string(self.template_name_inline, context)

View File

@@ -1,5 +1,35 @@
import os
from django.core.cache import get_cache
from django.utils.encoding import smart_str
from django.utils.hashcompat import sha_constructor
from compressor.conf import settings
cache = get_cache(settings.CACHE_BACKEND)
def get_hexdigest(plaintext):
return sha_constructor(plaintext).hexdigest()
def get_mtime_cachekey(filename):
return "django_compressor.mtime.%s" % filename
def get_offline_cachekey(source):
return ("django_compressor.offline.%s"
% get_hexdigest("".join(smart_str(s) for s in source)))
def get_mtime(filename):
if settings.COMPRESS_MTIME_DELAY:
key = get_mtime_cachekey(filename)
mtime = cache.get(key)
if mtime is None:
mtime = os.path.getmtime(filename)
cache.set(key, mtime, settings.COMPRESS_MTIME_DELAY)
return mtime
return os.path.getmtime(filename)
def get_hashed_mtime(filename, length=12):
filename = os.path.realpath(filename)
mtime = str(int(get_mtime(filename)))
return get_hexdigest(mtime)[:length]
cache = get_cache(settings.COMPRESS_CACHE_BACKEND)

3
compressor/conf.py Normal file
View File

@@ -0,0 +1,3 @@
from compressor.settings import CompressorSettings
settings = CompressorSettings(prefix="COMPRESS")

View File

@@ -1,119 +0,0 @@
import os
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
# Main switch
ENABLED = getattr(settings, 'COMPRESS', not settings.DEBUG)
# Uses the 1.3 STATIC_URL setting by default
URL = getattr(settings, 'COMPRESS_URL', getattr(settings, 'STATIC_URL', None))
# Check for emptyness since STATIC_URL can be None and ''
if URL:
# Then on to extensive testing
ROOT = getattr(settings, 'COMPRESS_ROOT', getattr(settings, 'STATIC_ROOT', None))
if not ROOT:
raise ImproperlyConfigured('The COMPRESS_ROOT setting (or its '
'fallback STATIC_ROOT) must be set.')
# In case staticfiles is used, make sure COMPRESS_URL can be used
# by checking if the the FileSystemFinder is installed, and if is
# checking if COMPRESS_ROOT is in STATICFILES_DIRS to allow finding
# compressed files.
if ("staticfiles" in settings.INSTALLED_APPS or
"django.contrib.staticfiles" in settings.INSTALLED_APPS):
try:
from staticfiles.settings import FINDERS as finders
except ImportError:
finders = []
if not finders:
finders = getattr(settings, 'STATICFILES_FINDERS', [])
if ("django.contrib.staticfiles.finders.FileSystemFinder" not in finders and
"staticfiles.finders.FileSystemFinder" not in finders):
raise ImproperlyConfigured('Please enable the FileSystemFinder '
'finder of the staticfiles app to '
'use it with django_compressor.')
abs_paths = []
for path in getattr(settings, 'STATICFILES_DIRS', []):
if isinstance(path, tuple) or isinstance(path, list): # stupid Python 2.4
path = path[1] # in case the STATICFILES_DIRS setting has a prefix
abs_paths.append(os.path.abspath(path))
if os.path.abspath(ROOT) not in abs_paths:
raise ImproperlyConfigured('Please add COMPRESS_ROOT to the '
'STATICFILES_DIRS setting when using the '
'staticfiles app.')
else:
# Fallback to good ol' time of double meaning
URL, ROOT = settings.MEDIA_URL, settings.MEDIA_ROOT
if not URL.endswith('/'):
raise ImproperlyConfigured('The URL settings (e.g. COMPRESS_URL) '
'must have a trailing slash.')
OUTPUT_DIR = getattr(settings, 'COMPRESS_OUTPUT_DIR', 'cache')
STORAGE = getattr(settings, 'COMPRESS_STORAGE', 'compressor.storage.CompressorFileStorage')
CSS_FILTERS = getattr(settings, 'COMPRESS_CSS_FILTERS', ['compressor.filters.css_default.CssAbsoluteFilter'])
JS_FILTERS = getattr(settings, 'COMPRESS_JS_FILTERS', ['compressor.filters.jsmin.JSMinFilter'])
if CSS_FILTERS is None:
CSS_FILTERS = []
if JS_FILTERS is None:
JS_FILTERS = []
LESSC_BINARY = LESSC_BINARY = getattr(settings, 'COMPRESS_LESSC_BINARY', 'lessc')
CLOSURE_COMPILER_BINARY = getattr(settings, 'COMPRESS_CLOSURE_COMPILER_BINARY', 'java -jar compiler.jar')
CLOSURE_COMPILER_ARGUMENTS = getattr(settings, 'COMPRESS_CLOSURE_COMPILER_ARGUMENTS', '')
CSSTIDY_BINARY = getattr(settings, 'CSSTIDY_BINARY',
getattr(settings, 'COMPRESS_CSSTIDY_BINARY', 'csstidy'))
CSSTIDY_ARGUMENTS = getattr(settings, 'CSSTIDY_ARGUMENTS',
getattr(settings, 'COMPRESS_CSSTIDY_ARGUMENTS', '--template=highest'))
YUI_BINARY = getattr(settings, 'COMPRESS_YUI_BINARY', 'java -jar yuicompressor.jar')
YUI_CSS_ARGUMENTS = getattr(settings, 'COMPRESS_YUI_CSS_ARGUMENTS', '')
YUI_JS_ARGUMENTS = getattr(settings, 'COMPRESS_YUI_JS_ARGUMENTS', '')
DATA_URI_MIN_SIZE = getattr(settings, 'COMPRESS_DATA_URI_MIN_SIZE', 1024)
# rebuilds the cache every 30 days if nothing has changed.
REBUILD_TIMEOUT = getattr(settings, 'COMPRESS_REBUILD_TIMEOUT', 60 * 60 * 24 * 30) # 30 days
# the upper bound on how long any compression should take to be generated
# (used against dog piling, should be a lot smaller than REBUILD_TIMEOUT
MINT_DELAY = getattr(settings, 'COMPRESS_MINT_DELAY', 30) # 30 seconds
# check for file changes only after a delay (in seconds, disabled by default)
MTIME_DELAY = getattr(settings, 'COMPRESS_MTIME_DELAY', None)
# the backend to use when parsing the JavaScript or Stylesheet files
PARSER = getattr(settings, 'COMPRESS_PARSER', 'compressor.parser.BeautifulSoupParser')
# Allows changing verbosity from the settings.
VERBOSE = getattr(settings, "COMPRESS_VERBOSE", False)
# the cache backend to use
CACHE_BACKEND = getattr(settings, 'COMPRESS_CACHE_BACKEND', None)
if CACHE_BACKEND is None:
# If we are on Django 1.3 AND using the new CACHES setting...
if getattr(settings, "CACHES", None):
CACHE_BACKEND = "default"
else:
# fallback for people still using the old CACHE_BACKEND setting
CACHE_BACKEND = settings.CACHE_BACKEND
# enables the offline cache -- a cache that is filled by the compress management command
OFFLINE = getattr(settings, 'COMPRESS_OFFLINE', False)
# invalidates the offline cache after one year
OFFLINE_TIMEOUT = getattr(settings, 'COMPRESS_OFFLINE_TIMEOUT', 60 * 60 * 24 * 365) # 1 year
# The context to be used when compressing the files "offline"
OFFLINE_CONTEXT = getattr(settings, 'COMPRESS_OFFLINE_CONTEXT', {})
if not OFFLINE_CONTEXT:
OFFLINE_CONTEXT = {
'MEDIA_URL': settings.MEDIA_URL,
}
# Adds the 1.3 STATIC_URL setting to the context if available
if getattr(settings, 'STATIC_URL', None):
OFFLINE_CONTEXT['STATIC_URL'] = settings.STATIC_URL

View File

@@ -1,5 +1,3 @@
from django.conf import settings as django_settings
from compressor.conf import settings
from compressor.base import Compressor
from compressor.exceptions import UncompressableFileError
@@ -11,7 +9,7 @@ class CssCompressor(Compressor):
self.extension = ".css"
self.template_name = "compressor/css.html"
self.template_name_inline = "compressor/css_inline.html"
self.filters = list(settings.CSS_FILTERS)
self.filters = list(settings.COMPRESS_CSS_FILTERS)
self.type = 'css'
def split_contents(self):
@@ -27,7 +25,7 @@ class CssCompressor(Compressor):
content = self.parser.elem_content(elem)
data = ('file', self.get_filename(elem_attribs['href']), elem)
except UncompressableFileError:
if django_settings.DEBUG:
if settings.DEBUG:
raise
elif elem_name == 'style':
data = ('hunk', self.parser.elem_content(elem), elem)
@@ -48,7 +46,7 @@ class CssCompressor(Compressor):
self.split_contents()
if not hasattr(self, 'media_nodes'):
return super(CssCompressor, self).output()
if not settings.ENABLED:
if not settings.COMPRESS_ENABLED:
return self.content
ret = []
for media, subnode in self.media_nodes:

View File

@@ -1,12 +1,11 @@
from compressor.exceptions import FilterError
from compressor.utils import get_class, get_mod_func
from compressor.conf import settings
from compressor.exceptions import FilterError
class FilterBase(object):
def __init__(self, content, filter_type=None, verbose=0):
self.type = filter_type
self.content = content
self.verbose = verbose or settings.VERBOSE
self.verbose = verbose or settings.COMPRESS_VERBOSE
def input(self, **kwargs):
raise NotImplementedError

View File

@@ -8,9 +8,9 @@ from compressor.utils import cmd_split
class ClosureCompilerFilter(FilterBase):
def output(self, **kwargs):
arguments = settings.CLOSURE_COMPILER_ARGUMENTS
arguments = settings.COMPRESS_CLOSURE_COMPILER_ARGUMENTS
command = '%s %s' % (settings.CLOSURE_COMPILER_BINARY, arguments)
command = '%s %s' % (settings.COMPRESS_CLOSURE_COMPILER_BINARY, arguments)
try:
p = Popen(cmd_split(command), stdout=PIPE, stdin=PIPE, stderr=PIPE)

View File

@@ -2,23 +2,23 @@ import os
import re
import posixpath
from compressor.filters import FilterBase, FilterError
from compressor.cache import get_hexdigest, get_mtime
from compressor.conf import settings
from compressor.utils import get_hexdigest, get_mtime
from compressor.filters import FilterBase
URL_PATTERN = re.compile(r'url\(([^\)]+)\)')
class CssAbsoluteFilter(FilterBase):
def input(self, filename=None, **kwargs):
media_root = os.path.normcase(os.path.abspath(settings.ROOT))
media_root = os.path.normcase(os.path.abspath(settings.COMPRESS_ROOT))
if filename is not None:
filename = os.path.normcase(os.path.abspath(filename))
if not filename or not filename.startswith(media_root):
return self.content
self.media_path = filename[len(media_root):].replace(os.sep, '/')
self.media_path = self.media_path.lstrip('/')
self.media_url = settings.URL.rstrip('/')
self.media_url = settings.COMPRESS_URL.rstrip('/')
try:
mtime = get_mtime(filename)
self.mtime = get_hexdigest(str(int(mtime)))[:12]

View File

@@ -1,4 +1,4 @@
from compressor.filters import FilterBase, FilterError
from compressor.filters import FilterBase
from compressor.filters.cssmin.cssmin import cssmin
class CSSMinFilter(FilterBase):

View File

@@ -16,7 +16,7 @@ class CSSTidyFilter(FilterBase):
output_file = tempfile.NamedTemporaryFile(mode='w+b')
command = '%s %s %s %s' % (settings.CSSTIDY_BINARY, tmp_file.name, settings.CSSTIDY_ARGUMENTS, output_file.name)
command = '%s %s %s %s' % (settings.COMPRESS_CSSTIDY_BINARY, tmp_file.name, settings.COMPRESS_CSSTIDY_ARGUMENTS, output_file.name)
command_output = Popen(command, shell=True,
stdout=PIPE, stdin=PIPE, stderr=PIPE).communicate()

View File

@@ -1,11 +1,10 @@
import os
import re
import mimetypes
import urlparse
from base64 import b64encode
from compressor.filters import FilterBase
from compressor.conf import settings
from compressor.filters import FilterBase
class DataUriFilter(FilterBase):
"""Filter for embedding media as data: URIs.
@@ -18,7 +17,7 @@ class DataUriFilter(FilterBase):
Don't use this class directly. Use a subclass.
"""
def input(self, filename=None, **kwargs):
if not filename or not filename.startswith(settings.ROOT):
if not filename or not filename.startswith(settings.COMPRESS_ROOT):
return self.content
output = self.content
for url_pattern in self.url_patterns:
@@ -29,13 +28,13 @@ class DataUriFilter(FilterBase):
# strip query string of file paths
if "?" in url:
url = url.split("?")[0]
return os.path.join(settings.ROOT, url[len(settings.URL):])
return os.path.join(settings.COMPRESS_ROOT, url[len(settings.COMPRESS_URL):])
def data_uri_converter(self, matchobj):
url = matchobj.group(1).strip(' \'"')
if not url.startswith('data:'):
path = self.get_file_path(url)
if os.stat(path).st_size <= settings.DATA_URI_MIN_SIZE:
if os.stat(path).st_size <= settings.COMPRESS_DATA_URI_MIN_SIZE:
data = b64encode(open(path, 'rb').read())
return 'url("data:%s;base64,%s")' % (mimetypes.guess_type(path)[0], data)
return 'url("%s")' % url

View File

@@ -3,7 +3,7 @@ import warnings
import tempfile
from compressor.conf import settings
from compressor.filters import FilterBase, FilterError
from compressor.filters import FilterBase
warnings.simplefilter('ignore', RuntimeWarning)
@@ -17,7 +17,7 @@ class LessFilter(FilterBase):
output_file = tempfile.NamedTemporaryFile(mode='w+b')
command = '%s %s %s' % (settings.LESSC_BINARY, tmp_file.name, output_file.name)
command = '%s %s %s' % (settings.COMPRESS_LESSC_BINARY, tmp_file.name, output_file.name)
command_output = os.popen(command).read()

View File

@@ -10,11 +10,11 @@ class YUICompressorFilter(FilterBase):
def output(self, **kwargs):
arguments = ''
if self.type == 'js':
arguments = settings.YUI_JS_ARGUMENTS
arguments = settings.COMPRESS_YUI_JS_ARGUMENTS
if self.type == 'css':
arguments = settings.YUI_CSS_ARGUMENTS
arguments = settings.COMPRESS_YUI_CSS_ARGUMENTS
command = '%s --type=%s %s' % (settings.YUI_BINARY, self.type, arguments)
command = '%s --type=%s %s' % (settings.COMPRESS_YUI_BINARY, self.type, arguments)
if self.verbose:
command += ' --verbose'

View File

@@ -1,5 +1,3 @@
from django.conf import settings as django_settings
from compressor.conf import settings
from compressor.base import Compressor
from compressor.exceptions import UncompressableFileError
@@ -12,7 +10,7 @@ class JsCompressor(Compressor):
self.extension = ".js"
self.template_name = "compressor/js.html"
self.template_name_inline = "compressor/js_inline.html"
self.filters = settings.JS_FILTERS
self.filters = settings.COMPRESS_JS_FILTERS
self.type = 'js'
def split_contents(self):
@@ -24,7 +22,7 @@ class JsCompressor(Compressor):
try:
self.split_content.append(('file', self.get_filename(attribs['src']), elem))
except UncompressableFileError:
if django_settings.DEBUG:
if settings.DEBUG:
raise
else:
content = self.parser.elem_content(elem)

View File

@@ -9,16 +9,15 @@ try:
except ImportError:
from StringIO import StringIO
from django.conf import settings as django_settings
from django.core.management.base import NoArgsCommand, CommandError
from django.template import Context, Template, TemplateDoesNotExist, TemplateSyntaxError
from django.utils.datastructures import SortedDict
from compressor.cache import cache
from compressor.cache import cache, get_offline_cachekey
from compressor.conf import settings
from compressor.exceptions import OfflineGenerationError
from compressor.templatetags.compress import CompressorNode
from compressor.utils import get_offline_cachekey, walk, any, import_module
from compressor.utils import walk, any, import_module
class Command(NoArgsCommand):
@@ -46,7 +45,7 @@ class Command(NoArgsCommand):
from django.template.loader import find_template_source as finder_func
try:
source, name = finder_func('test')
except TemplateDoesNotExist, e:
except TemplateDoesNotExist:
pass
from django.template.loader import template_source_loaders
return template_source_loaders or []
@@ -64,7 +63,7 @@ class Command(NoArgsCommand):
verbosity = int(options.get("verbosity", 0))
if not log:
log = StringIO()
if not django_settings.TEMPLATE_LOADERS:
if not settings.TEMPLATE_LOADERS:
raise OfflineGenerationError("No template loaders defined. You "
"must set TEMPLATE_LOADERS in your "
"settings.")
@@ -76,7 +75,7 @@ class Command(NoArgsCommand):
if get_template_sources is None:
get_template_sources = loader.get_template_sources
paths.update(list(get_template_sources('')))
except (ImportError, AttributeError), e:
except (ImportError, AttributeError):
# Yeah, this didn't work out so well, let's move on
pass
if not paths:
@@ -107,7 +106,7 @@ class Command(NoArgsCommand):
template_file = open(template_name)
try:
template = Template(template_file.read().decode(
django_settings.FILE_CHARSET))
settings.FILE_CHARSET))
finally:
template_file.close()
except IOError: # unreadable file -> ignore
@@ -136,12 +135,12 @@ class Command(NoArgsCommand):
log.write("Compressing... ")
count = 0
results = []
context = Context(settings.OFFLINE_CONTEXT)
context = Context(settings.COMPRESS_OFFLINE_CONTEXT)
for nodes in compressor_nodes.values():
for node in nodes:
key = get_offline_cachekey(node.nodelist)
result = node.render(context, compress=True, offline=False)
cache.set(key, result, settings.OFFLINE_TIMEOUT)
cache.set(key, result, settings.COMPRESS_OFFLINE_TIMEOUT)
results.append(result)
count += 1
log.write("done\nCompressed %d block(s) from %d template(s).\n"
@@ -183,11 +182,11 @@ class Command(NoArgsCommand):
return set([x for x in ext_list if x != '.py'])
def handle_noargs(self, **options):
if not settings.ENABLED and not options.get("force"):
if not settings.COMPRESS_ENABLED and not options.get("force"):
raise CommandError("Compressor is disabled. Set COMPRESS "
"settting to True to enable it "
"(Use -f/--force to override).")
if not settings.OFFLINE:
if not settings.COMPRESS_OFFLINE:
if not options.get("force"):
raise CommandError("Aborting; COMPRESS_OFFLINE is not set. "
"(Use -f/--force to override)")

View File

@@ -4,9 +4,9 @@ from optparse import make_option
from django.core.management.base import NoArgsCommand, CommandError
from compressor.cache import cache
from compressor.cache import cache, get_mtime, get_mtime_cachekey
from compressor.conf import settings
from compressor.utils import get_mtime, get_mtime_cachekey, walk
from compressor.utils import walk
class Command(NoArgsCommand):
help = "Add or remove all mtime values from the cache"
@@ -50,19 +50,19 @@ class Command(NoArgsCommand):
if (options['add'] and options['clean']) or (not options['add'] and not options['clean']):
raise CommandError('Please specify either "--add" or "--clean"')
if not settings.MTIME_DELAY:
if not settings.COMPRESS_MTIME_DELAY:
raise CommandError('mtime caching is currently disabled. Please '
'set the COMPRESS_MTIME_DELAY setting to a number of seconds.')
files_to_add = set()
keys_to_delete = set()
for root, dirs, files in walk(settings.ROOT, followlinks=options['follow_links']):
for root, dirs, files in walk(settings.COMPRESS_ROOT, followlinks=options['follow_links']):
for dir_ in dirs:
if self.is_ignored(dir_):
dirs.remove(dir_)
for filename in files:
common = "".join(root.split(settings.ROOT))
common = "".join(root.split(settings.COMPRESS_ROOT))
if common.startswith(os.sep):
common = common[len(os.sep):]
if self.is_ignored(os.path.join(common, filename)):

View File

@@ -1,9 +1,6 @@
from django.conf import settings as django_settings
from django.utils.encoding import smart_unicode
from compressor.conf import settings
from compressor.exceptions import ParserError
from compressor.utils import get_class
class ParserBase(object):

125
compressor/settings.py Normal file
View File

@@ -0,0 +1,125 @@
import os
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from compressor.utils import AppSettings
class CompressorSettings(AppSettings):
# Main switch
ENABLED = False
# Allows changing verbosity from the settings.
VERBOSE = False
# the backend to use when parsing the JavaScript or Stylesheet files
PARSER = 'compressor.parser.BeautifulSoupParser'
OUTPUT_DIR = 'cache'
STORAGE = 'compressor.storage.CompressorFileStorage'
URL = None
ROOT = None
CSS_FILTERS = ['compressor.filters.css_default.CssAbsoluteFilter']
JS_FILTERS = ['compressor.filters.jsmin.JSMinFilter']
LESSC_BINARY = LESSC_BINARY = 'lessc'
CLOSURE_COMPILER_BINARY = 'java -jar compiler.jar'
CLOSURE_COMPILER_ARGUMENTS = ''
CSSTIDY_BINARY = 'csstidy'
CSSTIDY_ARGUMENTS = '--template=highest'
YUI_BINARY = 'java -jar yuicompressor.jar'
YUI_CSS_ARGUMENTS = ''
YUI_JS_ARGUMENTS = 'COMPRESS_YUI_JS_ARGUMENTS'
DATA_URI_MIN_SIZE = 1024
# the cache backend to use
CACHE_BACKEND = None
# rebuilds the cache every 30 days if nothing has changed.
REBUILD_TIMEOUT = 60 * 60 * 24 * 30 # 30 days
# the upper bound on how long any compression should take to be generated
# (used against dog piling, should be a lot smaller than REBUILD_TIMEOUT
MINT_DELAY = 30 # 30 seconds
# check for file changes only after a delay (in seconds, disabled by default)
MTIME_DELAY = None
# enables the offline cache -- a cache that is filled by the compress management command
OFFLINE = False
# invalidates the offline cache after one year
OFFLINE_TIMEOUT = 60 * 60 * 24 * 365 # 1 year
# The context to be used when compressing the files "offline"
OFFLINE_CONTEXT = {}
def configure_enabled(self, value):
return value or getattr(settings, 'COMPRESS', not self.DEBUG)
def configure_url(self, value):
# Uses the 1.3 STATIC_URL setting by default
url = getattr(settings, 'STATIC_URL', value)
# Check for emptyness since STATIC_URL can be None and ''
if url:
# Then on to extensive testing
root = getattr(settings, 'STATIC_ROOT', None)
if not root:
raise ImproperlyConfigured('The COMPRESS_ROOT setting (or its '
'fallback STATIC_ROOT) must be set.')
# In case staticfiles is used, make sure COMPRESS_URL can be used
# by checking if the the FileSystemFinder is installed, and if is
# checking if COMPRESS_ROOT is in STATICFILES_DIRS to allow finding
# compressed files.
if ("staticfiles" in self.INSTALLED_APPS or
"django.contrib.staticfiles" in self.INSTALLED_APPS):
try:
from staticfiles.conf import settings as staticfiles_settings
finders = staticfiles_settings.STATICFILES_FINDERS
standalone = True
except ImportError:
finders = []
standalone = False
if not finders:
finders = getattr(settings, 'STATICFILES_FINDERS', [])
if ("django.contrib.staticfiles.finders.FileSystemFinder" not in finders and
"staticfiles.finders.FileSystemFinder" not in finders):
raise ImproperlyConfigured(
'Please enable the FileSystemFinder finder of the '
'staticfiles app to use it with django_compressor.')
abs_paths = []
output_path = os.path.join(root, self.COMPRESS_OUTPUT_DIR)
for path in getattr(settings, 'STATICFILES_DIRS', []):
if isinstance(path, tuple) or isinstance(path, list): # stupid Python 2.4
path = path[1] # in case the STATICFILES_DIRS setting has a prefix
abs_paths.append(os.path.abspath(path))
if os.path.abspath(output_path) not in abs_paths:
extension = ((self.COMPRESS_OUTPUT_DIR, output_path),)
if standalone:
from staticfiles.conf import settings as staticfiles_settings
staticfiles_settings.STATICFILES_DIRS += extension
else:
settings.STATICFILES_DIRS += extension
else:
# Fallback to good ol' times of ambiguity
url, root = settings.MEDIA_URL, settings.MEDIA_ROOT
if not url.endswith('/'):
raise ImproperlyConfigured('The URL settings (e.g. COMPRESS_URL) '
'must have a trailing slash.')
self.COMPRESS_ROOT = root
return url
def configure_cache_backend(self, value):
if value is None:
# If we are on Django 1.3 AND using the new CACHES setting...
if getattr(settings, "CACHES", None):
return "default"
# fallback for people still using the old CACHE_BACKEND setting
return settings.CACHE_BACKEND
return value
def configure_offline_context(self, value):
if value:
value = {
'MEDIA_URL': settings.MEDIA_URL,
}
# Adds the 1.3 STATIC_URL setting to the context if available
if getattr(settings, 'STATIC_URL', None):
value['STATIC_URL'] = settings.STATIC_URL
return value

View File

@@ -13,14 +13,14 @@ class CompressorFileStorage(FileSystemStorage):
"""
def __init__(self, location=None, base_url=None, *args, **kwargs):
if location is None:
location = settings.ROOT
location = settings.COMPRESS_ROOT
if base_url is None:
base_url = settings.URL
base_url = settings.COMPRESS_URL
super(CompressorFileStorage, self).__init__(location, base_url,
*args, **kwargs)
class DefaultStorage(LazyObject):
def _setup(self):
self._wrapped = get_storage_class(settings.STORAGE)()
self._wrapped = get_storage_class(settings.COMPRESS_STORAGE)()
default_storage = DefaultStorage()

View File

@@ -2,11 +2,10 @@ import time
from django import template
from compressor.cache import cache, get_offline_cachekey
from compressor.conf import settings
from compressor.css import CssCompressor
from compressor.js import JsCompressor
from compressor.cache import cache
from compressor.conf import settings
from compressor.utils import get_offline_cachekey
OUTPUT_FILE = 'file'
@@ -28,17 +27,17 @@ class CompressorNode(template.Node):
if (time.time() > refresh_time) and not refreshed:
# Store the stale value while the cache
# revalidates for another MINT_DELAY seconds.
self.cache_set(key, val, timeout=settings.MINT_DELAY, refreshed=True)
self.cache_set(key, val, timeout=settings.COMPRESS_MINT_DELAY, refreshed=True)
return None
return val
def cache_set(self, key, val, timeout=settings.REBUILD_TIMEOUT, refreshed=False):
def cache_set(self, key, val, timeout=settings.COMPRESS_REBUILD_TIMEOUT, refreshed=False):
refresh_time = timeout + time.time()
real_timeout = timeout + settings.MINT_DELAY
real_timeout = timeout + settings.COMPRESS_MINT_DELAY
packed_val = (val, refresh_time, refreshed)
return cache.set(key, packed_val, real_timeout)
def render(self, context, compress=settings.ENABLED, offline=settings.OFFLINE):
def render(self, context, compress=settings.COMPRESS_ENABLED, offline=settings.COMPRESS_OFFLINE):
if compress and offline:
key = get_offline_cachekey(self.nodelist)
content = cache.get(key)

View File

@@ -2,24 +2,23 @@ import os
import re
from BeautifulSoup import BeautifulSoup
from django.conf import settings as django_settings
from django.core.cache.backends import dummy
from django.core.files.storage import get_storage_class
from django.template import Template, Context, TemplateSyntaxError
from django.test import TestCase
from compressor import storage
from compressor.cache import get_hashed_mtime
from compressor.conf import settings
from compressor.css import CssCompressor
from compressor.js import JsCompressor
from compressor.management.commands.compress import Command as CompressCommand
from compressor.utils import get_hashed_mtime
class CompressorTestCase(TestCase):
def setUp(self):
settings.ENABLED = True
settings.COMPRESS_ENABLED = True
self.css = """
<link rel="stylesheet" href="/media/css/one.css" type="text/css" charset="utf-8">
<style type="text/css">p { border:5px solid green;}</style>
@@ -35,9 +34,9 @@ class CompressorTestCase(TestCase):
def test_css_split(self):
out = [
('file', os.path.join(settings.ROOT, u'css/one.css'), u'<link rel="stylesheet" href="/media/css/one.css" type="text/css" charset="utf-8" />'),
('file', os.path.join(settings.COMPRESS_ROOT, u'css/one.css'), u'<link rel="stylesheet" href="/media/css/one.css" type="text/css" charset="utf-8" />'),
('hunk', u'p { border:5px solid green;}', u'<style type="text/css">p { border:5px solid green;}</style>'),
('file', os.path.join(settings.ROOT, u'css/two.css'), u'<link rel="stylesheet" href="/media/css/two.css" type="text/css" charset="utf-8" />'),
('file', os.path.join(settings.COMPRESS_ROOT, u'css/two.css'), u'<link rel="stylesheet" href="/media/css/two.css" type="text/css" charset="utf-8" />'),
]
split = self.cssNode.split_contents()
split = [(x[0], x[1], self.cssNode.parser.elem_str(x[2])) for x in split]
@@ -57,7 +56,7 @@ class CompressorTestCase(TestCase):
self.assert_(is_date.match(str(float(date))), "mtimes is returning something that doesn't look like a date: %s" % date)
def test_css_return_if_off(self):
settings.ENABLED = False
settings.COMPRESS_ENABLED = False
self.assertEqual(self.css, self.cssNode.output())
def test_cachekey(self):
@@ -72,7 +71,7 @@ class CompressorTestCase(TestCase):
self.assertEqual(output, self.cssNode.output().strip())
def test_js_split(self):
out = [('file', os.path.join(settings.ROOT, u'js/one.js'), '<script src="/media/js/one.js" type="text/javascript" charset="utf-8"></script>'),
out = [('file', os.path.join(settings.COMPRESS_ROOT, u'js/one.js'), '<script src="/media/js/one.js" type="text/javascript" charset="utf-8"></script>'),
('hunk', u'obj.value = "value";', '<script type="text/javascript" charset="utf-8">obj.value = "value";</script>')
]
split = self.jsNode.split_contents()
@@ -92,7 +91,7 @@ class CompressorTestCase(TestCase):
self.assertEqual(out, self.jsNode.combined)
def test_js_return_if_off(self):
settings.ENABLED = False
settings.COMPRESS_ENABLED = False
self.assertEqual(self.js, self.jsNode.output())
def test_js_return_if_on(self):
@@ -100,17 +99,17 @@ class CompressorTestCase(TestCase):
self.assertEqual(output, self.jsNode.output())
def test_custom_output_dir(self):
old_output_dir = settings.OUTPUT_DIR
settings.OUTPUT_DIR = 'custom'
old_output_dir = settings.COMPRESS_OUTPUT_DIR
settings.COMPRESS_OUTPUT_DIR = 'custom'
output = u'<script type="text/javascript" src="/media/custom/js/3f33b9146e12.js" charset="utf-8"></script>'
self.assertEqual(output, JsCompressor(self.js).output())
settings.OUTPUT_DIR = ''
settings.COMPRESS_OUTPUT_DIR = ''
output = u'<script type="text/javascript" src="/media/js/3f33b9146e12.js" charset="utf-8"></script>'
self.assertEqual(output, JsCompressor(self.js).output())
settings.OUTPUT_DIR = '/custom/nested/'
settings.COMPRESS_OUTPUT_DIR = '/custom/nested/'
output = u'<script type="text/javascript" src="/media/custom/nested/js/3f33b9146e12.js" charset="utf-8"></script>'
self.assertEqual(output, JsCompressor(self.js).output())
settings.OUTPUT_DIR = old_output_dir
settings.COMPRESS_OUTPUT_DIR = old_output_dir
try:
@@ -123,27 +122,27 @@ else:
def test_css_split(self):
out = [
('file', os.path.join(settings.ROOT, u'css/one.css'), u'<link rel="stylesheet" href="/media/css/one.css" type="text/css" charset="utf-8">'),
('file', os.path.join(settings.COMPRESS_ROOT, u'css/one.css'), u'<link rel="stylesheet" href="/media/css/one.css" type="text/css" charset="utf-8">'),
('hunk', u'p { border:5px solid green;}', u'<style type="text/css">p { border:5px solid green;}</style>'),
('file', os.path.join(settings.ROOT, u'css/two.css'), u'<link rel="stylesheet" href="/media/css/two.css" type="text/css" charset="utf-8">'),
('file', os.path.join(settings.COMPRESS_ROOT, u'css/two.css'), u'<link rel="stylesheet" href="/media/css/two.css" type="text/css" charset="utf-8">'),
]
split = self.cssNode.split_contents()
split = [(x[0], x[1], self.cssNode.parser.elem_str(x[2])) for x in split]
self.assertEqual(out, split)
def setUp(self):
self.old_parser = settings.PARSER
settings.PARSER = 'compressor.parser.LxmlParser'
self.old_parser = settings.COMPRESS_PARSER
settings.COMPRESS_PARSER = 'compressor.parser.LxmlParser'
super(LxmlCompressorTestCase, self).setUp()
def tearDown(self):
settings.PARSER = self.old_parser
settings.COMPRESS_PARSER = self.old_parser
class CssAbsolutizingTestCase(TestCase):
def setUp(self):
settings.ENABLED = True
settings.URL = '/media/'
settings.COMPRESS_ENABLED = True
settings.COMPRESS_URL = '/media/'
self.css = """
<link rel="stylesheet" href="/media/css/url/url1.css" type="text/css" charset="utf-8">
<link rel="stylesheet" href="/media/css/url/2/url2.css" type="text/css" charset="utf-8">
@@ -152,43 +151,43 @@ class CssAbsolutizingTestCase(TestCase):
def test_css_absolute_filter(self):
from compressor.filters.css_default import CssAbsoluteFilter
filename = os.path.join(settings.ROOT, 'css/url/test.css')
filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
content = "p { background: url('../../images/image.gif') }"
output = "p { background: url('%simages/image.gif?%s') }" % (settings.URL, get_hashed_mtime(filename))
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
filter = CssAbsoluteFilter(content)
self.assertEqual(output, filter.input(filename=filename))
settings.URL = 'http://media.example.com/'
filename = os.path.join(settings.ROOT, 'css/url/test.css')
output = "p { background: url('%simages/image.gif?%s') }" % (settings.URL, get_hashed_mtime(filename))
settings.COMPRESS_URL = 'http://media.example.com/'
filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
self.assertEqual(output, filter.input(filename=filename))
def test_css_absolute_filter_https(self):
from compressor.filters.css_default import CssAbsoluteFilter
filename = os.path.join(settings.ROOT, 'css/url/test.css')
filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
content = "p { background: url('../../images/image.gif') }"
output = "p { background: url('%simages/image.gif?%s') }" % (settings.URL, get_hashed_mtime(filename))
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
filter = CssAbsoluteFilter(content)
self.assertEqual(output, filter.input(filename=filename))
settings.URL = 'https://media.example.com/'
filename = os.path.join(settings.ROOT, 'css/url/test.css')
output = "p { background: url('%simages/image.gif?%s') }" % (settings.URL, get_hashed_mtime(filename))
settings.COMPRESS_URL = 'https://media.example.com/'
filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
self.assertEqual(output, filter.input(filename=filename))
def test_css_absolute_filter_relative_path(self):
from compressor.filters.css_default import CssAbsoluteFilter
filename = os.path.join(django_settings.TEST_DIR, 'whatever', '..', 'media', 'whatever/../css/url/test.css')
filename = os.path.join(settings.TEST_DIR, 'whatever', '..', 'media', 'whatever/../css/url/test.css')
content = "p { background: url('../../images/image.gif') }"
output = "p { background: url('%simages/image.gif?%s') }" % (settings.URL, get_hashed_mtime(filename))
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
filter = CssAbsoluteFilter(content)
self.assertEqual(output, filter.input(filename=filename))
settings.URL = 'https://media.example.com/'
output = "p { background: url('%simages/image.gif?%s') }" % (settings.URL, get_hashed_mtime(filename))
settings.COMPRESS_URL = 'https://media.example.com/'
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
self.assertEqual(output, filter.input(filename=filename))
def test_css_hunks(self):
hash_dict = {
'hash1': get_hashed_mtime(os.path.join(settings.ROOT, 'css/url/url1.css')),
'hash2': get_hashed_mtime(os.path.join(settings.ROOT, 'css/url/2/url2.css')),
'hash1': get_hashed_mtime(os.path.join(settings.COMPRESS_ROOT, 'css/url/url1.css')),
'hash2': get_hashed_mtime(os.path.join(settings.COMPRESS_ROOT, 'css/url/2/url2.css')),
}
out = [u"p { background: url('/media/images/test.png?%(hash1)s'); }\np { background: url('/media/images/test.png?%(hash1)s'); }\np { background: url('/media/images/test.png?%(hash1)s'); }\np { background: url('/media/images/test.png?%(hash1)s'); }\n" % hash_dict,
u"p { background: url('/media/images/test.png?%(hash2)s'); }\np { background: url('/media/images/test.png?%(hash2)s'); }\np { background: url('/media/images/test.png?%(hash2)s'); }\np { background: url('/media/images/test.png?%(hash2)s'); }\n" % hash_dict]
@@ -197,19 +196,19 @@ class CssAbsolutizingTestCase(TestCase):
class CssDataUriTestCase(TestCase):
def setUp(self):
settings.ENABLED = True
settings.CSS_FILTERS = [
settings.COMPRESS_ENABLED = True
settings.COMPRESS_CSS_FILTERS = [
'compressor.filters.css_default.CssAbsoluteFilter',
'compressor.filters.datauri.CssDataUriFilter',
]
settings.URL = '/media/'
settings.COMPRESS_URL = '/media/'
self.css = """
<link rel="stylesheet" href="/media/css/datauri.css" type="text/css" charset="utf-8">
"""
self.cssNode = CssCompressor(self.css)
def test_data_uris(self):
datauri_hash = get_hashed_mtime(os.path.join(settings.ROOT, 'css/datauri.css'))
datauri_hash = get_hashed_mtime(os.path.join(settings.COMPRESS_ROOT, 'css/datauri.css'))
out = [u'.add { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJvSURBVDjLpZPrS5NhGIf9W7YvBYOkhlkoqCklWChv2WyKik7blnNris72bi6dus0DLZ0TDxW1odtopDs4D8MDZuLU0kXq61CijSIIasOvv94VTUfLiB74fXngup7nvrnvJABJ/5PfLnTTdcwOj4RsdYmo5glBWP6iOtzwvIKSWstI0Wgx80SBblpKtE9KQs/We7EaWoT/8wbWP61gMmCH0lMDvokT4j25TiQU/ITFkek9Ow6+7WH2gwsmahCPdwyw75uw9HEO2gUZSkfyI9zBPCJOoJ2SMmg46N61YO/rNoa39Xi41oFuXysMfh36/Fp0b7bAfWAH6RGi0HglWNCbzYgJaFjRv6zGuy+b9It96N3SQvNKiV9HvSaDfFEIxXItnPs23BzJQd6DDEVM0OKsoVwBG/1VMzpXVWhbkUM2K4oJBDYuGmbKIJ0qxsAbHfRLzbjcnUbFBIpx/qH3vQv9b3U03IQ/HfFkERTzfFj8w8jSpR7GBE123uFEYAzaDRIqX/2JAtJbDat/COkd7CNBva2cMvq0MGxp0PRSCPF8BXjWG3FgNHc9XPT71Ojy3sMFdfJRCeKxEsVtKwFHwALZfCUk3tIfNR8XiJwc1LmL4dg141JPKtj3WUdNFJqLGFVPC4OkR4BxajTWsChY64wmCnMxsWPCHcutKBxMVp5mxA1S+aMComToaqTRUQknLTH62kHOVEE+VQnjahscNCy0cMBWsSI0TCQcZc5ALkEYckL5A5noWSBhfm2AecMAjbcRWV0pUTh0HE64TNf0mczcnnQyu/MilaFJCae1nw2fbz1DnVOxyGTlKeZft/Ff8x1BRssfACjTwQAAAABJRU5ErkJggg=="); }\n.python { background-image: url("/media/img/python.png?%s"); }\n.datauri { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IAAAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1JREFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jqch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0 vr4MkhoXe0rZigAAAABJRU5ErkJggg=="); }\n' % datauri_hash]
self.assertEqual(out, self.cssNode.hunks)
@@ -264,12 +263,12 @@ def render(template_string, context_dict=None):
class TemplatetagTestCase(TestCase):
def setUp(self):
settings.ENABLED = True
settings.COMPRESS_ENABLED = True
def test_empty_tag(self):
template = u"""{% load compress %}{% compress js %}{% block js %}
{% endblock %}{% endcompress %}"""
context = { 'MEDIA_URL': settings.URL }
context = { 'MEDIA_URL': settings.COMPRESS_URL }
self.assertEqual(u'', render(template, context))
def test_css_tag(self):
@@ -279,7 +278,7 @@ class TemplatetagTestCase(TestCase):
<link rel="stylesheet" href="{{ MEDIA_URL }}css/two.css" type="text/css" charset="utf-8">
{% endcompress %}
"""
context = { 'MEDIA_URL': settings.URL }
context = { 'MEDIA_URL': settings.COMPRESS_URL }
out = u'<link rel="stylesheet" href="/media/cache/css/f7c661b7a124.css" type="text/css">'
self.assertEqual(out, render(template, context))
@@ -289,7 +288,7 @@ class TemplatetagTestCase(TestCase):
<style type="text/css">p { border:5px solid green;}</style>
{% endcompress %}
"""
context = { 'MEDIA_URL': settings.URL }
context = { 'MEDIA_URL': settings.COMPRESS_URL }
out = '<link rel="stylesheet" href="/media/cache/css/1c1c0855907b.css" type="text/css">'
self.assertEqual(out, render(template, context))
@@ -299,7 +298,7 @@ class TemplatetagTestCase(TestCase):
<script type="text/javascript" charset="utf-8">obj.value = "value";</script>
{% endcompress %}
"""
context = { 'MEDIA_URL': settings.URL }
context = { 'MEDIA_URL': settings.COMPRESS_URL }
out = u'<script type="text/javascript" src="/media/cache/js/3f33b9146e12.js" charset="utf-8"></script>'
self.assertEqual(out, render(template, context))
@@ -309,7 +308,7 @@ class TemplatetagTestCase(TestCase):
<script type="text/javascript" charset="utf-8">var test_value = "\u2014";</script>
{% endcompress %}
"""
context = { 'MEDIA_URL': settings.URL }
context = { 'MEDIA_URL': settings.COMPRESS_URL }
out = u'<script type="text/javascript" src="/media/cache/js/5d5c0e1cb25f.js" charset="utf-8"></script>'
self.assertEqual(out, render(template, context))
@@ -319,7 +318,7 @@ class TemplatetagTestCase(TestCase):
<script type="text/javascript" charset="utf-8">var test_value = "\u2014";</script>
{% endcompress %}
"""
context = { 'MEDIA_URL': settings.URL }
context = { 'MEDIA_URL': settings.COMPRESS_URL }
out = u'<script type="text/javascript" src="/media/cache/js/40a8e9ffb476.js" charset="utf-8"></script>'
self.assertEqual(out, render(template, context))
@@ -334,7 +333,7 @@ class StorageTestCase(TestCase):
def setUp(self):
self._storage = storage.default_storage
storage.default_storage = get_storage_class('compressor.tests.storage.TestStorage')()
settings.ENABLED = True
settings.COMPRESS_ENABLED = True
def tearDown(self):
storage.default_storage = self._storage
@@ -346,7 +345,7 @@ class StorageTestCase(TestCase):
<link rel="stylesheet" href="{{ MEDIA_URL }}css/two.css" type="text/css" charset="utf-8">
{% endcompress %}
"""
context = { 'MEDIA_URL': settings.URL }
context = { 'MEDIA_URL': settings.COMPRESS_URL }
out = u'<link rel="stylesheet" href="/media/cache/css/5b231a62e9a6.css.gz" type="text/css">'
self.assertEqual(out, render(template, context))
@@ -355,7 +354,7 @@ class VerboseTestCase(CompressorTestCase):
def setUp(self):
super(VerboseTestCase, self).setUp()
settings.VERBOSE = True
settings.COMPRESS_VERBOSE = True
class CacheBackendTestCase(CompressorTestCase):
@@ -369,11 +368,11 @@ class OfflineGenerationTestCase(TestCase):
"""Uses templates/test_compressor_offline.html"""
def setUp(self):
self._old_compress = settings.ENABLED
settings.ENABLED = True
self._old_compress = settings.COMPRESS_ENABLED
settings.COMPRESS_ENABLED = True
def tearDown(self):
settings.ENABLED = self._old_compress
settings.COMPRESS_ENABLED = self._old_compress
def test_offline(self):
count, result = CompressCommand().compress()
@@ -384,8 +383,8 @@ class OfflineGenerationTestCase(TestCase):
])
def test_offline_with_context(self):
self._old_offline_context = settings.OFFLINE_CONTEXT
settings.OFFLINE_CONTEXT = {
self._old_offline_context = settings.COMPRESS_OFFLINE_CONTEXT
settings.COMPRESS_OFFLINE_CONTEXT = {
'color': 'blue',
}
count, result = CompressCommand().compress()
@@ -394,4 +393,4 @@ class OfflineGenerationTestCase(TestCase):
u'<link rel="stylesheet" href="/media/cache/css/8a2405e029de.css" type="text/css">\n',
u'<script type="text/javascript" src="/media/cache/js/bf53fa5b13e2.js" charset="utf-8"></script>',
])
settings.OFFLINE_CONTEXT = self._old_offline_context
settings.COMPRESS_OFFLINE_CONTEXT = self._old_offline_context

View File

@@ -1,16 +1,12 @@
import inspect
import os
import sys
from django.core.exceptions import ImproperlyConfigured
from django.utils.encoding import smart_str
from django.utils.hashcompat import sha_constructor
from compressor.cache import cache
from compressor.conf import settings
from compressor.exceptions import FilterError
from shlex import split as cmd_split
from django.conf import settings
from compressor.exceptions import FilterError
try:
any = any
except NameError:
@@ -20,31 +16,6 @@ except NameError:
return True
return False
def get_hexdigest(plaintext):
return sha_constructor(plaintext).hexdigest()
def get_mtime_cachekey(filename):
return "django_compressor.mtime.%s" % filename
def get_offline_cachekey(source):
return ("django_compressor.offline.%s"
% get_hexdigest("".join(smart_str(s) for s in source)))
def get_mtime(filename):
if settings.MTIME_DELAY:
key = get_mtime_cachekey(filename)
mtime = cache.get(key)
if mtime is None:
mtime = os.path.getmtime(filename)
cache.set(key, mtime, settings.MTIME_DELAY)
return mtime
return os.path.getmtime(filename)
def get_hashed_mtime(filename, length=12):
filename = os.path.realpath(filename)
mtime = str(int(get_mtime(filename)))
return get_hexdigest(mtime)[:length]
def get_class(class_string, exception=FilterError):
"""
Convert a string version of a function name to the callable object.