summaryrefslogtreecommitdiff
path: root/compressor/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'compressor/base.py')
-rw-r--r--compressor/base.py145
1 files changed, 69 insertions, 76 deletions
diff --git a/compressor/base.py b/compressor/base.py
index 860b0f2..0f88d10 100644
--- a/compressor/base.py
+++ b/compressor/base.py
@@ -1,127 +1,116 @@
import os
+from itertools import chain
from django.template.loader import render_to_string
from django.core.files.base import ContentFile
-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_class
+from compressor.storage import default_storage
+from compressor.utils import get_class, cached_property
class Compressor(object):
- def __init__(self, content, output_prefix="compressed"):
- self.content = content
+ def __init__(self, content=None, output_prefix="compressed"):
+ self.content = content or ""
+ self.extra_context = {}
self.type = None
self.output_prefix = output_prefix
self.split_content = []
- self._parser = None
+ self.charset = settings.DEFAULT_CHARSET
def split_contents(self):
- raise NotImplementedError('split_contents must be defined in a subclass')
+ raise NotImplementedError(
+ "split_contents must be defined in a subclass")
def get_filename(self, url):
try:
base_url = self.storage.base_url
except AttributeError:
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))
+ 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.COMPRESS_ROOT, basename)
if not os.path.exists(filename):
- raise UncompressableFileError('"%s" does not exist' % filename)
+ raise UncompressableFileError("'%s' does not exist" % filename)
return filename
- def _get_parser(self):
- if self._parser:
- return self._parser
- parser_cls = get_class(settings.COMPRESS_PARSER)
- self._parser = parser_cls(self.content)
- return self._parser
+ @cached_property
+ def parser(self):
+ return get_class(settings.COMPRESS_PARSER)(self.content)
- def _set_parser(self, parser):
- self._parser = parser
- parser = property(_get_parser, _set_parser)
+ @cached_property
+ def cached_filters(self):
+ return [get_class(filter_cls) for filter_cls in self.filters]
- @property
+ @cached_property
def mtimes(self):
- return [get_mtime(h[1]) for h in self.split_contents() if h[0] == 'file']
+ for kind, value, elem in self.split_contents():
+ if kind == 'file':
+ yield str(get_mtime(value))
- @property
+ @cached_property
def cachekey(self):
- cachebits = [self.content]
- cachebits.extend([str(m) for m in self.mtimes])
- cachestr = "".join(cachebits).encode(settings.DEFAULT_CHARSET)
+ cachestr = "".join(
+ chain([self.content], self.mtimes)).encode(self.charset)
return "django_compressor.%s" % get_hexdigest(cachestr)[:12]
- @property
+ @cached_property
def storage(self):
- from compressor.storage import default_storage
return default_storage
- @property
+ @cached_property
def hunks(self):
- if getattr(self, '_hunks', ''):
- return self._hunks
- self._hunks = []
- for kind, v, elem in self.split_contents():
+ for kind, value, elem in self.split_contents():
attribs = self.parser.elem_attribs(elem)
- if kind == 'hunk':
- input = v
- if self.filters:
- input = self.filter(input, 'input', elem=elem)
+ if kind == "hunk":
# Let's cast BeautifulSoup element to unicode here since
# it will try to encode using ascii internally later
- self._hunks.append(unicode(input))
- if kind == 'file':
- # TODO: wrap this in a try/except for IoErrors(?)
- fd = open(v, 'rb')
- input = fd.read()
- if self.filters:
- input = self.filter(input, 'input', filename=v, elem=elem)
- charset = attribs.get('charset', settings.DEFAULT_CHARSET)
- self._hunks.append(unicode(input, charset))
- fd.close()
- return self._hunks
+ yield unicode(self.filter(value, "input", elem=elem))
+ elif kind == "file":
+ content = ""
+ try:
+ fd = open(value, 'rb')
+ try:
+ content = fd.read()
+ finally:
+ fd.close()
+ except IOError, e:
+ raise UncompressableFileError(
+ "IOError while processing '%s': %s" % (value, e))
+ content = self.filter(content, "input", filename=value, elem=elem)
+ yield unicode(content, attribs.get("charset", self.charset))
def concat(self):
- # 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(settings.DEFAULT_CHARSET) for hunk in self.hunks])
+ return "\n".join((hunk.encode(self.charset) for hunk in self.hunks))
def filter(self, content, method, **kwargs):
- for f in self.filters:
- filter = getattr(get_class(f)(content, filter_type=self.type), method)
+ for filter_cls in self.cached_filters:
+ filter_func = getattr(
+ filter_cls(content, filter_type=self.type), method)
try:
- if callable(filter):
- content = filter(**kwargs)
+ if callable(filter_func):
+ content = filter_func(**kwargs)
except NotImplementedError:
pass
return content
- @property
+ @cached_property
def combined(self):
- if getattr(self, '_output', ''):
- return self._output
- output = self.concat()
- if self.filters:
- output = self.filter(output, 'output')
- self._output = output
- return self._output
-
- @property
+ return self.filter(self.concat(), 'output')
+
+ @cached_property
def hash(self):
return get_hexdigest(self.combined)[:12]
- @property
+ @cached_property
def new_filepath(self):
- filename = "".join([self.hash, self.extension])
- return os.path.join(
- settings.COMPRESS_OUTPUT_DIR.strip(os.sep), self.output_prefix, filename)
+ return os.path.join(settings.COMPRESS_OUTPUT_DIR.strip(os.sep),
+ self.output_prefix, "%s.%s" % (self.hash, self.type))
def save_file(self):
if self.storage.exists(self.new_filepath):
@@ -129,16 +118,20 @@ class Compressor(object):
self.storage.save(self.new_filepath, ContentFile(self.combined))
return True
- def output(self):
- if not settings.COMPRESS_ENABLED:
+ def output(self, forced=False):
+ if not settings.COMPRESS_ENABLED and not forced:
return self.content
- self.save_file()
- context = getattr(self, 'extra_context', {})
- context['url'] = self.storage.url(self.new_filepath)
+ context = {
+ "saved": self.save_file(),
+ "url": self.storage.url(self.new_filepath),
+ }
+ context.update(self.extra_context)
return render_to_string(self.template_name, context)
def output_inline(self):
- context = {'content': settings.COMPRESS_ENABLED and self.combined or self.concat()}
- if hasattr(self, 'extra_context'):
- context.update(self.extra_context)
+ if settings.COMPRESS_ENABLED:
+ content = self.combined
+ else:
+ content = self.concat()
+ context = dict(content=content, **self.extra_context)
return render_to_string(self.template_name_inline, context)