Adopted AppSettings and moved a few things from utils to the cache module where they belong.
This commit is contained in:
@@ -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)
|
||||
|
@@ -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
3
compressor/conf.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from compressor.settings import CompressorSettings
|
||||
|
||||
settings = CompressorSettings(prefix="COMPRESS")
|
@@ -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
|
@@ -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:
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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]
|
||||
|
@@ -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):
|
||||
|
@@ -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()
|
||||
|
@@ -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
|
||||
|
@@ -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()
|
||||
|
||||
|
@@ -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'
|
||||
|
@@ -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)
|
||||
|
@@ -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)")
|
||||
|
@@ -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)):
|
||||
|
@@ -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
125
compressor/settings.py
Normal 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
|
@@ -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()
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
|
Reference in New Issue
Block a user