summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEevee (Alex Munroe) <eevee.git@veekun.com>2014-08-28 13:50:49 -0700
committerEevee (Alex Munroe) <eevee.git@veekun.com>2014-08-28 13:59:27 -0700
commit5ae0fffc030353e697acbd26e6145364afc9ffc8 (patch)
tree9b07d13e6413c0a7211871f3d806e2c3224d5ac2
parent8b32fe7369ea287681292c0036b5458843b299c8 (diff)
downloadpyscss-5ae0fffc030353e697acbd26e6145364afc9ffc8.tar.gz
Shuffle exception hierarchy a bit.
-rw-r--r--scss/compiler.py14
-rw-r--r--scss/errors.py105
2 files changed, 84 insertions, 35 deletions
diff --git a/scss/compiler.py b/scss/compiler.py
index e07b515..ac9dea7 100644
--- a/scss/compiler.py
+++ b/scss/compiler.py
@@ -20,6 +20,8 @@ from scss.cssdefs import _spaces_re
from scss.cssdefs import _escape_chars_re
from scss.cssdefs import _prop_split_re
from scss.errors import SassError
+from scss.errors import SassBaseError
+from scss.errors import SassImportError
from scss.expression import Calculator
from scss.extension import Extension
from scss.extension.core import CoreExtension
@@ -322,9 +324,7 @@ class Compilation(object):
def manage_children(self, rule, scope):
try:
self._manage_children_impl(rule, scope)
- except SassReturn:
- raise
- except SassError as e:
+ except SassBaseError as e:
e.add_rule(rule)
raise
except Exception as e:
@@ -815,7 +815,7 @@ class Compilation(object):
generated_code = self._at_magic_import(
calculator, rule, scope, block)
if generated_code is None:
- raise
+ raise SassImportError(sass_path, self.compiler, rule=rule)
source = SourceFile.from_string(generated_code)
else:
@@ -1587,15 +1587,13 @@ class Compilation(object):
return result
-# TODO: this should inherit from SassError, but can't, because that assumes
-# it's wrapping another error. fix this with the exception hierarchy
-class SassReturn(Exception):
+class SassReturn(SassBaseError):
"""Special control-flow exception used to hop up the stack from a Sass
function's ``@return``.
"""
def __init__(self, retval):
+ super(SassReturn, self).__init__()
self.retval = retval
- Exception.__init__(self)
def __str__(self):
return "Returning {0!r}".format(self.retval)
diff --git a/scss/errors.py b/scss/errors.py
index b3b4d53..b1e7f12 100644
--- a/scss/errors.py
+++ b/scss/errors.py
@@ -30,6 +30,7 @@ body:before {{
}}
"""
+
def add_error_marker(text, position, start_line=1):
"""Add a caret marking a given position in a string of input.
@@ -52,21 +53,18 @@ def add_error_marker(text, position, start_line=1):
return "\n".join(lines), caret_line
-class SassError(Exception):
- """Error class that wraps another exception and attempts to bolt on some
- useful context.
+class SassBaseError(Exception):
+ """Base class for all errors caused by Sass code.
+
+ Shouldn't be raising this directly; use or create a subclass instead.
"""
- def __init__(self, exc, rule=None, expression=None, expression_pos=None):
- self.exc = exc
- self.rule_stack = []
- if rule:
- self.rule_stack.append(rule)
+ def __init__(self, rule=None):
+ super(SassBaseError, self).__init__()
- self.expression = expression
- self.expression_pos = expression_pos
-
- _, _, self.original_traceback = sys.exc_info()
+ self.rule_stack = []
+ if rule is not None:
+ self.add_rule(rule)
def add_rule(self, rule):
"""Add a new rule to the "stack" of rules -- this is used to track,
@@ -74,36 +72,88 @@ class SassError(Exception):
"""
self.rule_stack.append(rule)
- def format_prefix(self):
- """Return the general name of the error and the contents of the rule or
- property that caused the failure. This is the initial part of the
- error message and should be error-specific.
- """
- # TODO this contains NULs and line numbers; could be much prettier
- if self.rule_stack:
- return (
- "Error parsing block:\n" +
- " " + self.rule_stack[0].unparsed_contents + "\n"
- )
- else:
- return "Unknown error\n"
+ def format_file_and_line(self, rule):
+ return "line {rule.lineno} of {rule.source_file.path}".format(
+ rule=rule,
+ )
def format_sass_stack(self):
"""Return a "traceback" of Sass imports."""
if not self.rule_stack:
return ""
- ret = ["From ", self.rule_stack[0].file_and_line, "\n"]
+ ret = ["on ", self.format_file_and_line(self.rule_stack[0]), "\n"]
last_file = self.rule_stack[0].source_file
# TODO this could go away if rules knew their import chains...
+ # TODO mixins and the like here too?
+ # TODO the line number is wrong here, since this doesn't include the
+ # *block* being parsed!
for rule in self.rule_stack[1:]:
if rule.source_file is not last_file:
- ret.extend(("...imported from ", rule.file_and_line, "\n"))
+ ret.extend((
+ "imported from ", self.format_file_and_line(rule), "\n"))
last_file = rule.source_file
return "".join(ret)
+ def format_message(self):
+ return ""
+
+ def __str__(self):
+ return "{message}\n{sass_stack}".format(
+ message=self.format_message(),
+ sass_stack=self.format_sass_stack(),
+ )
+
+
+class SassImportError(SassBaseError):
+ """Error raised when unable to resolve an @import."""
+
+ def __init__(self, bad_name, compiler, **kwargs):
+ super(SassImportError, self).__init__(**kwargs)
+
+ self.bad_name = bad_name
+ self.compiler = compiler
+
+ def format_message(self):
+ return (
+ "Couldn't find anything to import: {0}\n"
+ "Search path:\n {1}"
+ .format(
+ self.bad_name,
+ "\n ".join(self.compiler.search_path),
+ )
+ )
+
+
+class SassError(SassBaseError):
+ """Error class that wraps another exception and attempts to bolt on some
+ useful context.
+ """
+ def __init__(self, exc, expression=None, expression_pos=None, **kwargs):
+ super(SassError, self).__init__(**kwargs)
+
+ self.exc = exc
+ self.expression = expression
+ self.expression_pos = expression_pos
+
+ _, _, self.original_traceback = sys.exc_info()
+
+ def format_prefix(self):
+ """Return the general name of the error and the contents of the rule or
+ property that caused the failure. This is the initial part of the
+ error message and should be error-specific.
+ """
+ # TODO this contains NULs and line numbers; could be much prettier
+ if self.rule_stack:
+ return (
+ "Error parsing block:\n" +
+ " " + self.rule_stack[0].unparsed_contents + "\n"
+ )
+ else:
+ return "Unknown error\n"
+
def format_python_stack(self):
"""Return a traceback of Python frames, from where the error occurred
to where it was first caught and wrapped.
@@ -170,6 +220,7 @@ class SassEvaluationError(SassError):
"""Error raised when evaluating a parsed expression fails."""
def format_prefix(self):
+ # TODO boy this is getting repeated a lot
# TODO would be nice for the AST to have position information
# TODO might be nice to print the AST and indicate where the failure
# was?