diff --git a/compressor/base.py b/compressor/base.py index 5d83044..f47fd83 100644 --- a/compressor/base.py +++ b/compressor/base.py @@ -36,7 +36,7 @@ class Compressor(object): """ raise NotImplementedError - def get_filename(self, url): + def get_basename(self, url): try: base_url = self.storage.base_url except AttributeError: @@ -48,6 +48,9 @@ class Compressor(object): basename = url.replace(base_url, "", 1) # drop the querystring, which is used for non-compressed cache-busting. basename = basename.split("?", 1)[0] + return basename + + def get_filename(self, basename): # first try to find it with staticfiles (in debug mode) filename = None if settings.DEBUG and self.finders: @@ -76,7 +79,8 @@ class Compressor(object): @cached_property def mtimes(self): return [str(get_mtime(value)) - for kind, value, _ in self.split_contents() if kind == 'file'] + for kind, value, _, _ in self.split_contents() + if kind == 'file'] @cached_property def cachekey(self): @@ -86,10 +90,11 @@ class Compressor(object): @cached_property def hunks(self): - for kind, value, elem in self.split_contents(): + for kind, value, basename, elem in self.split_contents(): if kind == "hunk": yield unicode(self.filter( - value, method="input", elem=elem, kind=kind)) + value, basename=basename, method="input", elem=elem, + kind=kind)) elif kind == "file": content = "" fd = open(value, 'rb') @@ -101,7 +106,8 @@ class Compressor(object): finally: fd.close() content = self.filter(content, - method="input", filename=value, elem=elem, kind=kind) + method="input", filename=value, basename=basename, + elem=elem, kind=kind) attribs = self.parser.elem_attribs(elem) charset = attribs.get("charset", self.charset) yield unicode(content, charset) diff --git a/compressor/css.py b/compressor/css.py index d7922b5..69116f9 100644 --- a/compressor/css.py +++ b/compressor/css.py @@ -1,3 +1,5 @@ +import os + from compressor.conf import settings from compressor.base import Compressor from compressor.exceptions import UncompressableFileError @@ -22,13 +24,14 @@ class CssCompressor(Compressor): elem_attribs = self.parser.elem_attribs(elem) if elem_name == 'link' and elem_attribs['rel'] == 'stylesheet': try: - filename = self.get_filename(elem_attribs['href']) - data = ('file', filename, elem) + basename = self.get_basename(elem_attribs['href']) + filename = self.get_filename(basename) + data = ('file', filename, basename, elem) except UncompressableFileError: if settings.DEBUG: raise elif elem_name == 'style': - data = ('hunk', self.parser.elem_content(elem), elem) + data = ('hunk', self.parser.elem_content(elem), None, elem) if data: self.split_content.append(data) media = elem_attribs.get('media', None) diff --git a/compressor/filters/css_default.py b/compressor/filters/css_default.py index 8e9a672..5a3cd3e 100644 --- a/compressor/filters/css_default.py +++ b/compressor/filters/css_default.py @@ -5,18 +5,20 @@ import posixpath from compressor.cache import get_hashed_mtime from compressor.conf import settings from compressor.filters import FilterBase +from compressor.utils import staticfiles URL_PATTERN = re.compile(r'url\(([^\)]+)\)') class CssAbsoluteFilter(FilterBase): - def input(self, filename=None, **kwargs): + def input(self, filename=None, basename=None, **kwargs): self.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(self.root): + if not (filename and filename.startswith(self.root)) and \ + not self.find(basename): return self.content - self.path = filename[len(self.root):].replace(os.sep, '/') + self.path = basename.replace(os.sep, '/') self.path = self.path.lstrip('/') self.url = settings.COMPRESS_URL.rstrip('/') self.url_path = self.url @@ -36,6 +38,10 @@ class CssAbsoluteFilter(FilterBase): output = URL_PATTERN.sub(self.url_converter, self.content) return output + def find(self, basename): + if settings.DEBUG and basename and staticfiles.finders: + return staticfiles.finders.find(basename) + def guess_filename(self, url): local_path = url if self.has_http: diff --git a/compressor/js.py b/compressor/js.py index 4657f00..e28b102 100644 --- a/compressor/js.py +++ b/compressor/js.py @@ -1,3 +1,5 @@ +import os + from compressor.conf import settings from compressor.base import Compressor from compressor.exceptions import UncompressableFileError @@ -19,12 +21,14 @@ class JsCompressor(Compressor): attribs = self.parser.elem_attribs(elem) if 'src' in attribs: try: + basename = self.get_basename(attribs['src']) + filename = self.get_filename(basename) self.split_content.append( - ('file', self.get_filename(attribs['src']), elem)) + ('file', filename, basename, elem)) except UncompressableFileError: if settings.DEBUG: raise else: content = self.parser.elem_content(elem) - self.split_content.append(('hunk', content, elem)) + self.split_content.append(('hunk', content, None, elem)) return self.split_content diff --git a/compressor/tests/tests.py b/compressor/tests/tests.py index 242ab5e..9cd51a4 100644 --- a/compressor/tests/tests.py +++ b/compressor/tests/tests.py @@ -55,12 +55,12 @@ class CompressorTestCase(TestCase): def test_css_split(self): out = [ - ('file', os.path.join(settings.COMPRESS_ROOT, u'css/one.css'), u''), - ('hunk', u'p { border:5px solid green;}', u''), - ('file', os.path.join(settings.COMPRESS_ROOT, u'css/two.css'), u''), + ('file', os.path.join(settings.COMPRESS_ROOT, u'css/one.css'), u'css/one.css', u''), + ('hunk', u'p { border:5px solid green;}', None, u''), + ('file', os.path.join(settings.COMPRESS_ROOT, u'css/two.css'), u'css/two.css', u''), ] split = self.css_node.split_contents() - split = [(x[0], x[1], self.css_node.parser.elem_str(x[2])) for x in split] + split = [(x[0], x[1], x[2], self.css_node.parser.elem_str(x[3])) for x in split] self.assertEqual(out, split) def test_css_hunks(self): @@ -93,11 +93,11 @@ class CompressorTestCase(TestCase): self.assertEqual(output, self.css_node.output().strip()) def test_js_split(self): - out = [('file', os.path.join(settings.COMPRESS_ROOT, u'js/one.js'), ''), - ('hunk', u'obj.value = "value";', '') + out = [('file', os.path.join(settings.COMPRESS_ROOT, u'js/one.js'), u'js/one.js', ''), + ('hunk', u'obj.value = "value";', None, '') ] split = self.js_node.split_contents() - split = [(x[0], x[1], self.js_node.parser.elem_str(x[2])) for x in split] + split = [(x[0], x[1], x[2], self.js_node.parser.elem_str(x[3])) for x in split] self.assertEqual(out, split) def test_js_hunks(self): @@ -164,20 +164,20 @@ class Html5LibParserTests(ParserTestCase, CompressorTestCase): def test_css_split(self): out = [ - ('file', os.path.join(settings.COMPRESS_ROOT, u'css/one.css'), u''), - ('hunk', u'p { border:5px solid green;}', u''), - ('file', os.path.join(settings.COMPRESS_ROOT, u'css/two.css'), u''), + ('file', os.path.join(settings.COMPRESS_ROOT, u'css/one.css'), u'css/one.css', u''), + ('hunk', u'p { border:5px solid green;}', None, u''), + ('file', os.path.join(settings.COMPRESS_ROOT, u'css/two.css'), u'css/two.css', u''), ] split = self.css_node.split_contents() - split = [(x[0], x[1], self.css_node.parser.elem_str(x[2])) for x in split] + split = [(x[0], x[1], x[2], self.css_node.parser.elem_str(x[3])) for x in split] self.assertEqual(out, split) def test_js_split(self): - out = [('file', os.path.join(settings.COMPRESS_ROOT, u'js/one.js'), u''), - ('hunk', u'obj.value = "value";', u'') + out = [('file', os.path.join(settings.COMPRESS_ROOT, u'js/one.js'), u'js/one.js', u''), + ('hunk', u'obj.value = "value";', None, u'') ] split = self.js_node.split_contents() - split = [(x[0], x[1], self.js_node.parser.elem_str(x[2])) for x in split] + split = [(x[0], x[1], x[2], self.js_node.parser.elem_str(x[3])) for x in split] self.assertEqual(out, split) Html5LibParserTests = skipIf( @@ -211,11 +211,11 @@ class CssAbsolutizingTestCase(TestCase): content = "p { background: url('../../images/image.gif') }" 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)) + self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) 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)) + self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) def test_css_absolute_filter_https(self): from compressor.filters.css_default import CssAbsoluteFilter @@ -223,11 +223,11 @@ class CssAbsolutizingTestCase(TestCase): content = "p { background: url('../../images/image.gif') }" 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)) + self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) 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)) + self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) def test_css_absolute_filter_relative_path(self): from compressor.filters.css_default import CssAbsoluteFilter @@ -235,10 +235,10 @@ class CssAbsolutizingTestCase(TestCase): content = "p { background: url('../../images/image.gif') }" 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)) + self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) 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)) + self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) def test_css_hunks(self): hash_dict = {