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