Ticket #6094: http_refactor_r7320.patch

File http_refactor_r7320.patch, 23.8 KB (added by George Vilches, 16 years ago)

Patch cleanup to work with r7320, no other changes made from previous.

  • django/test/client.py

     
    2323    Uses the WSGI interface to compose requests, but returns
    2424    the raw HttpResponse object
    2525    """
     26    request_class = WSGIRequest
     27
    2628    def __call__(self, environ):
    2729        from django.conf import settings
    28         from django.core import signals
    2930
    3031        # Set up middleware if needed. We couldn't do this earlier, because
    3132        # settings weren't available.
    3233        if self._request_middleware is None:
    3334            self.load_middleware()
    3435
    35         dispatcher.send(signal=signals.request_started)
    36         try:
    37             request = WSGIRequest(environ)
    38             response = self.get_response(request)
     36        return self.handle_request(environ)
    3937
    40             # Apply response middleware
    41             for middleware_method in self._response_middleware:
    42                 response = middleware_method(request, response)
    43             response = self.apply_response_fixes(request, response)
    44         finally:
    45             dispatcher.send(signal=signals.request_finished)
    46 
    47         return response
    48 
    4938def store_rendered_templates(store, signal, sender, template, context):
    5039    "A utility function for storing templates and contexts that are rendered"
    5140    store.setdefault('template',[]).append(template)
     
    9483    ])
    9584    return '\r\n'.join(lines)
    9685
    97 class Client:
     86class Client(object):
    9887    """
    9988    A class that can act as a client for testing purposes.
    10089
     
    117106        self.defaults = defaults
    118107        self.cookies = SimpleCookie()
    119108        self.exc_info = None
     109        self.reraise_exceptions = True
    120110
    121111    def store_exc_info(self, *args, **kwargs):
    122112        """
     
    178168                raise
    179169
    180170        # Look for a signalled exception and reraise it
    181         if self.exc_info:
     171        if self.exc_info and self.reraise_exceptions:
    182172            raise self.exc_info[1], None, self.exc_info[2]
    183173
    184174        # Save the client and request that stimulated the response
  • 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)
     196        response = self.handle_request(environ)
    206197
    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)
    213 
    214198        try:
    215199            status_text = STATUS_CODE_TEXT[response.status_code]
    216200        except KeyError:
  • django/core/handlers/base.py

     
    33from django import http
    44from django.core import signals
    55from django.dispatch import dispatcher
     6from django.core.urlresolvers import RegexURLResolver
    67
     8
     9def resolver(request):
     10    """
     11    Returns a RegexURLResolver for the request's urlconf.
     12
     13    If the request does not have a urlconf object, then the default of
     14    settings.ROOT_URLCONF is used.
     15    """
     16    from django.conf import settings
     17    urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
     18    return RegexURLResolver(r'^/', urlconf)
     19
     20
    721class BaseHandler(object):
    822    # Changes that are always applied to a response (in this order).
    923    response_fixes = [http.fix_location_header,
     
    2842            try:
    2943                dot = middleware_path.rindex('.')
    3044            except ValueError:
    31                 raise exceptions.ImproperlyConfigured, '%s isn\'t a middleware module' % middleware_path
     45                raise exceptions.ImproperlyConfigured('%s isn\'t a middleware module' % middleware_path)
    3246            mw_module, mw_classname = middleware_path[:dot], middleware_path[dot+1:]
    3347            try:
    3448                mod = __import__(mw_module, {}, {}, [''])
    3549            except ImportError, e:
    36                 raise exceptions.ImproperlyConfigured, 'Error importing middleware %s: "%s"' % (mw_module, e)
     50                raise exceptions.ImproperlyConfigured('Error importing middleware %s: "%s"' % (mw_module, e))
    3751            try:
    3852                mw_class = getattr(mod, mw_classname)
    3953            except AttributeError:
    40                 raise exceptions.ImproperlyConfigured, 'Middleware module "%s" does not define a "%s" class' % (mw_module, mw_classname)
     54                raise exceptions.ImproperlyConfigured('Middleware module "%s" does not define a "%s" class' % (mw_module, mw_classname))
    4155
    4256            try:
    4357                mw_instance = mw_class()
     
    5367            if hasattr(mw_instance, 'process_exception'):
    5468                self._exception_middleware.insert(0, mw_instance.process_exception)
    5569
    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
    60         from django.conf import settings
     70    def handle_request(self, request):
     71        """
     72        Takes a request and returns a response.
    6173
    62         # Apply request middleware
     74        Processing occurs according to the following diagram:
     75
     76            Request                    Response
     77               |                           |
     78          +----------+                +----------+
     79          | Request  |                | Response |<--------+
     80          |Middleware|---Response?--->|Middleware|         |
     81          +----------+                +----------+   404/500 Response
     82               |                           |               |
     83        ---------Request Exception Handler-----------------+---------
     84               |                           |
     85          +---------+                      +<-------+
     86          | URLConf |                      |        |
     87          +---------+                      |   +----------+
     88               |                           |   |Exception |
     89          +----------+                     |   |Middleware|
     90          |   View   |                     |   +----------+
     91          |Middleware|-----Response?------>+        |
     92          +----------+                     |    Exception
     93               |                           |        |
     94        ----------View Exception Handler------------+----------------
     95               |                           |
     96            +------+                       |
     97            | View |-----Response--------->+
     98            +------+
     99        """
     100        dispatcher.send(signal=signals.request_started)
     101        try:
     102            try:
     103                request = self.request_class(request)
     104            except UnicodeDecodeError:
     105                response = http.HttpResponseBadRequest()
     106            else:
     107                # See if the request middleware returns a response.
     108                response = self.get_response(self.apply_request_middleware,
     109                                             request)
     110                # If not, then call the view.
     111                if not response:
     112                    response = self.get_response(self.run_view_processing,
     113                                                 request)
     114            # All responses go through the response middleware.
     115            response = self.get_response(self.apply_response_middleware,
     116                                         request, response)
     117            response = self.apply_response_fixes(request, response)
     118            return response
     119        finally:
     120            dispatcher.send(signal=signals.request_finished)
     121
     122    def apply_request_middleware(self, request):
     123        """
     124        Applies request middleware one at a time until a response is returned.
     125        If none of request middleware returns a response, then return None.
     126        """
    63127        for middleware_method in self._request_middleware:
    64128            response = middleware_method(request)
    65129            if response:
    66130                return response
    67131
    68         # Get urlconf from request object, if available.  Otherwise use default.
    69         urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
     132    def run_view_processing(self, request):
     133        """
     134        Apply view middleware and call view.
     135        """
     136        callback, callback_args, callback_kwargs = resolver(request).resolve(request.path)
    70137
    71         resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
     138        # Apply view middleware
     139        for middleware_method in self._view_middleware:
     140            response = middleware_method(request, callback, callback_args, callback_kwargs)
     141            if response:
     142                return response
     143
    72144        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)
     145            response = callback(request, *callback_args, **callback_kwargs)
     146        except Exception, e:
     147            # If the view raised an exception, run it through exception
     148            # middleware, and if the exception middleware returns a
     149            # response, use that. Otherwise, reraise the exception.
     150            for middleware_method in self._exception_middleware:
     151                response = middleware_method(request, e)
    78152                if response:
    79153                    return response
     154            raise
    80155
     156        # Complain if the view returned None (a common error).
     157        if response is None:
    81158            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
     159                view_name = callback.func_name # If it's a function
     160            except AttributeError:
     161                view_name = callback.__class__.__name__ + '.__call__' # If it's a class
     162            raise ValueError("The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name))
    92163
    93             # Complain if the view returned None (a common error).
     164        return response
     165
     166    def apply_response_middleware(self, request, response):
     167        """
     168        Applies all response middleware to the response.
     169        """
     170        for middleware_method in self._response_middleware:
     171            response = middleware_method(request, response)
     172        return response
     173
     174    def get_response(self, method, request, response=None):
     175        """
     176        Returns an HttpResponse by calling the passed method with the given
     177        HttpRequest object.  If an HttpResponse is passed, it will also be
     178        passed in the call to method.  Exceptions raised by calling method
     179        will be handled like so:
     180
     181        * Http404 exception - If settings.DEBUG is True, the
     182          technical_404_response debug view will be called.  If settings.DEBUG
     183          is False, the URL resolver's handler404 view will be called.
     184        * PermissionDenied exception - return HttpResponseForbidden.
     185        * SystemExit exception - ignored.
     186        * All other exceptions - If settings.DEBUG is True, the
     187          technical_500_response debug view will be called.  If settings.DEBUG
     188          is False, the URL resolver's handler500 view will be called.
     189        """
     190        from django.core import exceptions
     191        from django.core.mail import mail_admins
     192        from django.conf import settings
     193
     194        try:
    94195            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
     196                return method(request)
     197            else:
     198                return method(request, response)
    102199        except http.Http404, e:
    103200            if settings.DEBUG:
    104201                from django.views import debug
    105202                return debug.technical_404_response(request, e)
    106203            else:
    107                 callback, param_dict = resolver.resolve404()
     204                callback, param_dict = resolver(request).resolve404()
    108205                return callback(request, **param_dict)
    109206        except exceptions.PermissionDenied:
    110207            return http.HttpResponseForbidden('<h1>Permission denied</h1>')
     
    128225                message = "%s\n\n%s" % (self._get_traceback(exc_info), request_repr)
    129226                mail_admins(subject, message, fail_silently=True)
    130227                # Return an HttpResponse that displays a friendly error message.
    131                 callback, param_dict = resolver.resolve500()
     228                callback, param_dict = resolver(request).resolve500()
    132229                return callback(request, **param_dict)
    133230
    134231    def _get_traceback(self, exc_info=None):
     
    145242        for func in self.response_fixes:
    146243            response = func(request, response)
    147244        return response
    148 
  • 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, smart_str
    108
     
    152150        if self._request_middleware is None:
    153151            self.load_middleware()
    154152
    155         dispatcher.send(signal=signals.request_started)
    156         try:
    157             try:
    158                 request = self.request_class(req)
    159             except UnicodeDecodeError:
    160                 response = http.HttpResponseBadRequest()
    161             else:
    162                 response = self.get_response(request)
     153        response = self.handle_request(req)
    163154
    164                 # Apply response middleware
    165                 for middleware_method in self._response_middleware:
    166                     response = middleware_method(request, response)
    167                 response = self.apply_response_fixes(request, response)
    168         finally:
    169             dispatcher.send(signal=signals.request_finished)
    170 
    171155        # Convert our custom HttpResponse object back into the mod_python req.
    172156        req.content_type = response['Content-Type']
    173157        for key, value in response.items():
  • 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")
  • 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/models.py

     
     1# models file for tests to run.
  • 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/urls.py

     
    1818    # test urlconf for middleware tests
    1919    (r'^middleware/', include('regressiontests.middleware.urls')),
    2020
     21    # urlconf for handler_exceptions tests.
     22    (r'^handler_exceptions/', include('regressiontests.handler_exceptions.urls')),
     23
    2124    (r'^utils/', include('regressiontests.utils.urls')),
    2225)
Back to Top