@@ -0,0 +1,70 @@ | |||
# -*- coding: utf-8 -*- | |||
""" | |||
OPTIPNG plugin | |||
""" | |||
from hyde.plugin import CLTransformer | |||
from hyde.fs import File | |||
class OptiPNGPlugin(CLTransformer): | |||
""" | |||
The plugin class for OptiPNG | |||
""" | |||
def __init__(self, site): | |||
super(OptiPNGPlugin, self).__init__(site) | |||
@property | |||
def plugin_name(self): | |||
""" | |||
The name of the plugin. | |||
""" | |||
return "optipng" | |||
def option_prefix(self, option): | |||
return "-" | |||
def binary_resource_complete(self, resource): | |||
""" | |||
If the site is in development mode, just return. | |||
Otherwise, run optipng to compress the png file. | |||
""" | |||
try: | |||
mode = self.site.config.mode | |||
except AttributeError: | |||
mode = "production" | |||
if not resource.source_file.kind == 'png': | |||
return | |||
if mode.startswith('dev'): | |||
self.logger.debug("Skipping optipng in development mode.") | |||
return | |||
supported = [ | |||
"o", | |||
"fix", | |||
"force", | |||
"preserve", | |||
"quiet", | |||
"log", | |||
"f", | |||
"i", | |||
"zc", | |||
"zm", | |||
"zs", | |||
"zw", | |||
"full", | |||
"nb", | |||
"nc", | |||
"np", | |||
"nz" | |||
] | |||
target = File(self.site.config.deploy_root_path.child( | |||
resource.relative_deploy_path)) | |||
optipng = self.app | |||
args = [str(optipng)] | |||
args.extend(self.process_args(supported)) | |||
args.extend([str(target)]) | |||
self.call_app(args) |
@@ -13,6 +13,7 @@ import os | |||
import shutil | |||
from distutils import dir_util | |||
import functools | |||
import fnmatch | |||
from hyde.util import getLoggerWithNullHandler | |||
@@ -94,12 +95,12 @@ class FS(object): | |||
Generates the parents until stop or the absolute | |||
root directory is reached. | |||
""" | |||
f = self | |||
while f.parent != stop: | |||
if f.parent == f: | |||
folder = self | |||
while folder.parent != stop: | |||
if folder.parent == folder: | |||
return | |||
yield f.parent | |||
f = f.parent | |||
yield folder.parent | |||
folder = folder.parent | |||
def is_descendant_of(self, ancestor): | |||
""" | |||
@@ -185,9 +186,22 @@ class File(FS): | |||
""" | |||
return self.extension.lstrip(".") | |||
@property | |||
def size(self): | |||
""" | |||
Size of this file. | |||
""" | |||
if not self.exists: | |||
return -1 | |||
return os.path.getsize(self.path) | |||
@property | |||
def mimetype(self): | |||
(mime, encoding) = mimetypes.guess_type(self.path) | |||
""" | |||
Gets the mimetype of this file. | |||
""" | |||
(mime, _) = mimetypes.guess_type(self.path) | |||
return mime | |||
@property | |||
@@ -246,9 +260,9 @@ class File(FS): | |||
import tempfile | |||
(handle, path) = tempfile.mkstemp(text=True) | |||
os.close(handle) | |||
f = File(path) | |||
f.write(text) | |||
return f | |||
afile = File(path) | |||
afile.write(text) | |||
return afile | |||
def read_all(self, encoding='utf-8'): | |||
""" | |||
@@ -296,26 +310,26 @@ class FSVisitor(object): | |||
self.folder = folder | |||
self.pattern = pattern | |||
def folder_visitor(self, f): | |||
def folder_visitor(self, function): | |||
""" | |||
Decorator for `visit_folder` protocol | |||
""" | |||
self.visit_folder = f | |||
return f | |||
self.visit_folder = function | |||
return function | |||
def file_visitor(self, f): | |||
def file_visitor(self, function): | |||
""" | |||
Decorator for `visit_file` protocol | |||
""" | |||
self.visit_file = f | |||
return f | |||
self.visit_file = function | |||
return function | |||
def finalizer(self, f): | |||
def finalizer(self, function): | |||
""" | |||
Decorator for `visit_complete` protocol | |||
""" | |||
self.visit_complete = f | |||
return f | |||
self.visit_complete = function | |||
return function | |||
def __enter__(self): | |||
return self | |||
@@ -341,7 +355,7 @@ class FolderWalker(FSVisitor): | |||
if not walk_files and not walk_folders: | |||
return | |||
for root, dirs, a_files in os.walk(self.folder.path, followlinks=True): | |||
for root, _, a_files in os.walk(self.folder.path, followlinks=True): | |||
folder = Folder(root) | |||
if walk_folders: | |||
yield folder | |||
@@ -262,6 +262,9 @@ class CLTransformer(Plugin): | |||
return app | |||
def option_prefix(self, option): | |||
return "--" | |||
def process_args(self, supported): | |||
""" | |||
Given a list of supported arguments, consutructs an argument | |||
@@ -282,7 +285,8 @@ class CLTransformer(Plugin): | |||
descriptive = short = arg | |||
if descriptive in args or short in args: | |||
result.append("--%s" % descriptive) | |||
result.append("%s%s" % (self.option_prefix(descriptive), | |||
descriptive)) | |||
val = args[descriptive if descriptive in args else short] | |||
if val: | |||
result.append(val) | |||
@@ -0,0 +1 @@ | |||
../../../../resources/hyde-lt-b.png |
@@ -32,7 +32,7 @@ class TestLess(object): | |||
paths = ['/usr/local/share/npm/bin/lessc', '~/local/bin/lessc'] | |||
less = [path for path in paths if File(path).exists] | |||
if not less: | |||
assert False, "Cannot find the uglify executable" | |||
assert False, "Cannot find the lessc executable" | |||
less = less[0] | |||
s.config.less = Expando(dict(app=less)) | |||
source = TEST_SITE.child('content/media/css/site.less') | |||
@@ -0,0 +1,45 @@ | |||
# -*- coding: utf-8 -*- | |||
""" | |||
Use nose | |||
`$ pip install nose` | |||
`$ nosetests` | |||
""" | |||
from hyde.fs import File, Folder | |||
from hyde.model import Expando | |||
from hyde.generator import Generator | |||
from hyde.site import Site | |||
OPTIPNG_SOURCE = File(__file__).parent.child_folder('optipng') | |||
TEST_SITE = File(__file__).parent.parent.child_folder('_test') | |||
class TestOptipng(object): | |||
def setUp(self): | |||
TEST_SITE.make() | |||
TEST_SITE.parent.child_folder( | |||
'sites/test_jinja').copy_contents_to(TEST_SITE) | |||
IMAGES = TEST_SITE.child_folder('content/media/images') | |||
IMAGES.make() | |||
OPTIPNG_SOURCE.copy_contents_to(IMAGES) | |||
def tearDown(self): | |||
TEST_SITE.delete() | |||
def test_can_execute_optipng(self): | |||
s = Site(TEST_SITE) | |||
s.config.mode = "production" | |||
s.config.plugins = ['hyde.ext.plugins.optipng.OptiPNGPlugin'] | |||
paths = ['/usr/local/bin/optipng'] | |||
optipng = [path for path in paths if File(path).exists] | |||
if not optipng: | |||
assert False, "Cannot find the optipng executable" | |||
optipng = optipng[0] | |||
s.config.optipng = Expando(dict(app=optipng, args=dict(quiet=""))) | |||
source =File(TEST_SITE.child('content/media/images/hyde-lt-b.png')) | |||
target = File(Folder(s.config.deploy_root_path).child('media/images/hyde-lt-b.png')) | |||
gen = Generator(s) | |||
gen.generate_resource_at_path(source) | |||
assert target.exists | |||
assert target.size < source.size |
@@ -204,6 +204,9 @@ def test_is_image(): | |||
assert not HELPERS.is_image | |||
assert LOGO.is_image | |||
def test_file_size(): | |||
assert LOGO.size == 1942 | |||
@nottest | |||
def setup_data(): | |||
DATA_ROOT.make() | |||