diff --git a/hyde/ext/plugins/less.py b/hyde/ext/plugins/less.py index 94fd9c2..ba874ef 100644 --- a/hyde/ext/plugins/less.py +++ b/hyde/ext/plugins/less.py @@ -7,7 +7,12 @@ from hyde.fs import File, Folder import re import subprocess +import traceback +import logging +from logging import NullHandler +logger = logging.getLogger('hyde.engine') +logger.addHandler(NullHandler()) class LessCSSPlugin(Plugin): """ @@ -71,8 +76,10 @@ class LessCSSPlugin(Plugin): source = File.make_temp(text) target = File.make_temp('') try: - subprocess.check_call([str(less), str(source), str(target)]) - except subprocess.CalledProcessError: + subprocess.check_output([str(less), str(source), str(target)]) + except subprocess.CalledProcessError, error: + logger.error(traceback.format_exc()) + logger.error(error.output) raise self.template.exception_class( "Cannot process less css. Error occurred when " "processing [%s]" % resource.source_file) diff --git a/hyde/ext/plugins/meta.py b/hyde/ext/plugins/meta.py index 760df43..e52ea44 100644 --- a/hyde/ext/plugins/meta.py +++ b/hyde/ext/plugins/meta.py @@ -54,35 +54,33 @@ class MetaPlugin(Plugin): super(MetaPlugin, self).__init__(site) def begin_site(self): - metadata = self.site.config.meta if hasattr(self.site.config, 'meta') else {} - self.site.meta = Metadata(metadata) - - def begin_node(self, node): """ - Look for nodemeta.yaml. Load and assign it to the node. + Initialize site meta data. + + Go through all the nodes and resources to initialize + meta data at each level. """ - nodemeta = node.get_resource('nodemeta.yaml') - parent_meta = node.parent.meta if node.parent else self.site.meta - if nodemeta: - nodemeta.is_processable = False - metadata = nodemeta.source_file.read_all() - if hasattr(node, 'meta') and node.meta: - node.meta.update(metadata) - else: - node.meta = Metadata(metadata, parent=parent_meta) - else: - node.meta = Metadata({}, parent=parent_meta) + config = self.site.config + metadata = config.meta if hasattr(config, 'meta') else {} + self.site.meta = Metadata(metadata) + for node in self.site.content.walk(): + self.__read_node__(node) + for resource in node.resources: + if not hasattr(resource, 'meta'): + resource.meta = Metadata({}, node.meta) + if resource.source_file.is_text: + self.__read_resource__(resource, resource.source_file.read_all()) - def begin_text_resource(self, resource, text): + def __read_resource__(self, resource, text): """ - Load meta data by looking for the marker. + Reads the resource metadata and assigns it to + the resource. Load meta data by looking for the marker. Once loaded, remove the meta area from the text. """ - logger.info("Trying to load metadata from resource [%s]" % resource) yaml_finder = re.compile( - r"^\s*(?:---|===)\s*\n((?:.|\n)+?)\n\s*(?:---|===)\s*\n", - re.MULTILINE) + r"^\s*(?:---|===)\s*\n((?:.|\n)+?)\n\s*(?:---|===)\s*\n", + re.MULTILINE) match = re.match(yaml_finder, text) if not match: logger.info("No metadata found in resource [%s]" % resource) @@ -98,3 +96,32 @@ class MetaPlugin(Plugin): logger.info("Successfully loaded metadata from resource [%s]" % resource) return text + + def __read_node__(self, node): + """ + Look for nodemeta.yaml. Load and assign it to the node. + """ + nodemeta = node.get_resource('nodemeta.yaml') + parent_meta = node.parent.meta if node.parent else self.site.meta + if nodemeta: + nodemeta.is_processable = False + metadata = nodemeta.source_file.read_all() + if hasattr(node, 'meta') and node.meta: + node.meta.update(metadata) + else: + node.meta = Metadata(metadata, parent=parent_meta) + else: + node.meta = Metadata({}, parent=parent_meta) + + def begin_node(self, node): + """ + Look for nodemeta.yaml. Load and assign it to the node. + """ + self.__read_node__(node) + + def begin_text_resource(self, resource, text): + """ + Update the meta data again, just in case it + has changed. Return text without meta data. + """ + return self.__read_resource__(resource, text) diff --git a/hyde/ext/templates/jinja.py b/hyde/ext/templates/jinja.py index 2c38b57..d5677ee 100644 --- a/hyde/ext/templates/jinja.py +++ b/hyde/ext/templates/jinja.py @@ -79,7 +79,11 @@ class Jinja2Template(Template): loader = FileSystemLoader(str(self.sitepath)) self.env = Environment(loader=loader, undefined=SilentUndefined, - extensions=[Markdown]) + trim_blocks=True, + extensions=[Markdown, + 'jinja2.ext.do', + 'jinja2.ext.loopcontrols', + 'jinja2.ext.with_']) self.env.globals['media_url'] = media_url self.env.globals['content_url'] = content_url self.env.extend(config=config) diff --git a/hyde/fs.py b/hyde/fs.py index d1bfdd6..1b0fd03 100644 --- a/hyde/fs.py +++ b/hyde/fs.py @@ -30,7 +30,10 @@ class FS(object): def __init__(self, path): super(FS, self).__init__() - self.path = os.path.expandvars(os.path.expanduser( + if path == os.sep: + self.path = path + else: + self.path = os.path.expandvars(os.path.expanduser( str(path).strip().rstrip(os.sep))) def __str__(self): diff --git a/hyde/site.py b/hyde/site.py index f6bfaf8..e7511f2 100644 --- a/hyde/site.py +++ b/hyde/site.py @@ -169,6 +169,35 @@ class Node(Processable): for resource in node.resources: yield resource + def walk_resources_sorted(self, attr='name', reverse=False, default=None): + """ + Walks the resources in this hierarchy sorted by + the given key. + """ + from operator import attrgetter + + def safe_attrgetter(*items): + f = attrgetter(*items) + + def wrapper(obj): + res = None + try: + res = f(obj) + except: + logger.error("Cannot get the requested items[%s]" + " from the object [%s]" % + (items, obj)) + res = default + return res + + return wrapper + + sorted_resources = sorted(self.walk_resources(), + key=safe_attrgetter(attr), + reverse=reverse) + for resource in sorted_resources: + yield resource + @property def relative_path(self): """ diff --git a/hyde/tests/test_site.py b/hyde/tests/test_site.py index 75e62c5..8a00119 100644 --- a/hyde/tests/test_site.py +++ b/hyde/tests/test_site.py @@ -103,6 +103,44 @@ def test_walk_resources(): expected.sort() assert pages == expected +def test_walk_resources_sorted(): + s = Site(TEST_SITE_ROOT) + s.load() + pages = [page.name for page in s.content.walk_resources_sorted(attr='name')] + expected = ["404.html", + "about.html", + "apple-touch-icon.png", + "merry-christmas.html", + "crossdomain.xml", + "favicon.ico", + "robots.txt", + "site.css" + ] + assert pages == sorted(expected) + pages = [page.name for page in + s.content.walk_resources_sorted(attr='name', reverse=True)] + assert pages == sorted(expected, reverse=True) + pages = [page.name for page in + s.content.walk_resources_sorted(attr='source_file.kind')] + assert pages == sorted(expected, key=lambda f: File(f).kind) + + from datetime import datetime, timedelta + + d = {} + t = datetime.now() + for name in expected: + d[name] = t + t = t - timedelta(days=1) + + for page in s.content.walk_resources(): + page.meta = Expando(dict(time=d[page.name])) + + pages = [page.name for page in + s.content.walk_resources_sorted(attr='meta.time', reverse=True)] + assert pages == expected + + + def test_contains_resource(): s = Site(TEST_SITE_ROOT) s.load()