diff options
author | Eevee (Alex Munroe) <eevee.git@veekun.com> | 2013-08-19 14:25:15 -0700 |
---|---|---|
committer | Eevee (Alex Munroe) <eevee.git@veekun.com> | 2013-08-19 14:29:12 -0700 |
commit | fd185b90961cd86b6d79a666bca186005690c831 (patch) | |
tree | f375866082beabed2bbb9178802ac6d3b7b88f06 | |
parent | 31ae37c62a71968fbf6a00fe911b005b22b4245e (diff) | |
download | pyscss-fd185b90961cd86b6d79a666bca186005690c831.tar.gz |
Basic error wrapping.
-rw-r--r-- | scss/__init__.py | 11 | ||||
-rw-r--r-- | scss/errors.py | 61 | ||||
-rw-r--r-- | scss/expression.py | 13 |
3 files changed, 81 insertions, 4 deletions
diff --git a/scss/__init__.py b/scss/__init__.py index 540fcfb..3cad57c 100644 --- a/scss/__init__.py +++ b/scss/__init__.py @@ -65,6 +65,7 @@ from scss.cssdefs import ( _spaces_re, _expand_rules_space_re, _collapse_properties_space_re, _strings_re, _prop_split_re, ) +from scss.errors import SassError from scss.expression import Calculator from scss.functions import ALL_BUILTINS_LIBRARY from scss.functions.compass.sprites import sprite_map @@ -514,6 +515,16 @@ class Scss(object): @print_timing(4) def manage_children(self, rule, p_children, scope): + try: + return self._manage_children_impl(rule, p_children, scope) + except SassError, e: + e.set_rule(rule) + raise + except Exception, e: + raise SassError(e, rule=rule) + + + def _manage_children_impl(self, rule, p_children, scope): # A rule that has already returned should not end up here assert rule.retval is None diff --git a/scss/errors.py b/scss/errors.py new file mode 100644 index 0000000..6e946e5 --- /dev/null +++ b/scss/errors.py @@ -0,0 +1,61 @@ +import sys +import traceback + +class SassError(Exception): + """Error class that wraps another exception and attempts to bolt on some + useful context. + """ + def __init__(self, exc, rule=None, expression=None): + self.exc = exc + self.rule = rule + self.expression = expression + _, _, self.original_traceback = sys.exc_info() + + def set_rule(self, rule): + """Set the current rule. + + If this error already has a rule, it's left alone; this is because + exceptions propagate outwards and so the first rule seen is the + "innermost". + """ + if not self.rule: + self.rule = rule + + def format_prefix(self): + # TODO pointer, yadda + # TODO newlines, yadda + if self.rule: + return "Error parsing block:\n" + " " + self.rule.unparsed_contents + else: + return "Unknown error" + + def __str__(self): + prefix = self.format_prefix() + + ret = [prefix, "\n\n"] + + if self.rule: + ret.extend(("From ", self.rule.file_and_line, "\n\n")) + + ret.append("Traceback:\n") + ret.extend(traceback.format_tb(self.original_traceback)) + ret.extend((type(self.exc).__name__, ": ", str(self.exc), "\n")) + return ''.join(ret) + +class SassParseError(SassError): + """Error raised when parsing a Sass expression fails.""" + + def format_prefix(self): + # TODO pointer + # TODO deal with newlines, etc + return """Error parsing expression:\n %s""" % (self.expression,) + + +class SassEvaluationError(SassError): + """Error raised when evaluating a parsed expression fails.""" + + def format_prefix(self): + # TODO pointer + # TODO ast needs to know where each node came from for that to work :( + # TODO deal with newlines, etc + return """Error evaluating expression:\n %s""" % (self.expression,) diff --git a/scss/expression.py b/scss/expression.py index 869550a..5c6dd15 100644 --- a/scss/expression.py +++ b/scss/expression.py @@ -9,6 +9,7 @@ import six import scss.config as config from scss.cssdefs import COLOR_NAMES, is_builtin_css_function, _expr_glob_re, _interpolate_re, _variable_re +from scss.errors import SassEvaluationError, SassParseError from scss.rule import Namespace from scss.types import Boolean, Color, List, Map, Null, Number, ParserValue, String, Undefined from scss.util import dequote, normalize_var @@ -137,14 +138,18 @@ class Calculator(object): try: parser = SassExpression(SassExpressionScanner(expr)) ast = parser.goal() - except SyntaxError: + except SyntaxError, e: if config.DEBUG: - raise - return None + raise SassParseError(e, expression=expr) + else: + return None else: ast_cache[expr] = ast - return ast.evaluate(self, divide=divide) + try: + return ast.evaluate(self, divide=divide) + except Exception, e: + raise SassEvaluationError(e, expression=expr) def parse_expression(self, expr, target='goal'): ast_cache = self.get_ast_cache(target) |