Django

Code

root/django/branches/gis/django/http/__init__.py

Revision 8215, 14.5 kB (checked in by jbronn, 4 months ago)

gis: Merged revisions 7981-8001,8003-8011,8013-8033,8035-8036,8038-8039,8041-8063,8065-8076,8078-8139,8141-8154,8156-8214 via svnmerge from trunk.

  • Property svn:eol-style set to native
  • Property svn:keywords set to LastChangedRevision
Line 
1 import os
2 from Cookie import SimpleCookie, CookieError
3 from pprint import pformat
4 from urllib import urlencode
5 from urlparse import urljoin
6 try:
7     # The mod_python version is more efficient, so try importing it first.
8     from mod_python.util import parse_qsl
9 except ImportError:
10     from cgi import parse_qsl
11
12 from django.utils.datastructures import MultiValueDict, ImmutableList
13 from django.utils.encoding import smart_str, iri_to_uri, force_unicode
14 from django.http.multipartparser import MultiPartParser
15 from django.conf import settings
16 from django.core.files import uploadhandler
17 from utils import *
18
19 RESERVED_CHARS="!*'();:@&=+$,/?%#[]"
20
21 class Http404(Exception):
22     pass
23
24 class HttpRequest(object):
25     """A basic HTTP request."""
26
27     # The encoding used in GET/POST dicts. None means use default setting.
28     _encoding = None
29     _upload_handlers = []
30
31     def __init__(self):
32         self.GET, self.POST, self.COOKIES, self.META, self.FILES = {}, {}, {}, {}, {}
33         self.path = ''
34         self.path_info = ''
35         self.method = None
36
37     def __repr__(self):
38         return '<HttpRequest\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % \
39             (pformat(self.GET), pformat(self.POST), pformat(self.COOKIES),
40             pformat(self.META))
41
42     def get_host(self):
43         """Returns the HTTP host using the environment or request headers."""
44         # We try three options, in order of decreasing preference.
45         if 'HTTP_X_FORWARDED_HOST' in self.META:
46             host = self.META['HTTP_X_FORWARDED_HOST']
47         elif 'HTTP_HOST' in self.META:
48             host = self.META['HTTP_HOST']
49         else:
50             # Reconstruct the host using the algorithm from PEP 333.
51             host = self.META['SERVER_NAME']
52             server_port = self.META['SERVER_PORT']
53             if server_port != (self.is_secure() and 443 or 80):
54                 host = '%s:%s' % (host, server_port)
55         return host
56
57     def get_full_path(self):
58         return ''
59
60     def build_absolute_uri(self, location=None):
61         """
62         Builds an absolute URI from the location and the variables available in
63         this request. If no location is specified, the absolute URI is built on
64         ``request.get_full_path()``.
65         """
66         if not location:
67             location = self.get_full_path()
68         if not ':' in location:
69             current_uri = '%s://%s%s' % (self.is_secure() and 'https' or 'http',
70                                          self.get_host(), self.path)
71             location = urljoin(current_uri, location)
72         return location
73
74     def is_secure(self):
75         return os.environ.get("HTTPS") == "on"
76
77     def is_ajax(self):
78         return self.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
79
80     def _set_encoding(self, val):
81         """
82         Sets the encoding used for GET/POST accesses. If the GET or POST
83         dictionary has already been created, it is removed and recreated on the
84         next access (so that it is decoded correctly).
85         """
86         self._encoding = val
87         if hasattr(self, '_get'):
88             del self._get
89         if hasattr(self, '_post'):
90             del self._post
91
92     def _get_encoding(self):
93         return self._encoding
94
95     encoding = property(_get_encoding, _set_encoding)
96
97     def _initialize_handlers(self):
98         self._upload_handlers = [uploadhandler.load_handler(handler, self)
99                                  for handler in settings.FILE_UPLOAD_HANDLERS]
100
101     def _set_upload_handlers(self, upload_handlers):
102         if hasattr(self, '_files'):
103             raise AttributeError("You cannot set the upload handlers after the upload has been processed.")
104         self._upload_handlers = upload_handlers
105
106     def _get_upload_handlers(self):
107         if not self._upload_handlers:
108             # If thre are no upload handlers defined, initialize them from settings.
109             self._initialize_handlers()
110         return self._upload_handlers
111
112     upload_handlers = property(_get_upload_handlers, _set_upload_handlers)
113
114     def parse_file_upload(self, META, post_data):
115         """Returns a tuple of (POST QueryDict, FILES MultiValueDict)."""
116         self.upload_handlers = ImmutableList(
117             self.upload_handlers,
118             warning = "You cannot alter upload handlers after the upload has been processed."
119         )
120         parser = MultiPartParser(META, post_data, self.upload_handlers, self.encoding)
121         return parser.parse()
122
123 class QueryDict(MultiValueDict):
124     """
125     A specialized MultiValueDict that takes a query string when initialized.
126     This is immutable unless you create a copy of it.
127
128     Values retrieved from this class are converted from the given encoding
129     (DEFAULT_CHARSET by default) to unicode.
130     """
131     def __init__(self, query_string, mutable=False, encoding=None):
132         MultiValueDict.__init__(self)
133         if not encoding:
134             # *Important*: do not import settings any earlier because of note
135             # in core.handlers.modpython.
136             from django.conf import settings
137             encoding = settings.DEFAULT_CHARSET
138         self.encoding = encoding
139         self._mutable = True
140         for key, value in parse_qsl((query_string or ''), True): # keep_blank_values=True
141             self.appendlist(force_unicode(key, encoding, errors='replace'),
142                             force_unicode(value, encoding, errors='replace'))
143         self._mutable = mutable
144
145     def _assert_mutable(self):
146         if not self._mutable:
147             raise AttributeError("This QueryDict instance is immutable")
148
149     def __setitem__(self, key, value):
150         self._assert_mutable()
151         key = str_to_unicode(key, self.encoding)
152         value = str_to_unicode(value, self.encoding)
153         MultiValueDict.__setitem__(self, key, value)
154
155     def __delitem__(self, key):
156         self._assert_mutable()
157         super(QueryDict, self).__delitem__(key)
158
159     def __copy__(self):
160         result = self.__class__('', mutable=True)
161         for key, value in dict.items(self):
162             dict.__setitem__(result, key, value)
163         return result
164
165     def __deepcopy__(self, memo):
166         import copy
167         result = self.__class__('', mutable=True)
168         memo[id(self)] = result
169         for key, value in dict.items(self):
170             dict.__setitem__(result, copy.deepcopy(key, memo), copy.deepcopy(value, memo))
171         return result
172
173     def setlist(self, key, list_):
174         self._assert_mutable()
175         key = str_to_unicode(key, self.encoding)
176         list_ = [str_to_unicode(elt, self.encoding) for elt in list_]
177         MultiValueDict.setlist(self, key, list_)
178
179     def setlistdefault(self, key, default_list=()):
180         self._assert_mutable()
181         if key not in self:
182             self.setlist(key, default_list)
183         return MultiValueDict.getlist(self, key)
184
185     def appendlist(self, key, value):
186         self._assert_mutable()
187         key = str_to_unicode(key, self.encoding)
188         value = str_to_unicode(value, self.encoding)
189         MultiValueDict.appendlist(self, key, value)
190
191     def update(self, other_dict):
192         self._assert_mutable()
193         f = lambda s: str_to_unicode(s, self.encoding)
194         d = dict([(f(k), f(v)) for k, v in other_dict.items()])
195         MultiValueDict.update(self, d)
196
197     def pop(self, key, *args):
198         self._assert_mutable()
199         return MultiValueDict.pop(self, key, *args)
200
201     def popitem(self):
202         self._assert_mutable()
203         return MultiValueDict.popitem(self)
204
205     def clear(self):
206         self._assert_mutable()
207         MultiValueDict.clear(self)
208
209     def setdefault(self, key, default=None):
210         self._assert_mutable()
211         key = str_to_unicode(key, self.encoding)
212         default = str_to_unicode(default, self.encoding)
213         return MultiValueDict.setdefault(self, key, default)
214
215     def copy(self):
216         """Returns a mutable copy of this object."""
217         return self.__deepcopy__({})
218
219     def urlencode(self):
220         output = []
221         for k, list_ in self.lists():
222             k = smart_str(k, self.encoding)
223             output.extend([urlencode({k: smart_str(v, self.encoding)}) for v in list_])
224         return '&'.join(output)
225
226 def parse_cookie(cookie):
227     if cookie == '':
228         return {}
229     try:
230         c = SimpleCookie()
231         c.load(cookie)
232     except CookieError:
233         # Invalid cookie
234         return {}
235
236     cookiedict = {}
237     for key in c.keys():
238         cookiedict[key] = c.get(key).value
239     return cookiedict
240
241 class HttpResponse(object):
242     """A basic HTTP response, with content and dictionary-accessed headers."""
243
244     status_code = 200
245
246     def __init__(self, content='', mimetype=None, status=None,
247             content_type=None):
248         from django.conf import settings
249         self._charset = settings.DEFAULT_CHARSET
250         if mimetype:
251             content_type = mimetype     # For backwards compatibility
252         if not content_type:
253             content_type = "%s; charset=%s" % (settings.DEFAULT_CONTENT_TYPE,
254                     settings.DEFAULT_CHARSET)
255         if not isinstance(content, basestring) and hasattr(content, '__iter__'):
256             self._container = content
257             self._is_string = False
258         else:
259             self._container = [content]
260             self._is_string = True
261         self.cookies = SimpleCookie()
262         if status:
263             self.status_code = status
264
265         # _headers is a mapping of the lower-case name to the original case of
266         # the header (required for working with legacy systems) and the header
267         # value.
268         self._headers = {'content-type': ('Content-Type', content_type)}
269
270     def __str__(self):
271         """Full HTTP message, including headers."""
272         return '\n'.join(['%s: %s' % (key, value)
273             for key, value in self._headers.values()]) \
274             + '\n\n' + self.content
275
276     def _convert_to_ascii(self, *values):
277         """Converts all values to ascii strings."""
278         for value in values:
279             if isinstance(value, unicode):
280                 try:
281                     yield value.encode('us-ascii')
282                 except UnicodeError, e:
283                     e.reason += ', HTTP response headers must be in US-ASCII format'
284                     raise
285             else:
286                 yield str(value)
287
288     def __setitem__(self, header, value):
289         header, value = self._convert_to_ascii(header, value)
290         self._headers[header.lower()] = (header, value)
291
292     def __delitem__(self, header):
293         try:
294             del self._headers[header.lower()]
295         except KeyError:
296             pass
297
298     def __getitem__(self, header):
299         return self._headers[header.lower()][1]
300
301     def has_header(self, header):
302         """Case-insensitive check for a header."""
303         return self._headers.has_key(header.lower())
304
305     __contains__ = has_header
306
307     def items(self):
308         return self._headers.values()
309
310     def get(self, header, alternate):
311         return self._headers.get(header.lower(), (None, alternate))[1]
312
313     def set_cookie(self, key, value='', max_age=None, expires=None, path='/',
314                    domain=None, secure=False):
315         self.cookies[key] = value
316         if max_age is not None:
317             self.cookies[key]['max-age'] = max_age
318         if expires is not None:
319             self.cookies[key]['expires'] = expires
320         if path is not None:
321             self.cookies[key]['path'] = path
322         if domain is not None:
323             self.cookies[key]['domain'] = domain
324         if secure:
325             self.cookies[key]['secure'] = True
326
327     def delete_cookie(self, key, path='/', domain=None):
328         self.set_cookie(key, max_age=0, path=path, domain=domain,
329                         expires='Thu, 01-Jan-1970 00:00:00 GMT')
330
331     def _get_content(self):
332         if self.has_header('Content-Encoding'):
333             return ''.join(self._container)
334         return smart_str(''.join(self._container), self._charset)
335
336     def _set_content(self, value):
337         self._container = [value]
338         self._is_string = True
339
340     content = property(_get_content, _set_content)
341
342     def __iter__(self):
343         self._iterator = iter(self._container)
344         return self
345
346     def next(self):
347         chunk = self._iterator.next()
348         if isinstance(chunk, unicode):
349             chunk = chunk.encode(self._charset)
350         return str(chunk)
351
352     def close(self):
353         if hasattr(self._container, 'close'):
354             self._container.close()
355
356     # The remaining methods partially implement the file-like object interface.
357     # See http://docs.python.org/lib/bltin-file-objects.html
358     def write(self, content):
359         if not self._is_string:
360             raise Exception("This %s instance is not writable" % self.__class__)
361         self._container.append(content)
362
363     def flush(self):
364         pass
365
366     def tell(self):
367         if not self._is_string:
368             raise Exception("This %s instance cannot tell its position" % self.__class__)
369         return sum([len(chunk) for chunk in self._container])
370
371 class HttpResponseRedirect(HttpResponse):
372     status_code = 302
373
374     def __init__(self, redirect_to):
375         HttpResponse.__init__(self)
376         self['Location'] = iri_to_uri(redirect_to)
377
378 class HttpResponsePermanentRedirect(HttpResponse):
379     status_code = 301
380
381     def __init__(self, redirect_to):
382         HttpResponse.__init__(self)
383         self['Location'] = iri_to_uri(redirect_to)
384
385 class HttpResponseNotModified(HttpResponse):
386     status_code = 304
387
388 class HttpResponseBadRequest(HttpResponse):
389     status_code = 400
390
391 class HttpResponseNotFound(HttpResponse):
392     status_code = 404
393
394 class HttpResponseForbidden(HttpResponse):
395     status_code = 403
396
397 class HttpResponseNotAllowed(HttpResponse):
398     status_code = 405
399
400     def __init__(self, permitted_methods):
401         HttpResponse.__init__(self)
402         self['Allow'] = ', '.join(permitted_methods)
403
404 class HttpResponseGone(HttpResponse):
405     status_code = 410
406
407     def __init__(self, *args, **kwargs):
408         HttpResponse.__init__(self, *args, **kwargs)
409
410 class HttpResponseServerError(HttpResponse):
411     status_code = 500
412
413     def __init__(self, *args, **kwargs):
414         HttpResponse.__init__(self, *args, **kwargs)
415
416 # A backwards compatible alias for HttpRequest.get_host.
417 def get_host(request):
418     return request.get_host()
419
420 # It's neither necessary nor appropriate to use
421 # django.utils.encoding.smart_unicode for parsing URLs and form inputs. Thus,
422 # this slightly more restricted function.
423 def str_to_unicode(s, encoding):
424     """
425     Converts basestring objects to unicode, using the given encoding. Illegally
426     encoded input characters are replaced with Unicode "unknown" codepoint
427     (\ufffd).
428
429     Returns any non-basestring objects without change.
430     """
431     if isinstance(s, str):
432         return unicode(s, encoding, 'replace')
433     else:
434         return s
Note: See TracBrowser for help on using the browser.