Ticket #6094: 6094.diff
File 6094.diff, 19.9 KB (added by , 17 years ago) |
---|
-
tests/regressiontests/handler_exceptions/models.py
=== added directory 'tests/regressiontests/handler_exceptions' === added file 'tests/regressiontests/handler_exceptions/__init__.py' === added file 'tests/regressiontests/handler_exceptions/models.py'
1 # models file for tests to run. -
tests/regressiontests/handler_exceptions/tests.py
=== added file 'tests/regressiontests/handler_exceptions/tests.py'
1 from django.test import TestCase 2 from django.test.client import Client, ClientHandler 3 4 class GoodMiddleware(object): 5 def process_request(self, *args, **kwargs): 6 pass 7 def process_view(self, *args, **kwargs): 8 pass 9 def process_exception(self, *args, **kwargs): 10 pass 11 def process_response(self, request, response): 12 return response 13 14 15 class MiddlewareException(Exception): pass 16 class RequestMiddlewareException(MiddlewareException): pass 17 class ViewMiddlewareException(MiddlewareException): pass 18 class ExceptionMiddlewareException(MiddlewareException): pass 19 class ResponseMiddlewareException(MiddlewareException): pass 20 21 22 class BadRequestMiddleware(object): 23 def process_request(self, *args, **kwargs): 24 raise RequestMiddlewareException 25 26 class BadViewMiddleware(object): 27 def process_view(self, *args, **kwargs): 28 raise ViewMiddlewareException 29 30 class BadExceptionMiddleware(object): 31 def process_exception(self, *args, **kwargs): 32 raise ExceptionMiddlewareException 33 34 class BadResponseMiddleware(object): 35 def process_response(self, *args, **kwargs): 36 raise ResponseMiddlewareException 37 38 39 class MiddlewareResettingClientHandler(ClientHandler): 40 """ 41 A handler that clears loaded middleware after each request. 42 """ 43 def __call__(self, *args, **kwargs): 44 response = super(MiddlewareResettingClientHandler, self).__call__(*args, **kwargs) 45 # ClientHandler's __call__ method will load middleware if 46 # self._request_middleware is None. We set it to None here so that 47 # middleware will be reloaded next request. 48 self._request_middleware = None 49 return response 50 51 52 class HandlerClient(Client): 53 """ 54 A custom Client that doesn't reraise request-handling exceptions and uses 55 the MiddlewareResettingClientHandler. 56 """ 57 def __init__(self, *args, **kwargs): 58 super(HandlerClient, self).__init__(*args, **kwargs) 59 self.reraise_exceptions = False 60 self.handler = MiddlewareResettingClientHandler() 61 62 63 class HandlerExceptionTests(TestCase): 64 def setUp(self): 65 self.client = HandlerClient() 66 self.default_middleware = None 67 # Combinations that will raise errors. 68 self.bad_combinations = ( 69 # Middleware, request path 70 ('BadRequestMiddleware', '/good_view/', RequestMiddlewareException), 71 ('BadViewMiddleware', '/good_view/', ViewMiddlewareException), 72 ('BadResponseMiddleware', '/good_view/', ResponseMiddlewareException), 73 ('BadRequestMiddleware', '/bad_view/', RequestMiddlewareException), 74 ('BadViewMiddleware', '/bad_view/', ViewMiddlewareException), 75 ('BadExceptionMiddleware', '/bad_view/', ExceptionMiddlewareException), 76 ('BadResponseMiddleware', '/bad_view/', ResponseMiddlewareException), 77 ) 78 # Combinations that will not raise errors. 79 self.good_combinations = ( 80 ('GoodMiddleware', '/good_view/'), 81 # Exception middleware doesn't get run if the view doesn't raise 82 # an exception. 83 ('BadExceptionMiddleware', '/good_view/'), 84 ) 85 86 def setup_middleware(self, middleware): 87 """ 88 Append middleware to list of installed middleware. 89 """ 90 from django.conf import settings 91 # If we haven't saved the default middleware, do so now. 92 if self.default_middleware is None: 93 self.default_middleware = settings.MIDDLEWARE_CLASSES 94 middleware_path = 'regressiontests.handler_exceptions.tests.' + middleware 95 settings.MIDDLEWARE_CLASSES += (middleware_path,) 96 97 def reset_middleware(self): 98 """ 99 Restores middleware to the default installed middleware. 100 """ 101 from django.conf import settings 102 settings.MIDDLEWARE_CLASSES = self.default_middleware 103 104 def test_exception_handling(self): 105 """ 106 Tests that exceptions raised in middleware and views are properly 107 caught and render error templates. 108 """ 109 # Just for good measure, check that the good combination doesn't raise 110 # an error. 111 for middleware, path in self.good_combinations: 112 self.setup_middleware(middleware) 113 response = self.client.get('/handler_exceptions' + path) 114 self.assertContains(response, 'good view') 115 self.reset_middleware() 116 # Now test that all the bad combinations render the 500 template. 117 for middleware, path, exception in self.bad_combinations: 118 self.setup_middleware(middleware) 119 response = self.client.get('/handler_exceptions' + path) 120 self.assertContains(response, '500 Error', status_code=500) 121 self.assertTemplateUsed(response, '500.html') 122 self.reset_middleware() 123 124 def test_exception_raising(self): 125 """ 126 Tests that the correct exceptions bubble up from middleware with errors. 127 """ 128 # Turn on client's reraising of exceptions for this test. 129 self.client.reraise_exceptions = True 130 for middleware, path, exception in self.bad_combinations: 131 self.setup_middleware(middleware) 132 fail = False 133 try: 134 self.client.get('/handler_exceptions' + path) 135 # If above didn't raise an exception, then test failed. 136 fail = True 137 except exception: 138 # We got the exception we were looking for, test passes. 139 pass 140 except Exception: 141 # We got an exception that we wore not expecting. 142 from sys import exc_info 143 unexpected_exception = exc_info()[0] 144 self.fail("Expected %s to be raised, but got %s" 145 % (exception, unexpected_exception)) 146 if fail: 147 self.fail("Expected %s to be raised. Middleware: %s, Path: %s" 148 % (exception, middleware, path)) 149 self.reset_middleware() -
tests/regressiontests/handler_exceptions/urls.py
=== added file 'tests/regressiontests/handler_exceptions/urls.py'
1 from django.conf.urls.defaults import * 2 3 import views 4 5 urlpatterns = patterns('', 6 (r'^good_view/$', views.good_view), 7 (r'^bad_view/$', views.bad_view), 8 ) -
tests/regressiontests/handler_exceptions/views.py
=== added file 'tests/regressiontests/handler_exceptions/views.py'
1 from django.http import HttpResponse 2 3 def good_view(request): 4 return HttpResponse("good view") 5 6 def bad_view(request): 7 raise Exception("View raised exception") -
django/core/handlers/base.py
=== modified file 'django/core/handlers/base.py'
53 53 if hasattr(mw_instance, 'process_exception'): 54 54 self._exception_middleware.insert(0, mw_instance.process_exception) 55 55 56 def get_response(self, request): 57 "Returns an HttpResponse object for the given HttpRequest" 58 from django.core import exceptions, urlresolvers 59 from django.core.mail import mail_admins 56 def handle_request(self, request): 57 dispatcher.send(signal=signals.request_started) 58 try: 59 try: 60 request = self.request_class(request) 61 except UnicodeDecodeError: 62 response = http.HttpResponseBadRequest() 63 else: 64 # See if the request middleware returns a response. 65 response = self.get_response(self.apply_request_middleware, 66 request) 67 # If not, then call the view. 68 if not response: 69 response = self.get_response(self.run_view_processing, 70 request) 71 # All responses go through the response middleware. 72 response = self.get_response(self.apply_response_middleware, 73 request, response) 74 response = self.apply_response_fixes(request, response) 75 return response 76 finally: 77 dispatcher.send(signal=signals.request_finished) 78 79 def resolver(self, request): 80 """ 81 Returns a RegexURLResolver for the request's urlconf. 82 83 If the request does not have a urlconf object, then the default of 84 settings.ROOT_URLCONF is used. 85 """ 86 from django.core.urlresolvers import RegexURLResolver 60 87 from django.conf import settings 88 urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF) 89 return RegexURLResolver(r'^/', urlconf) 61 90 62 # Apply request middleware 91 def apply_request_middleware(self, request): 92 """ 93 Applies request middleware one at a time until a response is returned. 94 If none of request middleware returns a response, then return None. 95 """ 63 96 for middleware_method in self._request_middleware: 64 97 response = middleware_method(request) 65 98 if response: 66 99 return response 67 100 68 # Get urlconf from request object, if available. Otherwise use default. 69 urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF) 70 71 resolver = urlresolvers.RegexURLResolver(r'^/', urlconf) 101 def run_view_processing(self, request): 102 """ 103 Apply view middleware and call view. 104 """ 105 callback, callback_args, callback_kwargs = self.resolver(request).resolve(request.path) 106 107 # Apply view middleware 108 for middleware_method in self._view_middleware: 109 response = middleware_method(request, callback, callback_args, callback_kwargs) 110 if response: 111 return response 112 72 113 try: 73 callback, callback_args, callback_kwargs = resolver.resolve(request.path) 74 75 # Apply view middleware 76 for middleware_method in self._view_middleware: 77 response = middleware_method(request, callback, callback_args, callback_kwargs) 114 response = callback(request, *callback_args, **callback_kwargs) 115 except Exception, e: 116 # If the view raised an exception, run it through exception 117 # middleware, and if the exception middleware returns a 118 # response, use that. Otherwise, reraise the exception. 119 for middleware_method in self._exception_middleware: 120 response = middleware_method(request, e) 78 121 if response: 79 122 return response 123 raise 80 124 125 # Complain if the view returned None (a common error). 126 if response is None: 81 127 try: 82 response = callback(request, *callback_args, **callback_kwargs) 83 except Exception, e: 84 # If the view raised an exception, run it through exception 85 # middleware, and if the exception middleware returns a 86 # response, use that. Otherwise, reraise the exception. 87 for middleware_method in self._exception_middleware: 88 response = middleware_method(request, e) 89 if response: 90 return response 91 raise 92 93 # Complain if the view returned None (a common error). 128 view_name = callback.func_name # If it's a function 129 except AttributeError: 130 view_name = callback.__class__.__name__ + '.__call__' # If it's a class 131 raise ValueError, "The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name) 132 133 return response 134 135 def apply_response_middleware(self, request, response): 136 """ 137 Applies all response middleware to the response. 138 """ 139 for middleware_method in self._response_middleware: 140 response = middleware_method(request, response) 141 return response 142 143 def get_response(self, method, request, response=None): 144 "Returns an HttpResponse object for the given HttpRequest" 145 from django.core import exceptions 146 from django.core.mail import mail_admins 147 from django.conf import settings 148 149 try: 94 150 if response is None: 95 try: 96 view_name = callback.func_name # If it's a function 97 except AttributeError: 98 view_name = callback.__class__.__name__ + '.__call__' # If it's a class 99 raise ValueError, "The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name) 100 101 return response 151 return method(request) 152 else: 153 return method(request, response) 102 154 except http.Http404, e: 103 155 if settings.DEBUG: 104 156 from django.views import debug 105 157 return debug.technical_404_response(request, e) 106 158 else: 107 callback, param_dict = resolver.resolve404()159 callback, param_dict = self.resolver(request).resolve404() 108 160 return callback(request, **param_dict) 109 161 except exceptions.PermissionDenied: 110 162 return http.HttpResponseForbidden('<h1>Permission denied</h1>') … … 127 179 message = "%s\n\n%s" % (self._get_traceback(exc_info), request_repr) 128 180 mail_admins(subject, message, fail_silently=True) 129 181 # Return an HttpResponse that displays a friendly error message. 130 callback, param_dict = resolver.resolve500()182 callback, param_dict = self.resolver(request).resolve500() 131 183 return callback(request, **param_dict) 132 184 133 185 def _get_traceback(self, exc_info=None): -
django/core/handlers/modpython.py
=== modified file 'django/core/handlers/modpython.py'
2 2 from pprint import pformat 3 3 4 4 from django import http 5 from django.core import signals6 5 from django.core.handlers.base import BaseHandler 7 from django.dispatch import dispatcher8 6 from django.utils import datastructures 9 7 from django.utils.encoding import force_unicode 10 8 … … 151 149 if self._request_middleware is None: 152 150 self.load_middleware() 153 151 154 dispatcher.send(signal=signals.request_started) 155 try: 156 try: 157 request = self.request_class(req) 158 except UnicodeDecodeError: 159 response = http.HttpResponseBadRequest() 160 else: 161 response = self.get_response(request) 162 163 # Apply response middleware 164 for middleware_method in self._response_middleware: 165 response = middleware_method(request, response) 166 response = self.apply_response_fixes(request, response) 167 finally: 168 dispatcher.send(signal=signals.request_finished) 152 response = self.handle_request(req) 169 153 170 154 # Convert our custom HttpResponse object back into the mod_python req. 171 155 req.content_type = response['Content-Type'] -
django/core/handlers/wsgi.py
=== modified file 'django/core/handlers/wsgi.py'
6 6 from StringIO import StringIO 7 7 8 8 from django import http 9 from django.core import signals10 9 from django.core.handlers.base import BaseHandler 11 from django.dispatch import dispatcher12 10 from django.utils import datastructures 13 11 from django.utils.encoding import force_unicode 14 12 … … 195 193 self.load_middleware() 196 194 self.initLock.release() 197 195 198 dispatcher.send(signal=signals.request_started) 199 try: 200 try: 201 request = self.request_class(environ) 202 except UnicodeDecodeError: 203 response = http.HttpResponseBadRequest() 204 else: 205 response = self.get_response(request) 206 207 # Apply response middleware 208 for middleware_method in self._response_middleware: 209 response = middleware_method(request, response) 210 response = self.apply_response_fixes(request, response) 211 finally: 212 dispatcher.send(signal=signals.request_finished) 196 response = self.handle_request(environ) 213 197 214 198 try: 215 199 status_text = STATUS_CODE_TEXT[response.status_code] -
django/test/client.py
=== modified file 'django/test/client.py'
25 25 Uses the WSGI interface to compose requests, but returns 26 26 the raw HttpResponse object 27 27 """ 28 request_class = WSGIRequest 29 28 30 def __call__(self, environ): 29 31 from django.conf import settings 30 from django.core import signals31 32 32 33 # Set up middleware if needed. We couldn't do this earlier, because 33 34 # settings weren't available. 34 35 if self._request_middleware is None: 35 36 self.load_middleware() 36 37 37 dispatcher.send(signal=signals.request_started) 38 try: 39 request = WSGIRequest(environ) 40 response = self.get_response(request) 41 42 # Apply response middleware 43 for middleware_method in self._response_middleware: 44 response = middleware_method(request, response) 45 response = self.apply_response_fixes(request, response) 46 finally: 47 dispatcher.send(signal=signals.request_finished) 48 49 return response 38 return self.handle_request(environ) 50 39 51 40 def store_rendered_templates(store, signal, sender, template, context): 52 41 "A utility function for storing templates and contexts that are rendered" … … 96 85 ]) 97 86 return '\r\n'.join(lines) 98 87 99 class Client :88 class Client(object): 100 89 """ 101 90 A class that can act as a client for testing purposes. 102 91 … … 119 108 self.defaults = defaults 120 109 self.cookies = SimpleCookie() 121 110 self.exc_info = None 111 self.reraise_exceptions = True 122 112 123 113 def store_exc_info(self, *args, **kwargs): 124 114 """ … … 180 170 raise 181 171 182 172 # Look for a signalled exception and reraise it 183 if self.exc_info :173 if self.exc_info and self.reraise_exceptions: 184 174 raise self.exc_info[1], None, self.exc_info[2] 185 175 186 176 # Save the client and request that stimulated the response … … 262 252 self.cookies[settings.SESSION_COOKIE_NAME]['expires'] = None 263 253 264 254 # Save the session values 265 request.session.save() 255 request.session.save() 266 256 267 257 return True 268 258 else: -
tests/urls.py
=== modified file 'tests/urls.py'
14 14 15 15 # django built-in views 16 16 (r'^views/', include('regressiontests.views.urls')), 17 18 (r'^handler_exceptions/', include('regressiontests.handler_exceptions.urls')), 17 19 )