Django

Code

root/django/branches/newforms-admin/django/http/__init__.py

Revision 7815, 14.8 kB (checked in by brosner, 5 months ago)

newforms-admin: Merged from trunk up to [7814].

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