Ticket #15213: Enabled-compression-of-static-files-using-an-external-programs.patch

File Enabled-compression-of-static-files-using-an-external-programs.patch, 6.4 KB (added by Sebastian Noack, 14 years ago)
  • django/conf/global_settings.py

    From 7432380f5cff8abd8a389b4227440c87a272052d Mon Sep 17 00:00:00 2001
    From: Sebastian Noack <sebastian.noack@gmail.com>
    Date: Wed, 2 Feb 2011 12:47:16 +0100
    Subject: [PATCH] Enabled compression of static files using an external program (e.g. yui-compressor).
    
    ---
     django/conf/global_settings.py                     |    4 ++
     .../management/commands/collectstatic.py           |   52 ++++++++++++++++++++
     django/core/files/base.py                          |   16 +++---
     3 files changed, 65 insertions(+), 7 deletions(-)
    
    diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
    index 0017f46..7676f5a 100644
    a b STATICFILES_FINDERS = (  
    579579#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
    580580)
    581581
     582# Mapping from filename extensions to external programs (e.g. yui-compressor)
     583# used to compress static files with that extension.
     584STATICFILES_COMPRESSORS = {}
     585
    582586# URL prefix for admin media -- CSS, JavaScript and images.
    583587# Make sure to use a trailing slash.
    584588# Examples: "http://foo.com/static/admin/", "/static/admin/".
  • django/contrib/staticfiles/management/commands/collectstatic.py

    diff --git a/django/contrib/staticfiles/management/commands/collectstatic.py b/django/contrib/staticfiles/management/commands/collectstatic.py
    index 54eaab0..2bb8332 100644
    a b  
    11import os
    22import sys
    33import shutil
     4import subprocess
    45from optparse import make_option
    56
    67from django.conf import settings
     8from django.core.files.base import File
    79from django.core.files.storage import get_storage_class
    810from django.core.management.base import CommandError, NoArgsCommand
    911
    class Command(NoArgsCommand):  
    2931            dest='use_default_ignore_patterns', default=True,
    3032            help="Don't ignore the common private glob-style patterns 'CVS', "
    3133                "'.*' and '*~'."),
     34        make_option('--no-compress', action='store_false',
     35            dest='compress', default=True,
     36            help="Don't compress files, using the programs configured by the "
     37                "STATICFILES_COMPRESSORS setting."),
    3238    )
    3339    help = "Collect static files from apps and other locations in a single location."
    3440
    class Command(NoArgsCommand):  
    3642        super(NoArgsCommand, self).__init__(*args, **kwargs)
    3743        self.copied_files = []
    3844        self.symlinked_files = []
     45        self.compressed_files = []
    3946        self.unmodified_files = []
    4047        self.storage = get_storage_class(settings.STATICFILES_STORAGE)()
    4148        try:
    class Command(NoArgsCommand):  
    4956
    5057    def handle_noargs(self, **options):
    5158        symlink = options['link']
     59        compress = options['compress']
    5260        ignore_patterns = options['ignore_patterns']
    5361        if options['use_default_ignore_patterns']:
    5462            ignore_patterns += ['CVS', '.*', '*~']
    Type 'yes' to continue, or 'no' to cancel: """ % settings.STATIC_ROOT)  
    8290                    prefixed_path = os.path.join(storage.prefix, path)
    8391                else:
    8492                    prefixed_path = path
     93
     94                if compress:
     95                    ext = os.path.splitext(path)[1][len(os.path.extsep):]
     96                    compressor = settings.STATICFILES_COMPRESSORS.get(ext)
     97                    if compressor is not None:
     98                        self.compress_file(path, prefixed_path, storage, compressor, **options)
     99                        continue
     100
    85101                if symlink:
    86102                    self.link_file(path, prefixed_path, storage, **options)
    87103                else:
    Type 'yes' to continue, or 'no' to cancel: """ % settings.STATIC_ROOT)  
    200216                self.storage.save(prefixed_path, source_file)
    201217        if not prefixed_path in self.copied_files:
    202218            self.copied_files.append(prefixed_path)
     219
     220    def compress_file(self, path, prefixed_path, source_storage, compressor, **options):
     221        """
     222        Attempt to put a compressed version to ``path`` with storage
     223        """
     224        # Skip this file if it was already compressed earlier
     225        if prefixed_path in self.compressed_files:
     226            return self.log("Skipping '%s' (already compressed earlier)" % path)
     227        # Delete the target file if needed or break
     228        if not self.delete_file(path, prefixed_path, source_storage, **options):
     229            return
     230        # The full path of the source file
     231        source_path = source_storage.path(path)
     232        # Finally compress the file
     233        if options['dry_run']:
     234            self.log("Pretending to compress '%s'" % source_path, level=1)
     235        else:
     236            self.log("Compressing '%s'" % source_path, level=1)
     237            if self.local:
     238                full_path = self.storage.path(prefixed_path)
     239                try:
     240                    os.makedirs(os.path.dirname(full_path))
     241                except OSError:
     242                    pass
     243                subprocess.Popen(compressor, shell=True,
     244                                 stdin=open(source_path, 'rb'),
     245                                 stdout=open(full_path, 'wb')).wait()
     246                shutil.copystat(source_path, full_path)
     247            else:
     248                process = subprocess.Popen(compressor, shell=True,
     249                                           stdin=open(source_path, 'rb'),
     250                                           stdout=subprocess.PIPE)
     251                self.storage.save(prefixed_path, File(process.stdout))
     252                process.wait()
     253        if prefixed_path not in self.compressed_files:
     254            self.compressed_files.append(prefixed_path)
  • django/core/files/base.py

    diff --git a/django/core/files/base.py b/django/core/files/base.py
    index 6204d71..95231f2 100644
    a b class File(FileProxyMixin):  
    5959        if not chunk_size:
    6060            chunk_size = self.DEFAULT_CHUNK_SIZE
    6161
    62         if hasattr(self, 'seek'):
     62        try:
    6363            self.seek(0)
    64         # Assume the pointer is at zero...
    65         counter = self.size
    66 
    67         while counter > 0:
    68             yield self.read(chunk_size)
    69             counter -= chunk_size
     64        except (AttributeError, IOError):
     65            pass
     66
     67        while True:
     68            data = self.read(chunk_size)
     69            if not data:
     70                break
     71            yield data
    7072
    7173    def multiple_chunks(self, chunk_size=None):
    7274        """
Back to Top