From 2bdbbc3faf7bed127b843ad37ffa490c5dbf00d4 Mon Sep 17 00:00:00 2001 From: Matt Layman Date: Thu, 22 Nov 2012 20:47:59 -0500 Subject: [PATCH 1/4] Fixed line ending problems by adding a .gitattributes file --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..dfe07704 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto From c749671778972a6d53ef3501c74f18037c52d86d Mon Sep 17 00:00:00 2001 From: Bruno Binet Date: Thu, 29 Nov 2012 14:52:24 +0100 Subject: [PATCH 2/4] Reimplemented the gzip compression as a plugin. Extending the Writer wasn't the complete answer because the static generator also copies some files. Instead, I implemented the work as a plugin that attaches to the finalized event. --- pelican/plugins/gzip_cache.py | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 pelican/plugins/gzip_cache.py diff --git a/pelican/plugins/gzip_cache.py b/pelican/plugins/gzip_cache.py new file mode 100644 index 00000000..784a6ca0 --- /dev/null +++ b/pelican/plugins/gzip_cache.py @@ -0,0 +1,78 @@ +# Copyright (c) 2012 Matt Layman +'''A plugin to create .gz cache files for optimization.''' + +import gzip +import logging +import os + +from pelican import signals + +logger = logging.getLogger(__name__) + +# A list of file types to exclude from possible compression +EXCLUDE_TYPES = [ + # Compressed types + '.bz2', + '.gz', + + # Audio types + '.aac', + '.flac', + '.mp3', + '.wma', + + # Image types + '.gif', + '.jpg', + '.jpeg', + '.png', + + # Video types + '.avi', + '.mov', + '.mp4', +] + +def create_gzip_cache(pelican): + '''Create a gzip cache file for every file that a webserver would + reasonably want to cache (e.g., text type files). + + :param pelican: The Pelican instance + ''' + for dirpath, _, filenames in os.walk(pelican.settings['OUTPUT_PATH']): + for name in filenames: + if should_compress(name): + filepath = os.path.join(dirpath, name) + create_gzip_file(filepath) + +def should_compress(filename): + '''Check if the filename is a type of file that should be compressed. + + :param filename: A file name to check against + ''' + for extension in EXCLUDE_TYPES: + if filename.endswith(extension): + return False + + return True + +def create_gzip_file(filepath): + '''Create a gzipped file in the same directory with a filepath.gz name. + + :param filepath: A file to compress + ''' + compressed_path = filepath + '.gz' + + with open(filepath, 'rb') as uncompressed: + try: + logger.debug('Compressing: %s' % filepath) + compressed = gzip.open(compressed_path, 'wb') + compressed.writelines(uncompressed) + except Exception, ex: + logger.critical('Gzip compression failed: %s' % ex) + finally: + compressed.close() + +def register(): + signals.finalized.connect(create_gzip_cache) + From 3342d3b9b94aa2ae9c4c45456753a3fa800ec791 Mon Sep 17 00:00:00 2001 From: Bruno Binet Date: Thu, 29 Nov 2012 14:49:03 +0100 Subject: [PATCH 3/4] Added some documentation about the gzip plugin. --- docs/plugins.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/plugins.rst b/docs/plugins.rst index f871a88c..c6b18200 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -106,6 +106,7 @@ The following plugins are currently included with Pelican under ``pelican.plugin * `GitHub activity`_ * `Global license`_ * `Gravatar`_ +* `Gzip cache`_ * `HTML tags for reStructuredText`_ * `Related posts`_ * `Sitemap`_ @@ -245,6 +246,17 @@ Alternatively, you can provide an email address from within article metadata:: If the email address is defined via at least one of the two methods above, the ``author_gravatar`` variable is added to the article's context. +Gzip cache +---------- + +Certain web servers (e.g., Nginx) can use a static cache of gzip compressed +files to prevent the server from compressing files during an HTTP call. Since +compression occurs at another time, these compressed files can be compressed +at a higher compression level for increased optimization. + +The ``gzip_cache`` plugin compresses all common text type files into a ``.gz`` +file within the same directory as the original file. + HTML tags for reStructuredText ------------------------------ From 8fe68aa035c9162a612c062e4c43608f4e2e3792 Mon Sep 17 00:00:00 2001 From: Matt Layman Date: Sun, 25 Nov 2012 22:12:54 -0500 Subject: [PATCH 4/4] Added some unit tests for the gzip cache plugin --- tests/test_plugins.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 tests/test_plugins.py diff --git a/tests/test_plugins.py b/tests/test_plugins.py new file mode 100644 index 00000000..f6581018 --- /dev/null +++ b/tests/test_plugins.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +'''Core plugins unit tests''' + +import os +import tempfile + +from pelican.plugins import gzip_cache + +from support import unittest, temporary_folder + +class TestGzipCache(unittest.TestCase): + '''Unit tests for the gzip cache plugin''' + + def test_should_compress(self): + '''Test that some filetypes should compress and others shouldn't.''' + self.assertTrue(gzip_cache.should_compress('foo.html')) + self.assertTrue(gzip_cache.should_compress('bar.css')) + self.assertTrue(gzip_cache.should_compress('baz.js')) + self.assertTrue(gzip_cache.should_compress('foo.txt')) + + self.assertFalse(gzip_cache.should_compress('foo.gz')) + self.assertFalse(gzip_cache.should_compress('bar.png')) + self.assertFalse(gzip_cache.should_compress('baz.mp3')) + self.assertFalse(gzip_cache.should_compress('foo.mov')) + + def test_creates_gzip_file(self): + '''Test that a file matching the input filename with a .gz extension is + created.''' + # The plugin walks over the output content after the finalized signal + # so it is safe to assume that the file exists (otherwise walk would + # not report it). Therefore, create a dummy file to use. + with temporary_folder() as tempdir: + (_, a_html_filename) = tempfile.mkstemp(suffix='.html', dir=tempdir) + gzip_cache.create_gzip_file(a_html_filename) + self.assertTrue(os.path.exists(a_html_filename + '.gz')) +