A somewhat usable and tested version.
[django-ssify.git] / ssify / exceptions.py
index 8acc55c..d9e5319 100644 (file)
@@ -1,68 +1,98 @@
-class SsifyError(BaseException):
+# -*- coding: utf-8 -*-
+# This file is part of django-ssify, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See README.md for more information.
+#
+"""
+Exception classes used in django-ssify.
+"""
+from __future__ import unicode_literals
+from django.utils.encoding import python_2_unicode_compatible
+
+
+class RequestMixin(object):
+    """Lets us print request and view data in the exceptions messages."""
+
+    def __init__(self, request, *args):
+        self.request = request
+        super(RequestMixin, self).__init__(*args)
+
+    def view_path(self):
+        """Returns full Python path to the view used in the request."""
+        try:
+            view = self.request.resolver_match.func
+            return "%s.%s" % (view.__module__, view.__name__)
+        except AttributeError:
+            return "<unknown>"
+
+
+class SsifyError(RequestMixin, BaseException):
+    """Base class for all the errors."""
     pass
 
 
-class SsifyWarning(Warning):
+class SsifyWarning(RequestMixin, Warning):
+    """Base class for all the warnings."""
     pass
 
 
+@python_2_unicode_compatible
 class UndeclaredSsiVarsError(SsifyError):
+    """An ssi_included view used a SSI variable, but didn't declare it."""
+
     def __init__(self, request, ssi_vars):
         super(UndeclaredSsiVarsError, self).__init__(request, ssi_vars)
 
     def __str__(self):
-        request = self.args[0]
-        view = request.resolver_match.func
-        return "The view '%s.%s' at '%s' is marked as `ssi_included`, "\
+        return "The view '%s' at '%s' is marked as `ssi_included`, "\
             "but it uses ssi variables not declared in `get_ssi_vars` "\
             "argument: %s. " % (
-                view.__module__, view.__name__, request.path,
-                repr(self.args[1]))
+                self.view_path(), self.request.get_full_path(),
+                repr(self.args[0]))
 
 
+@python_2_unicode_compatible
 class UnusedSsiVarsWarning(SsifyWarning):
+    """An ssi_included declared a SSI variable, but didn't use it."""
+
     def __init__(self, request, ssi_vars):
         super(UnusedSsiVarsWarning, self).__init__(request, ssi_vars)
 
     def __str__(self):
-        request = self.args[0]
-        view = request.resolver_match.func
-        return "The `ssi_included` view '%s.%s' at '%s' declares "\
+        return "The `ssi_included` view '%s' at '%s' declares "\
             "using SSI variables %s but it looks like they're not "\
             "really used. " % (
-                view.__module__, view.__name__, request.path, self.args[1])
-
-
-class UndeclaredSsiRefError(SsifyError):
-    def __init__(self, request, var, ref_name):
-        super(UndeclaredSsiRefError, self).__init__(request, var, ref_name)
-
-    def __str__(self):
-        request = self.args[0]
-        view = request.resolver_match.func
-        return "Error while rendering ssi_included view '%s.%s' at '%s': "\
-            "SSI variable %s references variable %s, which doesn't match "\
-            "any variable declared in `get_ssi_vars`. " % (
-                view.__module__, view.__name__, request.path,
-                repr(self.args[1]), self.args[2])
+                self.view_path(), self.request.get_full_path(),
+                self.args[0])
 
 
+@python_2_unicode_compatible
 class NoLangFieldError(SsifyError):
+    """ssi_included views should have a `lang` field in their URL patterns."""
+
     def __init__(self, request):
         super(NoLangFieldError, self).__init__(request)
 
     def __str__(self):
-        request = self.args[0]
-        view = request.resolver_match.func
-        return "The view '%s.%s' at '%s' is marked as `ssi_included` "\
+        return "The view '%s' at '%s' is marked as `ssi_included` "\
             "with use_lang=True, but its URL match doesn't provide "\
             "a 'lang' keyword argument for language. " % (
-            view.__module__, view.__name__, request.path)
+                self.view_path(), self.request.get_full_path())
 
 
+@python_2_unicode_compatible
 class SsiVarsDependencyCycleError(SsifyError):
-    def __init__(self, ssi_vars):
-        super(SsiVarsDependencyCycleError, self).__init__(ssi_vars)
+    """Looks like there's a dependency cycle in the SSI variables.
+
+    Yet to find an example of a configuration that triggers that.
+    """
+
+    def __init__(self, request, ssi_vars, resolved):
+        super(SsiVarsDependencyCycleError, self).__init__(
+            request, ssi_vars, resolved)
 
     def __str__(self):
-        return "Dependency cycle in SSI variables: %s." % self.args[0]
+        return "The view '%s' at '%s' has dependency cycle. "\
+            "Unresolved SSI variables:\n%s\n\n"\
+            "Resolved SSI variables:\n%s." % (
+                self.view_path(), self.request.get_full_path(),
+                self.args[0], self.args[1])