1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 | from functools import wraps
from django.core.handlers.wsgi import WSGIRequest
from django.core.signals import got_request_exception
from django.template import TemplateDoesNotExist
from django.utils.functional import curry
from django.test import signals
from django.test.client import Client, store_rendered_templates
from django.core.handlers.base import BaseHandler
def new_init(f):
"""
We need to add a handler for returning the request instead of the response
and initialize a variable that will tell us whether a particular
get/post/put/delete is supposed to return the HttpResponse or the
WSGIRequest
"""
@wraps(f)
def wrapper(self, **defaults):
# It's an unbound function at this point, so we hook it with what we
# get off the wrapper
f(self, **defaults)
self._return_request_instead = False
self.handler_request = ClientHandler()
return wrapper
def switch_client_request_instead(self):
"""
Allows us to throw an internal switch in the Client to return the next
request as WSGIRequest instead of the HttpResponse
"""
self._return_request_instead = True
return self
def new_request(f):
"""
Handles actually creating a WSGIRequest instead of an HttpResponse if we
are switched into that mode. Will call the original request instead if
needed.
"""
def wrapper(self, **request):
if not self._return_request_instead:
# We are not supposed to return the request object itself, but do
# the normal Django test client request
return self.original_request(**request)
# So we are switched into a mode where we need to return the request
# instead of the response, we need to set the test client back to
# response-returning normality so the next call to this client doesn't
# continue to return WSGIRequest objects
self._return_request_instead = False
# Rip-off of the Django test client, but changed to return a fake WSGIRequest object
environ = {
'HTTP_COOKIE': self.cookies.output(header='', sep='; '),
'PATH_INFO': '/',
'QUERY_STRING': '',
'REMOTE_ADDR': '127.0.0.1',
'REQUEST_METHOD': 'GET',
'SCRIPT_NAME': '',
'SERVER_NAME': 'testserver',
'SERVER_PORT': '80',
'SERVER_PROTOCOL': 'HTTP/1.1',
'wsgi.version': (1,0),
'wsgi.url_scheme': 'http',
'wsgi.errors': self.errors,
'wsgi.multiprocess': True,
'wsgi.multithread': False,
'wsgi.run_once': False,
}
environ.update(self.defaults)
environ.update(request)
# Curry a data dictionary into an instance of the template renderer
# callback function.
data = {}
on_template_render = curry(store_rendered_templates, data)
signals.template_rendered.connect(on_template_render)
# Capture exceptions created by the handler.
got_request_exception.connect(self.store_exc_info)
try:
request = self.handler_request(environ)
except TemplateDoesNotExist, e:
# If the view raises an exception, Django will attempt to show
# the 500.html template. If that template is not available,
# we should ignore the error in favor of re-raising the
# underlying exception that caused the 500 error. Any other
# template found to be missing during view error handling
# should be reported as-is.
if e.args != ('500.html',):
raise
return request
return wrapper
class ClientHandler(BaseHandler):
"""
A HTTP Handler that can be used for testing purposes.
Uses the WSGI interface to compose requests, but returns
the raw HttpResponse object
"""
def __call__(self, environ):
from django.conf import settings
from django.core import signals
# Set up middleware if needed. We couldn't do this earlier, because
# settings weren't available.
if self._request_middleware is None:
self.load_middleware()
signals.request_started.send(sender=self.__class__)
try:
request = WSGIRequest(environ)
response = self.get_response(request)
# Apply response middleware.
for middleware_method in self._response_middleware:
response = middleware_method(request, response)
response = self.apply_response_fixes(request, response)
finally:
signals.request_finished.disconnect(close_connection)
signals.request_finished.send(sender=self.__class__)
signals.request_finished.connect(close_connection)
return request
# Time to do some monkey-patching.
#
# This allows us to do this with the client object
#
# >>> client.get('/')
# <HttpResponse >
# >>> client.request_from.get('/')
# <WSGIRequest >
#
setattr(Client, 'request_from', property(switch_client_request_instead))
setattr(Client, '__init__', new_init(Client.__init__))
setattr(Client, 'original_request', Client.request)
setattr(Client, 'request', new_request(Client.request))
|
Comments