Django

Code

root/django/trunk/django/core/handlers/base.py

Revision 8464, 8.6 kB (checked in by russellm, 3 months ago)

Fixed #8136: Added a signal emission when an error is raised handling an error. This was required for the test client to handle missing 404.html templates and errors in the 404.html template. Thanks to danfairs for the report and fix.

  • Property svn:eol-style set to native
Line 
1 import sys
2
3 from django import http
4 from django.core import signals
5 from django.utils.encoding import force_unicode
6
7 class BaseHandler(object):
8     # Changes that are always applied to a response (in this order).
9     response_fixes = [
10         http.fix_location_header,
11         http.conditional_content_removal,
12         http.fix_IE_for_attach,
13         http.fix_IE_for_vary,
14     ]
15
16     def __init__(self):
17         self._request_middleware = self._view_middleware = self._response_middleware = self._exception_middleware = None
18
19     def load_middleware(self):
20         """
21         Populate middleware lists from settings.MIDDLEWARE_CLASSES.
22
23         Must be called after the environment is fixed (see __call__).
24         """
25         from django.conf import settings
26         from django.core import exceptions
27         self._request_middleware = []
28         self._view_middleware = []
29         self._response_middleware = []
30         self._exception_middleware = []
31         for middleware_path in settings.MIDDLEWARE_CLASSES:
32             try:
33                 dot = middleware_path.rindex('.')
34             except ValueError:
35                 raise exceptions.ImproperlyConfigured, '%s isn\'t a middleware module' % middleware_path
36             mw_module, mw_classname = middleware_path[:dot], middleware_path[dot+1:]
37             try:
38                 mod = __import__(mw_module, {}, {}, [''])
39             except ImportError, e:
40                 raise exceptions.ImproperlyConfigured, 'Error importing middleware %s: "%s"' % (mw_module, e)
41             try:
42                 mw_class = getattr(mod, mw_classname)
43             except AttributeError:
44                 raise exceptions.ImproperlyConfigured, 'Middleware module "%s" does not define a "%s" class' % (mw_module, mw_classname)
45
46             try:
47                 mw_instance = mw_class()
48             except exceptions.MiddlewareNotUsed:
49                 continue
50
51             if hasattr(mw_instance, 'process_request'):
52                 self._request_middleware.append(mw_instance.process_request)
53             if hasattr(mw_instance, 'process_view'):
54                 self._view_middleware.append(mw_instance.process_view)
55             if hasattr(mw_instance, 'process_response'):
56                 self._response_middleware.insert(0, mw_instance.process_response)
57             if hasattr(mw_instance, 'process_exception'):
58                 self._exception_middleware.insert(0, mw_instance.process_exception)
59
60     def get_response(self, request):
61         "Returns an HttpResponse object for the given HttpRequest"
62         from django.core import exceptions, urlresolvers
63         from django.conf import settings
64
65         # Apply request middleware
66         for middleware_method in self._request_middleware:
67             response = middleware_method(request)
68             if response:
69                 return response
70
71         # Get urlconf from request object, if available.  Otherwise use default.
72         urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
73
74         resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
75         try:
76             callback, callback_args, callback_kwargs = resolver.resolve(
77                     request.path_info)
78
79             # Apply view middleware
80             for middleware_method in self._view_middleware:
81                 response = middleware_method(request, callback, callback_args, callback_kwargs)
82                 if response:
83                     return response
84
85             try:
86                 response = callback(request, *callback_args, **callback_kwargs)
87             except Exception, e:
88                 # If the view raised an exception, run it through exception
89                 # middleware, and if the exception middleware returns a
90                 # response, use that. Otherwise, reraise the exception.
91                 for middleware_method in self._exception_middleware:
92                     response = middleware_method(request, e)
93                     if response:
94                         return response
95                 raise
96
97             # Complain if the view returned None (a common error).
98             if response is None:
99                 try:
100                     view_name = callback.func_name # If it's a function
101                 except AttributeError:
102                     view_name = callback.__class__.__name__ + '.__call__' # If it's a class
103                 raise ValueError, "The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name)
104
105             return response
106         except http.Http404, e:
107             if settings.DEBUG:
108                 from django.views import debug
109                 return debug.technical_404_response(request, e)
110             else:
111                 try:
112                     callback, param_dict = resolver.resolve404()
113                     return callback(request, **param_dict)
114                 except:
115                     try:
116                         return self.handle_uncaught_exception(request, resolver, sys.exc_info())
117                     finally:
118                         receivers = signals.got_request_exception.send(sender=self.__class__, request=request)
119         except exceptions.PermissionDenied:
120             return http.HttpResponseForbidden('<h1>Permission denied</h1>')
121         except SystemExit:
122             # Allow sys.exit() to actually exit. See tickets #1023 and #4701
123             raise
124         except: # Handle everything else, including SuspiciousOperation, etc.
125             # Get the exception info now, in case another exception is thrown later.
126             exc_info = sys.exc_info()
127             receivers = signals.got_request_exception.send(sender=self.__class__, request=request)
128             return self.handle_uncaught_exception(request, resolver, exc_info)
129
130     def handle_uncaught_exception(self, request, resolver, exc_info):
131         """
132         Processing for any otherwise uncaught exceptions (those that will
133         generate HTTP 500 responses). Can be overridden by subclasses who want
134         customised 500 handling.
135
136         Be *very* careful when overriding this because the error could be
137         caused by anything, so assuming something like the database is always
138         available would be an error.
139         """
140         from django.conf import settings
141         from django.core.mail import mail_admins
142
143         if settings.DEBUG_PROPAGATE_EXCEPTIONS:
144             raise
145
146         if settings.DEBUG:
147             from django.views import debug
148             return debug.technical_500_response(request, *exc_info)
149
150         # When DEBUG is False, send an error message to the admins.
151         subject = 'Error (%s IP): %s' % ((request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS and 'internal' or 'EXTERNAL'), request.path)
152         try:
153             request_repr = repr(request)
154         except:
155             request_repr = "Request repr() unavailable"
156         message = "%s\n\n%s" % (self._get_traceback(exc_info), request_repr)
157         mail_admins(subject, message, fail_silently=True)
158         # Return an HttpResponse that displays a friendly error message.
159         callback, param_dict = resolver.resolve500()
160         return callback(request, **param_dict)
161
162     def _get_traceback(self, exc_info=None):
163         "Helper function to return the traceback as a string"
164         import traceback
165         return '\n'.join(traceback.format_exception(*(exc_info or sys.exc_info())))
166
167     def apply_response_fixes(self, request, response):
168         """
169         Applies each of the functions in self.response_fixes to the request and
170         response, modifying the response in the process. Returns the new
171         response.
172         """
173         for func in self.response_fixes:
174             response = func(request, response)
175         return response
176
177 def get_script_name(environ):
178     """
179     Returns the equivalent of the HTTP request's SCRIPT_NAME environment
180     variable. If Apache mod_rewrite has been used, returns what would have been
181     the script name prior to any rewriting (so it's the script name as seen
182     from the client's perspective), unless DJANGO_USE_POST_REWRITE is set (to
183     anything).
184     """
185     from django.conf import settings
186     if settings.FORCE_SCRIPT_NAME is not None:
187         return force_unicode(settings.FORCE_SCRIPT_NAME)
188
189     # If Apache's mod_rewrite had a whack at the URL, Apache set either
190     # SCRIPT_URL or REDIRECT_URL to the full resource URL before applying any
191     # rewrites. Unfortunately not every webserver (lighttpd!) passes this
192     # information through all the time, so FORCE_SCRIPT_NAME, above, is still
193     # needed.
194     script_url = environ.get('SCRIPT_URL', u'')
195     if not script_url:
196         script_url = environ.get('REDIRECT_URL', u'')
197     if script_url:
198         return force_unicode(script_url[:-len(environ.get('PATH_INFO', ''))])
199     return force_unicode(environ.get('SCRIPT_NAME', u''))
Note: See TracBrowser for help on using the browser.