Ticket #6094: 6094.diff

File 6094.diff, 19.9 KB (added by Gary Wilson, 16 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'
     
     1from django.test import TestCase
     2from django.test.client import Client, ClientHandler
     3
     4class 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
     15class MiddlewareException(Exception): pass
     16class RequestMiddlewareException(MiddlewareException): pass
     17class ViewMiddlewareException(MiddlewareException): pass
     18class ExceptionMiddlewareException(MiddlewareException): pass
     19class ResponseMiddlewareException(MiddlewareException): pass
     20
     21
     22class BadRequestMiddleware(object):
     23    def process_request(self, *args, **kwargs):
     24        raise RequestMiddlewareException
     25
     26class BadViewMiddleware(object):
     27    def process_view(self, *args, **kwargs):
     28        raise ViewMiddlewareException
     29
     30class BadExceptionMiddleware(object):
     31    def process_exception(self, *args, **kwargs):
     32        raise ExceptionMiddlewareException
     33
     34class BadResponseMiddleware(object):
     35    def process_response(self, *args, **kwargs):
     36        raise ResponseMiddlewareException
     37
     38
     39class 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
     52class 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
     63class 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'
     
     1from django.conf.urls.defaults import *
     2
     3import views
     4
     5urlpatterns = 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'
     
     1from django.http import HttpResponse
     2
     3def good_view(request):
     4    return HttpResponse("good view")
     5
     6def bad_view(request):
     7    raise Exception("View raised exception")
  • django/core/handlers/base.py

    === modified file 'django/core/handlers/base.py'
     
    5353            if hasattr(mw_instance, 'process_exception'):
    5454                self._exception_middleware.insert(0, mw_instance.process_exception)
    5555
    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
    6087        from django.conf import settings
     88        urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
     89        return RegexURLResolver(r'^/', urlconf)
    6190
    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        """
    6396        for middleware_method in self._request_middleware:
    6497            response = middleware_method(request)
    6598            if response:
    6699                return response
    67100
    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
    72113        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)
    78121                if response:
    79122                    return response
     123            raise
    80124
     125        # Complain if the view returned None (a common error).
     126        if response is None:
    81127            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:
    94150            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)
    102154        except http.Http404, e:
    103155            if settings.DEBUG:
    104156                from django.views import debug
    105157                return debug.technical_404_response(request, e)
    106158            else:
    107                 callback, param_dict = resolver.resolve404()
     159                callback, param_dict = self.resolver(request).resolve404()
    108160                return callback(request, **param_dict)
    109161        except exceptions.PermissionDenied:
    110162            return http.HttpResponseForbidden('<h1>Permission denied</h1>')
     
    127179                message = "%s\n\n%s" % (self._get_traceback(exc_info), request_repr)
    128180                mail_admins(subject, message, fail_silently=True)
    129181                # Return an HttpResponse that displays a friendly error message.
    130                 callback, param_dict = resolver.resolve500()
     182                callback, param_dict = self.resolver(request).resolve500()
    131183                return callback(request, **param_dict)
    132184
    133185    def _get_traceback(self, exc_info=None):
  • django/core/handlers/modpython.py

    === modified file 'django/core/handlers/modpython.py'
     
    22from pprint import pformat
    33
    44from django import http
    5 from django.core import signals
    65from django.core.handlers.base import BaseHandler
    7 from django.dispatch import dispatcher
    86from django.utils import datastructures
    97from django.utils.encoding import force_unicode
    108
     
    151149        if self._request_middleware is None:
    152150            self.load_middleware()
    153151
    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)
    169153
    170154        # Convert our custom HttpResponse object back into the mod_python req.
    171155        req.content_type = response['Content-Type']
  • django/core/handlers/wsgi.py

    === modified file 'django/core/handlers/wsgi.py'
     
    66    from StringIO import StringIO
    77
    88from django import http
    9 from django.core import signals
    109from django.core.handlers.base import BaseHandler
    11 from django.dispatch import dispatcher
    1210from django.utils import datastructures
    1311from django.utils.encoding import force_unicode
    1412
     
    195193                self.load_middleware()
    196194            self.initLock.release()
    197195
    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)
    213197
    214198        try:
    215199            status_text = STATUS_CODE_TEXT[response.status_code]
  • django/test/client.py

    === modified file 'django/test/client.py'
     
    2525    Uses the WSGI interface to compose requests, but returns
    2626    the raw HttpResponse object
    2727    """
     28    request_class = WSGIRequest
     29
    2830    def __call__(self, environ):
    2931        from django.conf import settings
    30         from django.core import signals
    3132
    3233        # Set up middleware if needed. We couldn't do this earlier, because
    3334        # settings weren't available.
    3435        if self._request_middleware is None:
    3536            self.load_middleware()
    3637
    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)
    5039
    5140def store_rendered_templates(store, signal, sender, template, context):
    5241    "A utility function for storing templates and contexts that are rendered"
     
    9685    ])
    9786    return '\r\n'.join(lines)
    9887
    99 class Client:
     88class Client(object):
    10089    """
    10190    A class that can act as a client for testing purposes.
    10291
     
    119108        self.defaults = defaults
    120109        self.cookies = SimpleCookie()
    121110        self.exc_info = None
     111        self.reraise_exceptions = True
    122112
    123113    def store_exc_info(self, *args, **kwargs):
    124114        """
     
    180170                raise
    181171
    182172        # Look for a signalled exception and reraise it
    183         if self.exc_info:
     173        if self.exc_info and self.reraise_exceptions:
    184174            raise self.exc_info[1], None, self.exc_info[2]
    185175
    186176        # Save the client and request that stimulated the response
     
    262252            self.cookies[settings.SESSION_COOKIE_NAME]['expires'] = None
    263253
    264254            # Save the session values
    265             request.session.save()   
     255            request.session.save()
    266256
    267257            return True
    268258        else:
  • tests/urls.py

    === modified file 'tests/urls.py'
     
    1414   
    1515    # django built-in views
    1616    (r'^views/', include('regressiontests.views.urls')),
     17   
     18    (r'^handler_exceptions/', include('regressiontests.handler_exceptions.urls')),
    1719)
Back to Top