Skip to content
Permalink
Browse files

Allowing to configure executables for filters (fixes #2615).

  • Loading branch information
felixfontein committed Jan 15, 2017
1 parent 6b6e2a9 commit 48ab72f91710e2f611a0d08697d6b1be2fdd5659
Showing with 75 additions and 14 deletions.
  1. +4 −1 CHANGES.txt
  2. +23 −0 nikola/conf.py.in
  3. +36 −13 nikola/filters.py
  4. +12 −0 nikola/nikola.py
@@ -3,8 +3,11 @@ New in master

Features
--------
* Add META_GENERATOR_TAG option in conf.py allowing the meta generator
* Add ``META_GENERATOR_TAG`` option in conf.py allowing the meta generator
to be disabled if needed. (Issue #2628)
* Add ``YUI_COMPRESSOR``, ``CLOSURE_COMPILER``, ``OPTIPNG``, ``JPEG_OPTIM``
and ``HTML_TIDY`` to configure executables for built-in filters.
(Issue #2615)

Bugfixes
--------
@@ -562,6 +562,29 @@ GITHUB_COMMIT_SOURCE = True
# ".jpg": ["jpegoptim --strip-all -m75 -v %s"],
# }

# The executable for the filter "yui_compressor" from nikola.filters can
# be configured here:
# YUI_COMPRESSOR = 'yui-compressor'

# The executable for the filter "closure_compiler" from nikola.filters can
# be configured here:
# CLOSURE_COMPILER = 'closure-compiler'

# The executable for the filter "optipng" from nikola.filters can be
# configured here:
# OPTIPNG = 'optipng'

# The executable for the filter "jpegoptim" from nikola.filters can be
# configured here:
# JPEG_OPTIM = 'jpegoptim'

# The executable for the filters "html_tidy_withconfig", "html_tidy_nowrap",
# "html_tidy_wrap", "html_tidy_wrap_attr" and "html_tidy_mini" from
# nikola.filters can be configured here:
# HTML_TIDY = 'tidy5'



# Expert setting! Create a gzipped copy of each generated file. Cheap server-
# side optimization for very high traffic sites or low memory servers.
# GZIP_FILES = False
@@ -45,6 +45,19 @@
from .utils import req_missing, LOGGER


class configurable_filter(object):
"""Allow Nikola to configure filter with site's config."""

def __init__(self, **configuration_variables):
"""Define which arguments to configure from which configuration variables."""
self.configuration_variables = configuration_variables

def __call__(self, f):
"""Store configuration_variables as attribute of function."""
f.configuration_variables = self.configuration_variables
return f


def apply_to_binary_file(f):
"""Apply a filter to a binary file.
@@ -126,14 +139,16 @@ def runinplace(command, infile):
shutil.rmtree(tmpdir)


def yui_compressor(infile):
@configurable_filter(executable='YUI_COMPRESSOR')
def yui_compressor(infile, executable=None):
"""Run YUI Compressor on a file."""
yuicompressor = False
try:
subprocess.call('yui-compressor', stdout=open(os.devnull, 'w'), stderr=open(os.devnull, 'w'))
yuicompressor = 'yui-compressor'
except Exception:
pass
yuicompressor = executable
if not yuicompressor:
try:
subprocess.call('yui-compressor', stdout=open(os.devnull, 'w'), stderr=open(os.devnull, 'w'))
yuicompressor = 'yui-compressor'
except Exception:
pass
if not yuicompressor:
try:
subprocess.call('yuicompressor', stdout=open(os.devnull, 'w'), stderr=open(os.devnull, 'w'))
@@ -145,41 +160,49 @@ def yui_compressor(infile):
return runinplace('{} --nomunge %1 -o %2'.format(yuicompressor), infile)


def closure_compiler(infile):
@configurable_filter(executable='CLOSURE_COMPILER')
def closure_compiler(infile, executable='closure-compiler'):
"""Run closure-compiler on a file."""
return runinplace('closure-compiler --warning_level QUIET --js %1 --js_output_file %2', infile)
return runinplace('{} --warning_level QUIET --js %1 --js_output_file %2'.format(executable), infile)


def optipng(infile):
@configurable_filter(executable='OPTIPNG')
def optipng(infile, executable='optiong'):
"""Run optipng on a file."""
return runinplace("optipng -preserve -o2 -quiet %1", infile)
return runinplace("{} -preserve -o2 -quiet %1".format(executable), infile)


def jpegoptim(infile):
@configurable_filter(executable='JPEG_OPTIM')
def jpegoptim(infile, executable='jpegoptim'):
"""Run jpegoptim on a file."""
return runinplace("jpegoptim -p --strip-all -q %1", infile)
return runinplace("{} -p --strip-all -q %1".format(executable), infile)


@configurable_filter(executable='HTML_TIDY')
def html_tidy_withconfig(infile, executable='tidy5'):
"""Run HTML Tidy with tidy5.conf as config file."""
return _html_tidy_runner(infile, "-quiet --show-info no --show-warnings no -utf8 -indent -config tidy5.conf -modify %1", executable=executable)


@configurable_filter(executable='HTML_TIDY')
def html_tidy_nowrap(infile, executable='tidy5'):
"""Run HTML Tidy without line wrapping."""
return _html_tidy_runner(infile, "-quiet --show-info no --show-warnings no -utf8 -indent --indent-attributes no --sort-attributes alpha --wrap 0 --wrap-sections no --drop-empty-elements no --tidy-mark no -modify %1", executable=executable)


@configurable_filter(executable='HTML_TIDY')
def html_tidy_wrap(infile, executable='tidy5'):
"""Run HTML Tidy with line wrapping."""
return _html_tidy_runner(infile, "-quiet --show-info no --show-warnings no -utf8 -indent --indent-attributes no --sort-attributes alpha --wrap 80 --wrap-sections no --drop-empty-elements no --tidy-mark no -modify %1", executable=executable)


@configurable_filter(executable='HTML_TIDY')
def html_tidy_wrap_attr(infile, executable='tidy5'):
"""Run HTML tidy with line wrapping and attribute indentation."""
return _html_tidy_runner(infile, "-quiet --show-info no --show-warnings no -utf8 -indent --indent-attributes yes --sort-attributes alpha --wrap 80 --wrap-sections no --drop-empty-elements no --tidy-mark no -modify %1", executable=executable)


@configurable_filter(executable='HTML_TIDY')
def html_tidy_mini(infile, executable='tidy5'):
"""Run HTML tidy with minimal settings."""
return _html_tidy_runner(infile, "-quiet --show-info no --show-warnings no -utf8 --indent-attributes no --sort-attributes alpha --wrap 0 --wrap-sections no --tidy-mark no --drop-empty-elements no -modify %1", executable=executable)
@@ -32,6 +32,7 @@
from copy import copy
from pkg_resources import resource_filename
import datetime
import functools
import locale
import os
import json
@@ -925,6 +926,17 @@ def __init__(self, **config):
utils.LOGGER.warn('The STORY_INDEX option is deprecated, use PAGE_INDEX instead.')
self.config['PAGE_INDEX'] = config['STORY_INDEX']

# Configure filters
for filter, actions in self.config['FILTERS'].items():
for i, f in enumerate(actions):
if hasattr(f, 'configuration_variables'):
args = {}
for arg, config in f.configuration_variables.items():
if config in site.config:
args[arg] = site.config[config]
if args:
actions[i] = functools.partial(f, **args)

# We use one global tzinfo object all over Nikola.
try:
self.tzinfo = dateutil.tz.gettz(self.config['TIMEZONE'])

0 comments on commit 48ab72f

Please sign in to comment.
You can’t perform that action at this time.