Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'release/0.6.1'

  • Loading branch information...
commit 5b0fd7e6f5b9c91f8cd2b97249fb3b15d028fb97 2 parents 7130cd7 + 95eef66
@jezdez jezdez authored
View
10 AUTHORS
@@ -15,8 +15,16 @@ Brad Whittington
Chris Adams
David Ziegler
Eugene Mirotin
+Fenn Bailey
Gert Van Gool
+Jason Davies
+Jeremy Dunck
Justin Lilly
Maciek Szczesniak
+Mathieu Pillard
Mehmet S. Catalbas
-Ulrich Petri
+Petar Radosevic
+Philipp Wollermann
+Shabda Raaj
+Thom Linton
+Ulrich Petri
View
2  compressor/__init__.py
@@ -1,4 +1,4 @@
-VERSION = (0, 6, 0, "f", 0) # following PEP 386
+VERSION = (0, 6, 1, "f", 0) # following PEP 386
DEV_N = None
View
15 compressor/base.py
@@ -12,7 +12,7 @@
from compressor.exceptions import CompressorError, UncompressableFileError
from compressor.filters import CompilerFilter
from compressor.storage import default_storage
-from compressor.utils import get_class, cached_property
+from compressor.utils import get_class, cached_property, get_staticfiles_finders
class Compressor(object):
@@ -30,6 +30,7 @@ def __init__(self, content=None, output_prefix="compressed"):
self.split_content = []
self.extra_context = {}
self.all_mimetypes = dict(settings.COMPRESS_PRECOMPILERS)
+ self.finders = get_staticfiles_finders()
def split_contents(self):
"""
@@ -50,9 +51,19 @@ def get_filename(self, url):
basename = url.replace(base_url, "", 1)
# drop the querystring, which is used for non-compressed cache-busting.
basename = basename.split("?", 1)[0]
+ # first try finding the file in the root
filename = os.path.join(settings.COMPRESS_ROOT, basename)
if not os.path.exists(filename):
- raise UncompressableFileError("'%s' does not exist" % filename)
+ # if not found and staticfiles is installed, use it
+ if self.finders:
+ filename = self.finders.find(basename)
+ if filename:
+ return filename
+ # or just raise an exception as the last resort
+ raise UncompressableFileError(
+ "'%s' could not be found in the COMPRESS_ROOT '%s'%s" % (
+ basename, settings.COMPRESS_ROOT,
+ self.finders and " or with staticfiles." or "."))
return filename
@cached_property
View
17 compressor/css.py
@@ -44,14 +44,13 @@ def split_contents(self):
def output(self, *args, **kwargs):
# Populate self.split_content
- self.split_contents()
- if not hasattr(self, 'media_nodes'):
- return super(CssCompressor, self).output(*args, **kwargs)
if (settings.COMPRESS_ENABLED or settings.COMPRESS_PRECOMPILERS or
kwargs.get('forced', False)):
- ret = []
- for media, subnode in self.media_nodes:
- subnode.extra_context.update({'media': media})
- ret.append(subnode.output(*args, **kwargs))
- return ''.join(ret)
- return self.content
+ self.split_contents()
+ if hasattr(self, 'media_nodes'):
+ ret = []
+ for media, subnode in self.media_nodes:
+ subnode.extra_context.update({'media': media})
+ ret.append(subnode.output(*args, **kwargs))
+ return ''.join(ret)
+ return super(CssCompressor, self).output(*args, **kwargs)
View
10 compressor/finders.py
@@ -2,19 +2,17 @@
from compressor.conf import settings
from compressor.storage import CompressorFileStorage
+from compressor.utils import get_staticfiles_finders
-if "django.contrib.staticfiles" in settings.INSTALLED_APPS:
- from django.contrib.staticfiles.finders import BaseStorageFinder
-elif "staticfiles" in settings.INSTALLED_APPS:
- from staticfiles.finders import BaseStorageFinder
-else:
+finders = get_staticfiles_finders()
+if finders is None:
raise ImproperlyConfigured("When using the compressor staticfiles finder"
"either django.contrib.staticfiles or the "
"standalone version django-staticfiles needs "
"to be installed.")
-class CompressorFinder(BaseStorageFinder):
+class CompressorFinder(finders.BaseStorageFinder):
"""
A staticfiles finder that looks in COMPRESS_ROOT
for compressed files, to be used during development
View
2  compressor/settings.py
@@ -78,7 +78,7 @@ def configure_root(self, value):
if ("compressor.finders.CompressorFinder" not in
staticfiles_settings.STATICFILES_FINDERS):
raise ImproperlyConfigured(
- "When using django_compressor together with staticfiles, "
+ "When using Django Compressor together with staticfiles, "
"please add 'compressor.finders.CompressorFinder' to the "
"STATICFILES_FINDERS setting.")
return value
View
75 compressor/templatetags/compress.py
@@ -48,35 +48,64 @@ def cache_set(self, key, val, refreshed=False,
def cache_key(self, compressor):
return "%s.%s.%s" % (compressor.cachekey, self.mode, self.kind)
- def render(self, context, forced=False, debug=False):
+ def debug_mode(self, context):
if settings.COMPRESS_DEBUG_TOGGLE:
# Only check for the debug parameter
# if a RequestContext was used
request = context.get('request', None)
if request is not None:
- debug = settings.COMPRESS_DEBUG_TOGGLE in request.GET
+ return settings.COMPRESS_DEBUG_TOGGLE in request.GET
+
+ def render_offline(self, forced):
+ """
+ If enabled and in offline mode, and not forced or in debug mode
+ check the offline cache and return the result if given
+ """
if (settings.COMPRESS_ENABLED and
- settings.COMPRESS_OFFLINE) and not forced and not debug:
- content = cache.get(get_offline_cachekey(self.nodelist))
- if content:
- return content
- content = self.nodelist.render(context)
- if debug:
- return content
- compressor = self.compressor_cls(content)
- cachekey = self.cache_key(compressor)
- output = self.cache_get(cachekey)
- if output is None or forced:
- try:
- output = compressor.output(self.mode, forced=forced)
- self.cache_set(cachekey, output)
- except:
- if settings.DEBUG or forced:
- from traceback import format_exc
- raise Exception(format_exc())
- else:
- return content
- return output
+ settings.COMPRESS_OFFLINE) and not forced:
+ return cache.get(get_offline_cachekey(self.nodelist))
+
+ def render_cached(self, compressor, forced):
+ """
+ If enabled checks the cache for the given compressor's cache key
+ and return a tuple of cache key and output
+ """
+ if settings.COMPRESS_ENABLED and not forced:
+ cache_key = self.cache_key(compressor)
+ cache_content = self.cache_get(cache_key)
+ return cache_key, cache_content
+ return None, None
+
+ def render(self, context, forced=False):
+ # 1. Check if in debug mode
+ if self.debug_mode(context):
+ return self.nodelist.render(context)
+
+ # 2. Try offline cache.
+ cached_offline = self.render_offline(forced)
+ if cached_offline:
+ return cached_offline
+
+ # 3. Prepare the actual compressor and check cache
+ compressor = self.compressor_cls(self.nodelist.render(context))
+ cache_key, cache_content = self.render_cached(compressor, forced)
+ if cache_content is not None:
+ return cache_content
+
+ # 4. call compressor output method and handle exceptions
+ try:
+ rendered_output = compressor.output(self.mode, forced=forced)
+ if cache_key:
+ self.cache_set(cache_key, rendered_output)
+ return rendered_output
+ except:
+ if settings.DEBUG or forced:
+ # Be very loud about the exception we just encountered
+ from traceback import format_exc
+ raise Exception(format_exc())
+
+ # 5. Or don't do anything in production
+ return self.nodelist.render(context)
@register.tag
def compress(parser, token):
View
14 compressor/utils.py
@@ -531,3 +531,17 @@ def format(self, *args, **kwargs):
params[str(id(item))] = _format_field(value, parts, conv, spec,
want_bytes)
return self._string % params
+
+
+def get_staticfiles_finders():
+ finders = None
+ if ('staticfiles' in settings.INSTALLED_APPS or
+ 'django.contrib.staticfiles' in settings.INSTALLED_APPS):
+ try:
+ from django.contrib.staticfiles import finders
+ except ImportError:
+ try:
+ from staticfiles import finders
+ except ImportError:
+ pass
+ return finders
View
69 docs/changelog.txt
@@ -0,0 +1,69 @@
+Changelog
+=========
+
+0.6.1
+-----
+
+- Fixed staticfiles support to also use its finder API to find files during
+ developement -- when the static files haven't been collected in
+ ``STATIC_ROOT``.
+
+- Fixed regression with the ``COMPRESS`` setting, pre-compilation and
+ staticfiles.
+
+0.6
+---
+
+Major improvements and a lot of bugfixes, some of which are:
+
+- New precompilation support, which allows compilation of files and
+ hunks with easily configurable compilers before calling the actual
+ output filters. See the
+ :ref:`COMPRESS_PRECOMPILERS <compress_precompilers>` for more details.
+
+- New staticfiles support. With the introduction of the staticfiles app
+ to Django 1.3, compressor officially supports finding the files to
+ compress using the app's finder API. Have a look at the documentation
+ about :ref:`remote storages <remote_storages>` in case you want to use
+ those together with compressor.
+
+- New ``compress`` management command which allows pre-running of what the
+ compress template tag does. See the
+ :ref:`pre-compression <pre-compression>` docs for more information.
+
+- Various perfomance improvements by better caching and mtime cheking.
+
+- Deprecated ``COMPRESS_LESSC_BINARY`` setting because it's now
+ superseded by the :ref:`COMPRESS_PRECOMPILERS <compress_precompilers>`
+ setting. Just make sure to use the correct mimetype when linking to less
+ files or adding inline code and add the following to your settings::
+
+ COMPRESS_PRECOMPILERS = (
+ ('text/less', 'lessc {infile} {outfile}'),
+ )
+
+- Added cssmin_ filter (``compressor.filters.CSSMinFilter``) based on
+ Zachary Voase's Python port of the YUI CSS compression algorithm.
+
+- Reimplemented the dog-piling prevention.
+
+- Make sure the CssAbsoluteFilter works for relative paths.
+
+- Added inline render mode. See :ref:`usage <usage>` docs.
+
+- Added ``mtime_cache`` management command to add and/or remove all mtimes
+ from the cache.
+
+- Moved docs to Read The Docs: http://django_compressor.readthedocs.org
+
+- Added optional ``compressor.storage.GzipCompressorFileStorage`` storage
+ backend that gzips of the saved files automatically for easier deployment.
+
+- Reimplemented a few filters on top of the new
+ ``compressor.filters.base.CompilerFilter`` to be a bit more DRY.
+
+- Added tox based test configuration, testing on Django 1.1-1.3 and Python
+ 2.5-2.7.
+
+.. _cssmin: http://pypi.python.org/pypi/cssmin/
+
View
2  docs/conf.py
@@ -50,7 +50,7 @@
# The short X.Y version.
version = '0.6'
# The full version, including alpha/beta/rc tags.
-release = '0.6'
+release = '0.6.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
View
2  docs/index.txt
@@ -40,4 +40,4 @@ Contents
settings
remote-storages
behind-the-scenes
-
+ changelog
View
10 docs/installation.txt
@@ -16,7 +16,7 @@ Installation
default behaviour and make adjustements for your website.
* In case you use Django 1.3's staticfiles_ contrib app (or its standalone
- clone django-staticfiles_) you have to add django_compressor's file finder
+ clone django-staticfiles_) you have to add Django Compressor's file finder
to the ``STATICFILES_FINDERS`` setting::
STATICFILES_FINDERS = (
@@ -58,11 +58,3 @@ Deprecation
This section lists features and settings that are deprecated or removed
in newer versions of Django Compressor.
-* ``COMPRESS_LESSC_BINARY``
- Superseded by the :ref:`COMPRESS_PRECOMPILERS <compress_precompilers>`
- setting. Just make sure to use the correct mimetype when linking to less
- files or adding inline code and add the following to your settings::
-
- COMPRESS_PRECOMPILERS = (
- ('text/less', 'lessc {infile} {outfile}'),
- )
View
2  docs/remote-storages.txt
@@ -1,3 +1,5 @@
+.. _remote_storages:
+
Remote storages
---------------
View
44 docs/settings.txt
@@ -241,6 +241,8 @@ Compressor from performing the actual compression. Only useful for debugging.
.. _RequestContext: http://docs.djangoproject.com/en/dev/ref/templates/api/#django.template.RequestContext
+.. _compress_offline:
+
COMPRESS_OFFLINE
^^^^^^^^^^^^^^^^
@@ -251,20 +253,7 @@ request/response loop -- independent from user requests. This allows to
pre-compress CSS and JavaScript files and works just like the automatic
compression with the ``{% compress %}`` tag.
-To compress the files "offline" and update the offline cache you have
-to use the ``compress`` management command, e.g. during deployment.
-In case you don't use the ``compress`` management command, Django
-Compressor will automatically fallback to the automatic compression.
-
-It'll will look in the templates that can be found with the template
-loader you specify in ``TEMPLATE_LOADERS`` for ``{% compress %}`` blocks
-and use COMPRESS_OFFLINE_CONTEXT_ to render its content. So if you use
-any variables inside the ``{% compress %}`` blocks, make sure to list
-all values you require in COMPRESS_OFFLINE_CONTEXT_.
-
-The result of running the ``compress`` management command will be saved
-in the cache as defined in COMPRESS_CACHE_BACKEND_ for the number of
-seconds defined in COMPRESS_OFFLINE_TIMEOUT_.
+.. _compress_offline_timeout:
COMPRESS_OFFLINE_TIMEOUT
^^^^^^^^^^^^^^^^^^^^^^^^
@@ -275,6 +264,8 @@ The period of time with which the ``compress`` management command stores
the pre-compressed the contents of ``{% compress %}`` template tags in
the cache.
+.. _compress_offline_context:
+
COMPRESS_OFFLINE_CONTEXT
^^^^^^^^^^^^^^^^^^^^^^^^
@@ -282,27 +273,4 @@ COMPRESS_OFFLINE_CONTEXT
The context to be used by the ``compress`` management command when rendering
the contents of ``{% compress %}`` template tags and saving the result in the
-offline cache. It's similar to a template context and should be used if a
-variable is used in the blocks, e.g.:
-
-.. code-block:: django
-
- {% load compress %}
- {% compress js %}
- <script src="{{ path_to_files }}js/one.js" type="text/javascript" charset="utf-8"></script>
- {% endcompress %}
-
-Since this template requires a variable (``path_to_files``) you need to
-specify this in your settings before using the ``compress`` management
-command::
-
- COMPRESS_OFFLINE_CONTEXT = {
- 'path_to_files': '/static/js/',
- }
-
-If not specified the COMPRESS_OFFLINE_CONTEXT will fall back to contain
-the commonly used setting to refer to saved files ``MEDIA_URL``.
-
-For forward compatibility Django Compressor will also add the ``STATIC_URL``
-setting (added in Django 1.3) to the COMPRESS_OFFLINE_CONTEXT_ if it's
-available.
+offline cache.
View
79 docs/usage.txt
@@ -1,10 +1,12 @@
+.. _usage:
+
Usage
=====
.. code-block:: django
{% load compress %}
- {% compress <js/css> %}
+ {% compress <js/css> [<file/infile>] %}
<html of inline or linked JS/CSS>
{% endcompress %}
@@ -44,7 +46,7 @@ Which would be rendered something like:
<script type="text/javascript" src="/static/CACHE/js/3f33b9146e12.js" charset="utf-8"></script>
-Linked files must be accesible via :ref:`COMPRESS_URL <compress_url>`.
+Linked files **must** be accesible via :ref:`COMPRESS_URL <compress_url>`.
If DEBUG is ``True``, off-site files will throw exceptions. If DEBUG is
``False`` they will be silently stripped.
@@ -60,9 +62,82 @@ what it was given, to ease development.
:ref:`COMPRESS_CACHE_BACKEND <compress_cache_backend>` and
Django's `caching documentation`_).
+The compress template tag supports a second arguemnt specifying the output
+mode and defaults to saving the result in a file. Alternatively you can
+pass '``inline``' to the template tag to return the content directly to the
+rendered page, e.g.:
+
+.. code-block:: django
+
+ {% load compress %}
+
+ {% compress js inline %}
+ <script src="/static/js/one.js" type="text/javascript" charset="utf-8"></script>
+ <script type="text/javascript" charset="utf-8">obj.value = "value";</script>
+ {% endcompress %}
+
+would be rendered something like:
+
+ <script type="text/javascript" charset="utf-8">
+ obj = {};
+ obj.value = "value";
+ </script>
+
.. _memcached: http://memcached.org/
.. _caching documentation: http://docs.djangoproject.com/en/1.2/topics/cache/#memcached
+.. _pre-compression:
+
+Pre-compression
+---------------
+
+Django Compressor comes with an optional ``compress`` mangement command to
+run the compression outside of the request/response loop -- independent
+from user requests. This allows to pre-compress CSS and JavaScript files and
+works just like the automatic compression with the ``{% compress %}`` tag.
+
+To compress the files "offline" and update the offline cache you have
+to use the ``compress`` management command, ideally during deployment.
+Also make sure to enable the :ref:`COMPRESS_OFFLINE <compress_offline>`
+setting. In case you don't use the ``compress`` management command, Django
+Compressor will automatically fallback to the automatic compression using
+the template tag.
+
+The command parses all templates that can be found with the template
+loader (as specified in the TEMPLATE_LOADERS_ setting) and looks for
+``{% compress %}`` blocks. It then will use the context as defined in
+:ref:`COMPRESS_OFFLINE_CONTEXT <compress_offline_context>` to render its
+content. So if you use any variables inside the ``{% compress %}`` blocks,
+make sure to list all values you require in ``COMPRESS_OFFLINE_CONTEXT``.
+It's similar to a template context and should be used if a variable is used
+in the blocks, e.g.:
+
+.. code-block:: django
+
+ {% load compress %}
+ {% compress js %}
+ <script src="{{ path_to_files }}js/one.js" type="text/javascript" charset="utf-8"></script>
+ {% endcompress %}
+
+Since this template requires a variable (``path_to_files``) you need to
+specify this in your settings before using the ``compress`` management
+command::
+
+ COMPRESS_OFFLINE_CONTEXT = {
+ 'path_to_files': '/static/js/',
+ }
+
+If not specified, the ``COMPRESS_OFFLINE_CONTEXT`` will by default contain
+the commonly used setting to refer to saved files ``MEDIA_URL`` and
+``STATIC_URL`` (if specified in the settings).
+
+The result of running the ``compress`` management command will be saved
+in the cache defined in :ref:`COMPRESS_CACHE_BACKEND <compress_cache_backend>`
+for the number of seconds defined in the
+:ref:`COMPRESS_OFFLINE_TIMEOUT <compress_offline_timeout>` setting.
+
+.. _TEMPLATE_LOADERS: http://docs.djangoproject.com/en/dev/ref/settings/#template-loaders
+
CSS Notes
---------
Please sign in to comment.
Something went wrong with that request. Please try again.