Commit 7833e1ce by andreas.pelme

Totally changed the way bump_filename works. More info coming on the wiki.


git-svn-id: https://django-compress.googlecode.com/svn/trunk@56 98d35234-f74b-0410-9e22-51d878bdf110
parent f32ce754
from compress.conf import settings
from compress.utils import needs_update, filter_css, filter_js
if settings.COMPRESS and settings.COMPRESS_AUTO:
for css in settings.COMPRESS_CSS.values():
if needs_update(css['output_filename'], css['source_filenames']):
filter_css(css)
for js in settings.COMPRESS_JS.values():
if needs_update(js['output_filename'], js['source_filenames']):
filter_js(js)
\ No newline at end of file
from django.core.exceptions import ImproperlyConfigured
from django.conf import settings
COMPRESS = getattr(settings, 'COMPRESS', not settings.DEBUG)
COMPRESS_AUTO = getattr(settings, 'COMPRESS_AUTO', True)
COMPRESS_AUTO_TEMPLATES = getattr(settings, 'COMPRESS_AUTO_TEMPLATES', True)
COMPRESS_VERSION = getattr(settings, 'COMPRESS_VERSION', False)
COMPRESS_VERSION_PLACEHOLDER = getattr(settings, 'COMPRESS_VERSION_PLACEHOLDER', '?')
COMPRESS_VERSION_DEFAULT = getattr(settings, 'COMPRESS_VERSION_DEFAULT', '0')
COMPRESS_CSS_FILTERS = getattr(settings, 'COMPRESS_CSS_FILTERS', ('compress.filters.csstidy.CSSTidyFilter', ))
COMPRESS_JS_FILTERS = getattr(settings, 'COMPRESS_JS_FILTERS', ('compress.filters.jsmin.JSMinFilter',))
......@@ -10,7 +13,10 @@ COMPRESS_CSS = getattr(settings, 'COMPRESS_CSS', {})
COMPRESS_JS = getattr(settings, 'COMPRESS_JS', {})
if COMPRESS_CSS_FILTERS is None:
COMPRESS_CSS_FILTERS = ()
COMPRESS_CSS_FILTERS = []
if COMPRESS_JS_FILTERS is None:
COMPRESS_JS_FILTERS = ()
\ No newline at end of file
COMPRESS_JS_FILTERS = []
if COMPRESS_VERSION and not COMPRESS_AUTO:
raise ImproperlyConfigured('COMPRESS_AUTO needs to be True when using COMPRESS_VERSION.')
\ No newline at end of file
......@@ -21,23 +21,31 @@ class Command(NoArgsCommand):
from compress.utils import needs_update, filter_css, filter_js
for name, css in settings.COMPRESS_CSS.items():
if force or needs_update(css['output_filename'], css['source_filenames']):
u, version = needs_update(css['output_filename'], css['source_filenames'])
if verbosity >= 1:
print ("Updating CSS group %s..." % name),
if (force or u) or verbosity >= 2:
msg = 'CSS Group \'%s\'' % name
print msg
print len(msg) * '-'
print "Version: %s" % version
filter_css(css, verbose=(verbosity >= 2))
if force or u:
filter_css(css, verbosity)
if verbosity >= 1:
print "done."
if (force or u) or verbosity >= 2:
print
for name, js in settings.COMPRESS_JS.items():
if force or needs_update(js['output_filename'], js['source_filenames']):
u, version = needs_update(js['output_filename'], js['source_filenames'])
if verbosity >= 1:
print ("Updating JavaScript group %s..." % name),
if (force or u) or verbosity >= 2:
msg = 'JavaScript Group \'%s\'' % name
print msg
print len(msg) * '-'
print "Version: %s" % version
filter_js(js, verbose=(verbosity >= 2))
if force or u:
filter_js(js, verbosity)
if verbosity >= 1:
print "done."
\ No newline at end of file
if (force or u) or verbosity >= 2:
print
\ No newline at end of file
css_filtered = object()
js_filtered = object()
\ No newline at end of file
import os
from django.utils.http import urlquote
from django import template
from django.conf import settings as django_settings
from compress.conf import settings
from compress.utils import media_root, needs_update, filter_css, filter_js
from compress.utils import media_root, media_url, needs_update, filter_css, filter_js, get_output_filename, get_version
register = template.Library()
def render_common(template_name, obj, filename):
url = django_settings.MEDIA_URL + urlquote(filename)
if settings.COMPRESS and obj.get('bump_filename', False):
try:
url += '?%d' % os.stat(media_root(filename)).st_mtime
except:
# do not output specified file if stat() fails
# the URL could be cached forever at the client
# this will (probably) make the problem visible, while not aborting the entire rendering
return ''
def render_common(template_name, obj, filename, version):
if settings.COMPRESS:
filename = get_output_filename(filename, get_version(version))
context = obj.get('extra_context', {})
context['url'] = url
context['url'] = media_url(filename)
return template.loader.render_to_string(template_name, context)
def render_css(css, filename):
return render_common(css.get('template_name', 'compress/css.html'), css, filename)
def render_css(css, filename, version=None):
return render_common(css.get('template_name', 'compress/css.html'), css, filename, version)
def render_js(js, filename):
return render_common(js.get('template_name', 'compress/js.html'), js, filename)
def render_js(js, filename, version=None):
return render_common(js.get('template_name', 'compress/js.html'), js, filename, version)
class CompressedCSSNode(template.Node):
def __init__(self, name):
......@@ -49,10 +38,16 @@ class CompressedCSSNode(template.Node):
if settings.COMPRESS:
if settings.COMPRESS_AUTO_TEMPLATES and needs_update(css['output_filename'], css['source_filenames']):
filter_css(css)
if settings.COMPRESS:
return render_css(css, css['output_filename'])
version = None
if settings.COMPRESS_AUTO:
u, version = needs_update(css['output_filename'], css['source_filenames'])
if u:
filter_css(css)
return render_css(css, css['output_filename'], version)
else:
# output source files
r = ''
......@@ -75,10 +70,14 @@ class CompressedJSNode(template.Node):
if settings.COMPRESS:
if settings.COMPRESS_AUTO_TEMPLATES and needs_update(js['output_filename'], js['source_filenames']):
filter_js(js)
version = None
if settings.COMPRESS_AUTO:
u, version = needs_update(js['output_filename'], js['source_filenames'])
if u:
filter_js(js)
return render_js(js, js['output_filename'])
return render_js(js, js['output_filename'], version)
else:
# output source files
r = ''
......
import os
import re
from compress.conf import settings
from django.conf import settings as django_settings
from django.utils.http import urlquote
from django.dispatch import dispatcher
from compress.conf import settings
from compress.signals import css_filtered, js_filtered
def get_filter(compressor_class):
"""
Convert a string version of a function name to the callable object.
If the lookup_view is not an import path, it is assumed to be a URL pattern
label and the original string is returned.
If can_fail is True, lookup_view might be a URL pattern label, so errors
during the import fail and the string is returned.
"""
if not hasattr(compressor_class, '__bases__'):
try:
# Bail early for non-ASCII strings (they can't be functions).
compressor_class = compressor_class.encode('ascii')
mod_name, class_name = get_mod_func(compressor_class)
if class_name != '':
......@@ -36,28 +34,27 @@ def get_mod_func(callback):
return callback, ''
return callback[:dot], callback[dot+1:]
def needs_update(compressed_file, source_files):
def needs_update(output_file, source_files):
"""
Scan the source files for changes and returns True if the compressed_file needs to be updated.
compressed_file and source_files should be given with full paths
Scan the source files for changes and returns True if the output_file needs to be updated.
"""
compressed_file_full = media_root(compressed_file)
if not os.path.exists(compressed_file_full):
return True
compressed_file_mtime = os.stat(compressed_file_full).st_mtime
mtime = max_mtime(source_files)
version = get_version(mtime)
compressed_file_full = media_root(get_output_filename(output_file, version))
for source_file in source_files:
if compressed_file_mtime < os.stat(media_root(source_file)).st_mtime:
return True
if not os.path.exists(compressed_file_full):
return True, version
return False
# Check if the output file is outdated
return (os.stat(compressed_file_full).st_mtime < mtime), mtime
def media_root(filename):
return os.path.join(django_settings.MEDIA_ROOT, filename)
def media_url(filename):
return django_settings.MEDIA_URL + filename
def media_url(url):
return django_settings.MEDIA_URL + urlquote(url)
def write_tmpfile(content):
try:
......@@ -90,24 +87,54 @@ def concat(filenames, separator=''):
fd.close()
return r
def max_mtime(files):
return int(max([os.stat(media_root(f)).st_mtime for f in files]))
def save_file(filename, contents):
fd = open(media_root(filename), 'w+')
fd.write(contents)
fd.close()
def filter_css(css, verbose=False):
output = concat(css['source_filenames'])
def get_output_filename(filename, version):
if settings.COMPRESS_VERSION:
return filename.replace(settings.COMPRESS_VERSION_PLACEHOLDER, version)
else:
return filename.replace(settings.COMPRESS_VERSION_PLACEHOLDER, settings.COMPRESS_VERSION_DEFAULT)
def get_version(mtime):
return str(int(mtime))
def remove_files(path, filename, verbosity=0):
escaped_filename = settings.COMPRESS_VERSION_PLACEHOLDER.join([re.escape(part) for part in filename.split(settings.COMPRESS_VERSION_PLACEHOLDER)])
regex = r'^%s$' % (os.path.basename(get_output_filename(escaped_filename, r'\d+')))
for f in os.listdir(path):
if re.match(regex, f):
if verbosity >= 1:
print "Removing outdated file %s" % f
os.unlink(os.path.join(path, f))
def filter_common(obj, verbosity, filters, attr, separator, signal):
output = concat(obj['source_filenames'], separator)
filename = get_output_filename(obj['output_filename'], get_version(max_mtime(obj['source_filenames'])))
if settings.COMPRESS_VERSION:
remove_files(os.path.dirname(media_root(filename)), obj['output_filename'], verbosity)
for f in settings.COMPRESS_CSS_FILTERS:
output = get_filter(f)(verbose=verbose).filter_css(output)
if verbosity >= 1:
print "Saving %s" % filename
save_file(css['output_filename'], output)
for f in filters:
output = getattr(get_filter(f)(verbose=(verbosity >= 2)), attr)(output)
def filter_js(js, verbose=False):
output = concat(js['source_filenames'], ';') # add a ; between each files to make sure every file is properly "closed"
save_file(filename, output)
dispatcher.send(signal=signal)
for f in settings.COMPRESS_JS_FILTERS:
output = get_filter(f)(verbose=verbose).filter_js(output)
def filter_css(css, verbosity=0):
return filter_common(css, verbosity, filters=settings.COMPRESS_CSS_FILTERS, attr='filter_css', separator='', signal=css_filtered)
save_file(js['output_filename'], output)
\ No newline at end of file
def filter_js(js, verbosity=0):
return filter_common(js, verbosity, filters=settings.COMPRESS_JS_FILTERS, attr='filter_js', separator=';', signal=js_filtered)
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment