Don't assume request object we are passed at any point still has our custom attributes.
[django-ssify.git] / ssify / exceptions.py
1 # -*- coding: utf-8 -*-
2 # This file is part of django-ssify, licensed under GNU Affero GPLv3 or later.
3 # Copyright © Fundacja Nowoczesna Polska. See README.md for more information.
4 #
5 """
6 Exception classes used in django-ssify.
7 """
8 from __future__ import unicode_literals
9 from django.utils.encoding import python_2_unicode_compatible
10
11
12 class RequestMixin(object):
13     """Lets us print request and view data in the exceptions messages."""
14
15     def __init__(self, request, *args):
16         self.request = request
17         super(RequestMixin, self).__init__(*args)
18
19     def view_path(self):
20         """Returns full Python path to the view used in the request."""
21         try:
22             view = self.request.resolver_match.func
23             return "%s.%s" % (view.__module__, view.__name__)
24         except AttributeError:
25             return "<unknown>"
26
27
28 class SsifyError(RequestMixin, BaseException):
29     """Base class for all the errors."""
30     pass
31
32
33 class SsifyWarning(RequestMixin, Warning):
34     """Base class for all the warnings."""
35     pass
36
37
38 @python_2_unicode_compatible
39 class UndeclaredSsiVarsError(SsifyError):
40     """An ssi_included view used a SSI variable, but didn't declare it."""
41
42     def __init__(self, request, ssi_vars):
43         super(UndeclaredSsiVarsError, self).__init__(request, ssi_vars)
44
45     def __str__(self):
46         return "The view '%s' at '%s' is marked as `ssi_included`, "\
47             "but it uses ssi variables not declared in `get_ssi_vars` "\
48             "argument: %s. " % (
49                 self.view_path(), self.request.get_full_path(),
50                 repr(self.args[0]))
51
52
53 @python_2_unicode_compatible
54 class UnusedSsiVarsWarning(SsifyWarning):
55     """An ssi_included declared a SSI variable, but didn't use it."""
56
57     def __init__(self, request, ssi_vars):
58         super(UnusedSsiVarsWarning, self).__init__(request, ssi_vars)
59
60     def __str__(self):
61         return "The `ssi_included` view '%s' at '%s' declares "\
62             "using SSI variables %s but it looks like they're not "\
63             "really used. " % (
64                 self.view_path(), self.request.get_full_path(),
65                 self.args[0])
66
67
68 @python_2_unicode_compatible
69 class NoLangFieldError(SsifyError):
70     """ssi_included views should have a `lang` field in their URL patterns."""
71
72     def __init__(self, request):
73         super(NoLangFieldError, self).__init__(request)
74
75     def __str__(self):
76         return "The view '%s' at '%s' is marked as `ssi_included` "\
77             "with use_lang=True, but its URL match doesn't provide "\
78             "a 'lang' keyword argument for language. " % (
79                 self.view_path(), self.request.get_full_path())
80
81
82 @python_2_unicode_compatible
83 class SsiVarsDependencyCycleError(SsifyError):
84     """Looks like there's a dependency cycle in the SSI variables.
85
86     Yet to find an example of a configuration that triggers that.
87     """
88
89     def __init__(self, request, ssi_vars, resolved):
90         super(SsiVarsDependencyCycleError, self).__init__(
91             request, ssi_vars, resolved)
92
93     def __str__(self):
94         return "The view '%s' at '%s' has dependency cycle. "\
95             "Unresolved SSI variables:\n%s\n\n"\
96             "Resolved SSI variables:\n%s." % (
97                 self.view_path(), self.request.get_full_path(),
98                 self.args[0], self.args[1])