* 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' | ||||