* Better exception handling. (Issue #204) - Hyde now propagates exceptions - Hyde does not raise exceptions by default. `-x` flag is required for raising exceptions on error. - Traceback is available in the `verbose` mode * Upgraded to commando 0.3.4 * Upgraded to Jinja 2.7main
@@ -1,3 +1,15 @@ | |||
Version 0.8.7a6 | |||
============================================================ | |||
* Better exception handling. (Issue #204) | |||
- Hyde now propagates exceptions | |||
- Hyde does not raise exceptions by default. `-x` flag is required | |||
for raising exceptions on error. | |||
- Traceback is available in the `verbose` mode | |||
* Upgraded to commando 0.3.4 | |||
* Upgraded to Jinja 2.7 | |||
Version 0.8.7a5 | |||
============================================================ | |||
@@ -7,7 +19,7 @@ Version 0.8.7a4 | |||
============================================================ | |||
* Include project artifacts in sdist. (Issue #211) | |||
- Add LICENSE, AUTHORS and CHANGELOG to MANIFEST.in | |||
- Add LICENSE, AUTHORS and CHANGELOG to MANIFEST.in | |||
* Add "Hyde contributors to LICENSE copyright" | |||
@@ -1,4 +1,4 @@ | |||
Version 0.8.7a5 | |||
Version 0.8.7a6 | |||
A brand new **hyde** | |||
==================== | |||
@@ -24,7 +24,7 @@ HYDE_LAYOUTS = "HYDE_LAYOUTS" | |||
class Engine(Application): | |||
def __init__(self, raise_exceptions=True): | |||
def __init__(self, raise_exceptions=False): | |||
logger = getLoggerWithConsoleHandler('hyde') | |||
super(Engine, self).__init__( | |||
raise_exceptions=raise_exceptions, | |||
@@ -34,6 +34,8 @@ class Engine(Application): | |||
@command(description='hyde - a python static website generator', | |||
epilog='Use %(prog)s {command} -h to get help on individual commands') | |||
@true('-v', '--verbose', help="Show detailed information in console") | |||
@true('-x', '--raise-exceptions', default=False, | |||
help="Don't handle exceptions.") | |||
@version('--version', version='%(prog)s ' + __version__) | |||
@store('-s', '--sitepath', default='.', help="Location of the hyde site") | |||
def main(self, args): | |||
@@ -43,6 +45,7 @@ class Engine(Application): | |||
like version and metadata | |||
""" | |||
sitepath = Folder(args.sitepath).fully_expanded_path | |||
self.raise_exceptions = args.raise_exceptions | |||
return Folder(sitepath) | |||
@subcommand('create', help='Create a new hyde site.') | |||
@@ -94,7 +97,6 @@ class Engine(Application): | |||
if args.regen: | |||
self.logger.info("Regenerating the site...") | |||
incremental = False | |||
gen.generate_all(incremental=incremental) | |||
self.logger.info("Generation complete.") | |||
@@ -147,7 +149,6 @@ class Engine(Application): | |||
publisher.publish() | |||
def make_site(self, sitepath, config, deploy=None): | |||
""" | |||
Creates a site object from the given sitepath and the config file. | |||
@@ -155,4 +156,4 @@ class Engine(Application): | |||
config = Config(sitepath, config_file=config) | |||
if deploy: | |||
config.deploy_root = deploy | |||
return Site(sitepath, config) | |||
return Site(sitepath, config) |
@@ -2,4 +2,10 @@ class HydeException(Exception): | |||
""" | |||
Base class for exceptions from hyde | |||
""" | |||
pass | |||
@staticmethod | |||
def reraise(message, exc_info): | |||
_, _, tb = exc_info | |||
raise HydeException(message), None, tb | |||
@@ -10,6 +10,7 @@ from hyde.exceptions import HydeException | |||
import os | |||
import re | |||
import subprocess | |||
import sys | |||
from fswrap import File | |||
@@ -71,8 +72,7 @@ class LessCSSPlugin(CLTransformer): | |||
afile = File(afile.path + '.less') | |||
ref = self.site.content.resource_from_path(afile.path) | |||
if not ref: | |||
raise self.template.exception_class( | |||
"Cannot import from path [%s]" % afile.path) | |||
raise HydeException("Cannot import from path [%s]" % afile.path) | |||
ref.is_processable = False | |||
return self.template.get_include_statement(ref.relative_path) | |||
text = self.import_finder.sub(import_to_include, text) | |||
@@ -114,9 +114,11 @@ class LessCSSPlugin(CLTransformer): | |||
try: | |||
self.call_app(args) | |||
except subprocess.CalledProcessError: | |||
raise self.template.exception_class( | |||
HydeException.reraise( | |||
"Cannot process %s. Error occurred when " | |||
"processing [%s]" % (self.app.name, resource.source_file)) | |||
"processing [%s]" % (self.app.name, resource.source_file), | |||
sys.exc_info()) | |||
return target.read_all() | |||
@@ -155,7 +157,7 @@ class StylusPlugin(CLTransformer): | |||
def import_to_include(match): | |||
""" | |||
Converts a css import statement to include statemnt. | |||
Converts a css import statement to include statement. | |||
""" | |||
if not match.lastindex: | |||
return '' | |||
@@ -172,7 +174,7 @@ class StylusPlugin(CLTransformer): | |||
except AttributeError: | |||
include = False | |||
if not include: | |||
raise self.template.exception_class( | |||
raise HydeException( | |||
"Cannot import from path [%s]" % afile.path) | |||
else: | |||
ref.is_processable = False | |||
@@ -225,9 +227,10 @@ class StylusPlugin(CLTransformer): | |||
try: | |||
self.call_app(args) | |||
except subprocess.CalledProcessError: | |||
raise self.template.exception_class( | |||
HydeException.reraise( | |||
"Cannot process %s. Error occurred when " | |||
"processing [%s]" % (stylus.name, resource.source_file)) | |||
"processing [%s]" % (stylus.name, resource.source_file), | |||
sys.exc_info()) | |||
return target.read_all() | |||
@@ -291,8 +294,7 @@ class CleverCSSPlugin(Plugin): | |||
afile = File(afile.path + '.ccss') | |||
ref = self.site.content.resource_from_path(afile.path) | |||
if not ref: | |||
raise self.template.exception_class( | |||
"Cannot import from path [%s]" % afile.path) | |||
raise HydeException("Cannot import from path [%s]" % afile.path) | |||
ref.is_processable = False | |||
return self.template.get_include_statement(ref.relative_path) | |||
text = import_finder.sub(import_to_include, text) | |||
@@ -3,7 +3,9 @@ | |||
JavaScript plugins | |||
""" | |||
import subprocess | |||
import sys | |||
from hyde.exceptions import HydeException | |||
from hyde.plugin import CLTransformer | |||
from fswrap import File | |||
@@ -127,9 +129,10 @@ class RequireJSPlugin(CLTransformer): | |||
try: | |||
self.call_app(args) | |||
except subprocess.CalledProcessError: | |||
raise self.template.exception_class( | |||
HydeException.reraise( | |||
"Cannot process %s. Error occurred when " | |||
"processing [%s]" % (self.app.name, resource.source_file)) | |||
"processing [%s]" % (self.app.name, resource.source_file), | |||
sys.exc_info()) | |||
return target.read_all() | |||
@@ -8,7 +8,9 @@ from functools import partial | |||
from itertools import ifilter | |||
from operator import attrgetter | |||
import re | |||
import sys | |||
from hyde.exceptions import HydeException | |||
from hyde.model import Expando | |||
from hyde.plugin import Plugin | |||
from hyde.site import Node, Resource | |||
@@ -248,8 +250,9 @@ def get_tagger_sort_method(site): | |||
try: | |||
walker = getattr(content, walker) | |||
except AttributeError: | |||
raise self.template.exception_class( | |||
"Cannot find the sorter: %s" % sorter) | |||
HydeException.reraise( | |||
"Cannot find the sorter: %s" % sorter, | |||
sys.exc_info()) | |||
return walker | |||
def walk_resources_tagged_with(node, tag): | |||
@@ -370,8 +373,7 @@ class TaggerPlugin(Plugin): | |||
Generates archives for each tag based on the given configuration. | |||
""" | |||
if not 'template' in config: | |||
raise self.template.exception_class( | |||
"No Template specified in tagger configuration.") | |||
raise HydeException("No Template specified in tagger configuration.") | |||
content = self.site.content.source_folder | |||
source = Folder(config.get('source', '')) | |||
target = content.child_folder(config.get('target', 'tags')) | |||
@@ -4,11 +4,13 @@ Jinja template utilties | |||
""" | |||
from datetime import datetime, date | |||
import itertools | |||
import os | |||
import re | |||
import itertools | |||
import sys | |||
from urllib import quote, unquote | |||
from hyde.exceptions import HydeException | |||
from hyde.model import Expando | |||
from hyde.template import HtmlWrap, Template | |||
from operator import attrgetter | |||
@@ -599,10 +601,20 @@ class HydeLoader(FileSystemLoader): | |||
# | |||
template = template.replace(os.sep, '/') | |||
logger.debug("Loading template [%s] and preprocessing" % template) | |||
(contents, | |||
filename, | |||
date) = super(HydeLoader, self).get_source( | |||
try: | |||
(contents, | |||
filename, | |||
date) = super(HydeLoader, self).get_source( | |||
environment, template) | |||
except UnicodeDecodeError: | |||
HydeException.reraise( | |||
"Unicode error when processing %s" % template, sys.exc_info()) | |||
except TemplateError, exc: | |||
HydeException.reraise('Error when processing %s: %s' % ( | |||
template, | |||
unicode(exc) | |||
), sys.exc_info()) | |||
if self.preprocessor: | |||
resource = self.site.content.resource_from_relative_path(template) | |||
if resource: | |||
@@ -736,9 +748,11 @@ class Jinja2Template(Template): | |||
from jinja2.meta import find_referenced_templates | |||
try: | |||
ast = self.env.parse(text) | |||
except: | |||
logger.error("Error parsing[%s]" % path) | |||
raise | |||
except Exception, e: | |||
HydeException.reraise( | |||
"Error processing %s: \n%s" % (path, unicode(e)), | |||
sys.exc_info()) | |||
tpls = find_referenced_templates(ast) | |||
deps = list(self.env.globals['deps'].get('path', [])) | |||
for dep in tpls: | |||
@@ -818,9 +832,6 @@ class Jinja2Template(Template): | |||
template = self.env.get_template(resource.relative_path) | |||
out = template.render(context) | |||
except: | |||
out = "" | |||
logger.debug(self.env.loader.get_source( | |||
self.env, resource.relative_path)) | |||
raise | |||
return out | |||
@@ -3,8 +3,9 @@ | |||
The generator class and related utility functions. | |||
""" | |||
from hyde.exceptions import HydeException | |||
from commando.util import getLoggerWithNullHandler | |||
from fswrap import File, Folder | |||
from hyde.exceptions import HydeException | |||
from hyde.model import Context, Dependents | |||
from hyde.plugin import Plugin | |||
from hyde.template import Template | |||
@@ -12,9 +13,9 @@ from hyde.site import Resource | |||
from contextlib import contextmanager | |||
from datetime import datetime | |||
from shutil import copymode | |||
from commando.util import getLoggerWithNullHandler | |||
import sys | |||
logger = getLoggerWithNullHandler('hyde.engine') | |||
@@ -334,10 +335,12 @@ class Generator(object): | |||
try: | |||
text = self.template.render_resource(resource, | |||
context) | |||
except Exception: | |||
logger.error("Error occurred when" | |||
" processing template: [%s]" % resource) | |||
raise | |||
except Exception, e: | |||
HydeException.reraise("Error occurred when" | |||
" processing template: [%s]: %s" % | |||
(resource, repr(e)), | |||
sys.exc_info() | |||
) | |||
else: | |||
text = resource.source_file.read_all() | |||
text = self.events.begin_text_resource(resource, text) or text | |||
@@ -12,6 +12,7 @@ import fnmatch | |||
import os | |||
import re | |||
import subprocess | |||
import sys | |||
import traceback | |||
from commando.util import getLoggerWithNullHandler, load_python_object | |||
@@ -56,18 +57,19 @@ class PluginProxy(object): | |||
def __getattr__(self, method_name): | |||
if hasattr(Plugin, method_name): | |||
def __call_plugins__(*args): | |||
# logger.debug("Calling plugin method [%s]", method_name) | |||
res = None | |||
if self.site.plugins: | |||
for plugin in self.site.plugins: | |||
if hasattr(plugin, method_name): | |||
# logger.debug( | |||
# "\tCalling plugin [%s]", | |||
# plugin.__class__.__name__) | |||
checker = getattr(plugin, 'should_call__' + method_name) | |||
if checker(*args): | |||
function = getattr(plugin, method_name) | |||
res = function(*args) | |||
try: | |||
res = function(*args) | |||
except: | |||
HydeException.reraise( | |||
'Error occured when calling %s' % | |||
plugin.plugin_name, sys.exc_info()) | |||
targs = list(args) | |||
if len(targs): | |||
last = targs.pop() | |||
@@ -356,14 +358,11 @@ class CLTransformer(Plugin): | |||
app_path = discover_executable(app_path, self.site.sitepath) | |||
if app_path is None: | |||
raise self.template.exception_class( | |||
self.executable_not_found_message) | |||
raise HydeException(self.executable_not_found_message) | |||
app = File(app_path) | |||
if not app.exists: | |||
raise self.template.exception_class( | |||
self.executable_not_found_message) | |||
raise HydeException(self.executable_not_found_message) | |||
return app | |||
@@ -418,7 +417,6 @@ class CLTransformer(Plugin): | |||
(args[0], unicode(args[1:]))) | |||
return subprocess.check_output(args) | |||
except subprocess.CalledProcessError, error: | |||
self.logger.error(traceback.format_exc()) | |||
self.logger.error(error.output) | |||
raise | |||
@@ -162,7 +162,7 @@ class HydeWebServer(HTTPServer): | |||
except Exception, exception: | |||
logger.error('Error occured when regenerating the site [%s]' | |||
% exception.message) | |||
logger.error(traceback.format_exc()) | |||
logger.debug(traceback.format_exc()) | |||
def generate_node(self, node): | |||
""" | |||
@@ -180,7 +180,7 @@ class HydeWebServer(HTTPServer): | |||
logger.error( | |||
'Error [%s] occured when generating the node [%s]' | |||
% (repr(exception), node)) | |||
logger.error(traceback.format_exc()) | |||
logger.debug(traceback.format_exc()) | |||
def generate_resource(self, resource): | |||
""" | |||
@@ -199,4 +199,4 @@ class HydeWebServer(HTTPServer): | |||
logger.error( | |||
'Error [%s] occured when serving the resource [%s]' | |||
% (repr(exception), resource)) | |||
logger.error(traceback.format_exc()) | |||
logger.debug(traceback.format_exc()) |
@@ -2,4 +2,4 @@ | |||
""" | |||
Handles hyde version. | |||
""" | |||
__version__ = '0.8.7a5' | |||
__version__ = '0.8.7a6' |
@@ -1,8 +1,8 @@ | |||
fswrap==0.1.1 | |||
commando==0.3.2a | |||
commando==0.3.4 | |||
PyYAML==3.10 | |||
Markdown==2.3.1 | |||
MarkupSafe==0.15 | |||
MarkupSafe==0.18 | |||
Pygments==1.6 | |||
typogrify==2.0.0 | |||
Jinja2==2.6 | |||
Jinja2==2.7 |
@@ -117,13 +117,13 @@ setup(name=PROJECT, | |||
requires=['python (>= 2.7)'], | |||
install_requires=( | |||
'fswrap==0.1.1', | |||
'commando==0.3.2a', | |||
'commando==0.3.4', | |||
'PyYAML==3.10', | |||
'Markdown==2.3.1', | |||
'MarkupSafe==0.15', | |||
'MarkupSafe==0.18', | |||
'Pygments==1.6', | |||
'typogrify==2.0.0', | |||
'Jinja2==2.6' | |||
'Jinja2==2.7' | |||
), | |||
tests_require=( | |||
'nose', 'mock' | |||