diff --git a/compressor/base.py b/compressor/base.py index 44c07ed..03c1d9e 100644 --- a/compressor/base.py +++ b/compressor/base.py @@ -21,6 +21,7 @@ from compressor.conf import settings from compressor.exceptions import (CompressorError, UncompressableFileError, FilterDoesNotExist) from compressor.filters import CachedCompilerFilter +from compressor.filters.css_default import CssAbsoluteFilter from compressor.storage import compressor_file_storage from compressor.signals import post_compress from compressor.utils import get_class, get_mod_func, staticfiles @@ -211,20 +212,24 @@ class Compressor(object): precompiled, value = self.precompile(value, **options) if enabled: - yield self.filter(value, **options) + yield self.filter(value, self.cached_filters, **options) + elif precompiled: + # since precompiling moves files around, it breaks url() + # statements in css files. therefore we run the absolute filter + # on precompiled css files even if compression is disabled. + if CssAbsoluteFilter in self.cached_filters: + value = self.filter(value, [CssAbsoluteFilter], **options) + yield self.handle_output(kind, value, forced=True, + basename=basename) else: - if precompiled: - yield self.handle_output(kind, value, forced=True, - basename=basename) - else: - yield self.parser.elem_str(elem) + yield self.parser.elem_str(elem) def filter_output(self, content): """ Passes the concatenated content to the 'output' methods of the compressor filters. """ - return self.filter(content, method=METHOD_OUTPUT) + return self.filter(content, self.cached_filters, method=METHOD_OUTPUT) def filter_input(self, forced=False): """ @@ -275,8 +280,8 @@ class Compressor(object): filename=filename) return True, filter.input(**kwargs) - def filter(self, content, method, **kwargs): - for filter_cls in self.cached_filters: + def filter(self, content, filters, method, **kwargs): + for filter_cls in filters: filter_func = getattr( filter_cls(content, filter_type=self.type), method) try: diff --git a/compressor/tests/static/css/relative_url.css b/compressor/tests/static/css/relative_url.css new file mode 100644 index 0000000..c690cde --- /dev/null +++ b/compressor/tests/static/css/relative_url.css @@ -0,0 +1 @@ +p { background: url('../img/python.png'); } \ No newline at end of file diff --git a/compressor/tests/test_base.py b/compressor/tests/test_base.py index 108b34d..c96f398 100644 --- a/compressor/tests/test_base.py +++ b/compressor/tests/test_base.py @@ -48,9 +48,55 @@ class TestPrecompiler(object): return 'OUTPUT' +class PassthroughPrecompiler(object): + """A filter whose outputs the input unmodified """ + def __init__(self, content, attrs, filter_type=None, filename=None, + charset=None): + self.content = content + + def input(self, **kwargs): + return self.content + + test_dir = os.path.abspath(os.path.join(os.path.dirname(__file__))) +class PrecompilerAndAbsoluteFilterTestCase(SimpleTestCase): + + def setUp(self): + self.html_orig = '' + self.html_link_to_precompiled_css = '' + self.html_link_to_absolutized_css = '' + self.css_orig = "p { background: url('../img/python.png'); }" # content of relative_url.css + self.css_absolutized = "p { background: url('/static/img/python.png?c2281c83670e'); }" + + def helper(self, enabled, use_precompiler, use_absolute_filter, expected_output): + precompiler = (('text/css', 'compressor.tests.test_base.PassthroughPrecompiler'),) if use_precompiler else () + filters = ('compressor.filters.css_default.CssAbsoluteFilter',) if use_absolute_filter else () + + with self.settings(COMPRESS_ENABLED=enabled, COMPRESS_PRECOMPILERS=precompiler, COMPRESS_CSS_FILTERS=filters): + css_node = CssCompressor(self.html_orig) + output = list(css_node.hunks())[0] + self.assertEqual(output, expected_output) + + @override_settings(COMPRESS_CSS_HASHING_METHOD="content") + def test_precompiler_enables_absolute(self): + """ + Tests whether specifying a precompiler also runs the CssAbsoluteFilter even if + compression is disabled, but only if the CssAbsoluteFilter is actually contained + in the filters setting. + While at it, ensure that everything runs as expected when compression is enabled. + """ + self.helper(enabled=False, use_precompiler=False, use_absolute_filter=False, expected_output=self.html_orig) + self.helper(enabled=False, use_precompiler=False, use_absolute_filter=True, expected_output=self.html_orig) + self.helper(enabled=False, use_precompiler=True, use_absolute_filter=False, expected_output=self.html_link_to_precompiled_css) + self.helper(enabled=False, use_precompiler=True, use_absolute_filter=True, expected_output=self.html_link_to_absolutized_css) + self.helper(enabled=True, use_precompiler=False, use_absolute_filter=False, expected_output=self.css_orig) + self.helper(enabled=True, use_precompiler=False, use_absolute_filter=True, expected_output=self.css_absolutized) + self.helper(enabled=True, use_precompiler=True, use_absolute_filter=False, expected_output=self.css_orig) + self.helper(enabled=True, use_precompiler=True, use_absolute_filter=True, expected_output=self.css_absolutized) + + class CompressorTestCase(SimpleTestCase): def setUp(self):