thirdparty/google_appengine/lib/webob/docs/reference.txt
changeset 109 620f9b141567
equal deleted inserted replaced
108:261778de26ff 109:620f9b141567
       
     1 WebOb Reference
       
     2 +++++++++++++++
       
     3 
       
     4 .. contents::
       
     5 
       
     6 .. comment:
       
     7 
       
     8     >>> from dtopt import ELLIPSIS
       
     9 
       
    10 Introduction
       
    11 ============
       
    12 
       
    13 This document covers all the details of the Request and Response
       
    14 objects.  It is written to be testable with `doctest
       
    15 <http://python.org/doc/current/lib/module-doctest.html>`_ -- this
       
    16 effects the flavor of the documentation, perhaps to its detriment.
       
    17 But it also means you can feel confident that the documentation is
       
    18 correct.
       
    19 
       
    20 This is a somewhat different approach to reference documentation
       
    21 compared to the extracted documentation for the `request
       
    22 <class-webob.Request.html>`_ and `response
       
    23 <class-webob.Response.html>`_.
       
    24 
       
    25 Request
       
    26 =======
       
    27 
       
    28 The primary object in WebOb is ``webob.Request``, a wrapper around a
       
    29 `WSGI environment <http://www.python.org/dev/peps/pep-0333/>`_.
       
    30 
       
    31 The basic way you create a request object is simple enough:
       
    32 
       
    33 .. code-block::
       
    34 
       
    35    >>> from webob import Request
       
    36    >>> environ = {}
       
    37    >>> req = Request(environ)
       
    38 
       
    39 The request object *wraps* the environment; it has very little
       
    40 internal state of its own.  Instead attributes you access read and
       
    41 write to the environment dictionary.
       
    42 
       
    43 You don't have to understand the details of WSGI to use this library;
       
    44 this library handles those details for you.  You also don't have to
       
    45 use this exclusively of other libraries.  If those other libraries
       
    46 also keep their state in the environment, multiple wrappers can
       
    47 coexist.  Examples of libraries that can coexist include
       
    48 `paste.wsgiwrappers.Request
       
    49 <http://pythonpaste.org/class-paste.wsgiwrappers.WSGIRequest.html>`_
       
    50 (used by Pylons) and `yaro.Request
       
    51 <http://lukearno.com/projects/yaro/>`_.
       
    52 
       
    53 The WSGI environment has a number of required variables.  To make it
       
    54 easier to test and play around with, the ``Request`` class has a
       
    55 constructor that will fill in a minimal environment:
       
    56 
       
    57 .. code-block::
       
    58 
       
    59    >>> req = Request.blank('/article?id=1')
       
    60    >>> from pprint import pprint
       
    61    >>> pprint(req.environ)
       
    62    {'HTTP_HOST': 'localhost:80',
       
    63     'PATH_INFO': '/article',
       
    64     'QUERY_STRING': 'id=1',
       
    65     'REQUEST_METHOD': 'GET',
       
    66     'SCRIPT_NAME': '',
       
    67     'SERVER_NAME': 'localhost',
       
    68     'SERVER_PORT': '80',
       
    69     'SERVER_PROTOCOL': 'HTTP/1.0',
       
    70     'wsgi.errors': <open file '<stderr>', mode 'w' at ...>,
       
    71     'wsgi.input': <cStringIO.StringI object at ...>,
       
    72     'wsgi.multiprocess': False,
       
    73     'wsgi.multithread': False,
       
    74     'wsgi.run_once': False,
       
    75     'wsgi.url_scheme': 'http',
       
    76     'wsgi.version': (1, 0)}
       
    77 
       
    78 Request Body
       
    79 ------------
       
    80 
       
    81 ``req.body`` is a file-like object that gives the body of the request
       
    82 (e.g., a POST form, the body of a PUT, etc).  It's kind of boring to
       
    83 start, but you can set it to a string and that will be turned into a
       
    84 file-like object.  You can read the entire body with
       
    85 ``req.body``.
       
    86 
       
    87 .. code-block:: 
       
    88 
       
    89     >>> req.body_file
       
    90     <cStringIO.StringI object at ...>
       
    91     >>> req.body
       
    92     ''
       
    93     >>> req.body = 'test'
       
    94     >>> req.body_file
       
    95     <cStringIO.StringI object at ...>
       
    96     >>> req.body
       
    97     'test'
       
    98 
       
    99 Method & URL
       
   100 ------------
       
   101 
       
   102 All the normal parts of a request are also accessible through the
       
   103 request object:
       
   104 
       
   105 .. code-block::
       
   106 
       
   107     >>> req.method
       
   108     'GET'
       
   109     >>> req.scheme
       
   110     'http'
       
   111     >>> req.script_name  # The base of the URL
       
   112     ''
       
   113     >>> req.script_name = '/blog' # make it more interesting
       
   114     >>> req.path_info    # The yet-to-be-consumed part of the URL
       
   115     '/article'
       
   116     >>> req.content_type # Content-Type of the request body
       
   117     ''
       
   118     >>> print req.remote_user  # The authenticated user (there is none set)
       
   119     None
       
   120     >>> print req.remote_addr  # The remote IP
       
   121     None
       
   122     >>> req.host
       
   123     'localhost:80'
       
   124     >>> req.host_url
       
   125     'http://localhost'
       
   126     >>> req.application_url
       
   127     'http://localhost/blog'
       
   128     >>> req.path_url
       
   129     'http://localhost/blog/article'
       
   130     >>> req.url
       
   131     'http://localhost/blog/article?id=1'
       
   132     >>> req.path
       
   133     '/blog/article'
       
   134     >>> req.path_qs
       
   135     '/blog/article?id=1'
       
   136     >>> req.query_string
       
   137     'id=1'
       
   138 
       
   139 You can make new URLs:
       
   140 
       
   141 .. code-block::
       
   142 
       
   143     >>> req.relative_url('archive')
       
   144     'http://localhost/blog/archive'
       
   145 
       
   146 For parsing the URLs, it is often useful to deal with just the next
       
   147 path segment on PATH_INFO:
       
   148 
       
   149 .. code-block::
       
   150 
       
   151     >>> req.path_info_peek() # Doesn't change request
       
   152     'article'
       
   153     >>> req.path_info_pop()  # Does change request!
       
   154     'article'
       
   155     >>> req.script_name
       
   156     '/blog/article'
       
   157     >>> req.path_info
       
   158     ''
       
   159 
       
   160 Headers
       
   161 -------
       
   162 
       
   163 All request headers are available through a dictionary-like object
       
   164 ``req.headers``.  Keys are case-insensitive.
       
   165 
       
   166 .. code-block::
       
   167 
       
   168     >>> req.headers['content-type'] = 'application/x-www-urlencoded'
       
   169     >>> req.headers
       
   170     {'Content-Length': '4', 'Content-Type': 'application/x-www-urlencoded', 'Host': 'localhost:80'}
       
   171     >>> req.environ['CONTENT_TYPE']
       
   172     'application/x-www-urlencoded'
       
   173 
       
   174 Query & POST variables
       
   175 ----------------------
       
   176 
       
   177 Requests can have variables in one of two locations: the query string
       
   178 (``?id=1``), or in the body of the request (generally a POST form).
       
   179 Note that even POST requests can have a query string, so both kinds of
       
   180 variables can exist at the same time.  Also, a variable can show up
       
   181 more than once, as in ``?check=a&check=b``.
       
   182 
       
   183 For these variables WebOb uses a `MultiDict
       
   184 <class-webob.multidict.MultiDict.html>`_, which is basically a
       
   185 dictionary wrapper on a list of key/value pairs.  It looks like a
       
   186 single-valued dictionary, but you can access all the values of a key
       
   187 with ``.getall(key)`` (which always returns a list, possibly an empty
       
   188 list).  You also get all key/value pairs when using ``.items()`` and
       
   189 all values with ``.values()``.
       
   190 
       
   191 Some examples:
       
   192 
       
   193 .. code-block::
       
   194 
       
   195     >>> req = Request.blank('/test?check=a&check=b&name=Bob')
       
   196     >>> req.GET
       
   197     MultiDict([('check', 'a'), ('check', 'b'), ('name', 'Bob')])
       
   198     >>> req.GET['check']
       
   199     'b'
       
   200     >>> req.GET.getall('check')
       
   201     ['a', 'b']
       
   202     >>> req.GET.items()
       
   203     [('check', 'a'), ('check', 'b'), ('name', 'Bob')]
       
   204 
       
   205 We'll have to create a request body and change the method to get
       
   206 POST.  Until we do that, the variables are boring:
       
   207 
       
   208 .. code-block::
       
   209 
       
   210     >>> req.POST
       
   211     <NoVars: Not a POST request>
       
   212     >>> req.POST.items()  # NoVars can be read like a dict, but not written
       
   213     []
       
   214     >>> req.method = 'POST'
       
   215     >>> req.body = 'name=Joe&email=joe@example.com'
       
   216     >>> req.POST
       
   217     MultiDict([('name', 'Joe'), ('email', 'joe@example.com')])
       
   218     >>> req.POST['name']
       
   219     'Joe'
       
   220 
       
   221 Often you won't care where the variables come from.  (Even if you care
       
   222 about the method, the location of the variables might not be
       
   223 important.)  There is a dictionary called ``req.params`` that
       
   224 contains variables from both sources:
       
   225 
       
   226 .. code-block::
       
   227 
       
   228     >>> req.params
       
   229     NestedMultiDict([('check', 'a'), ('check', 'b'), ('name', 'Bob'), ('name', 'Joe'), ('email', 'joe@example.com')])
       
   230     >>> req.params['name']
       
   231     'Bob'
       
   232     >>> req.params.getall('name')
       
   233     ['Bob', 'Joe']
       
   234     >>> for name, value in req.params.items():
       
   235     ...     print '%s: %r' % (name, value)
       
   236     check: 'a'
       
   237     check: 'b'
       
   238     name: 'Bob'
       
   239     name: 'Joe'
       
   240     email: 'joe@example.com'
       
   241 
       
   242 Unicode Variables
       
   243 ~~~~~~~~~~~~~~~~~
       
   244 
       
   245 Submissions are non-unicode (``str``) strings, unless some character
       
   246 set is indicated.  A client can indicate the character set with
       
   247 ``Content-Type: application/x-www-form-urlencoded; charset=utf8``, but
       
   248 very few clients actually do this (sometimes XMLHttpRequest requests
       
   249 will do this, as JSON is always UTF8 even when a page is served with a
       
   250 different character set).  You can force a charset, which will effect
       
   251 all the variables:
       
   252 
       
   253 .. code-block::
       
   254 
       
   255     >>> req.charset = 'utf8'
       
   256     >>> req.GET
       
   257     UnicodeMultiDict([(u'check', u'a'), (u'check', u'b'), (u'name', u'Bob')])
       
   258 
       
   259 If you always want ``str`` values, you can use ``req.str_GET``
       
   260 and ``str_POST``.
       
   261 
       
   262 Cookies
       
   263 -------
       
   264 
       
   265 Cookies are presented in a simple dictionary.  Like other variables,
       
   266 they will be decoded into Unicode strings if you set the charset.
       
   267 
       
   268 .. code-block::
       
   269 
       
   270     >>> req.headers['Cookie'] = 'test=value'
       
   271     >>> req.cookies
       
   272     UnicodeMultiDict([(u'test', u'value')])
       
   273     >>> req.charset = None
       
   274     >>> req.cookies
       
   275     {'test': 'value'}
       
   276 
       
   277 Modifying the request
       
   278 ---------------------
       
   279 
       
   280 The headers are all modifiable, as are other environmental variables
       
   281 (like ``req.remote_user``, which maps to
       
   282 ``request.environ['REMOTE_USER']``).
       
   283 
       
   284 If you want to copy the request you can use ``req.copy()``; this
       
   285 copies the ``environ`` dictionary, and the request body from
       
   286 ``environ['wsgi.input']``.
       
   287 
       
   288 The method ``req.remove_conditional_headers(remove_encoding=True)``
       
   289 can be used to remove headers that might result in a ``304 Not
       
   290 Modified`` response.  If you are writing some intermediary it can be
       
   291 useful to avoid these headers.  Also if ``remove_encoding`` is true
       
   292 (the default) then any ``Accept-Encoding`` header will be removed,
       
   293 which can result in gzipped responses.
       
   294 
       
   295 Header Getters
       
   296 --------------
       
   297 
       
   298 In addition to ``req.headers``, there are attributes for most of the
       
   299 request headers defined by the HTTP 1.1 specification.  These
       
   300 attributes often return parsed forms of the headers.
       
   301 
       
   302 Accept-* headers
       
   303 ~~~~~~~~~~~~~~~~
       
   304 
       
   305 There are several request headers that tell the server what the client
       
   306 accepts.  These are ``accept`` (the Content-Type that is accepted),
       
   307 ``accept_charset`` (the charset accepted), ``accept_encoding``
       
   308 (the Content-Encoding, like gzip, that is accepted), and
       
   309 ``accept_language`` (generally the preferred language of the client).
       
   310 
       
   311 The objects returned support containment to test for acceptability.
       
   312 E.g.:
       
   313 
       
   314 .. code-block::
       
   315 
       
   316     >>> 'text/html' in req.accept
       
   317     True
       
   318 
       
   319 Because no header means anything is potentially acceptable, this is
       
   320 returning True.  We can set it to see more interesting behavior (the
       
   321 example means that ``text/html`` is okay, but
       
   322 ``application/xhtml+xml`` is preferred):
       
   323 
       
   324 .. code-block::
       
   325 
       
   326     >>> req.accept = 'text/html;q=0.5, application/xhtml+xml;q=1'
       
   327     >>> req.accept
       
   328     <MIMEAccept at ... Accept: text/html;q=0.5, application/xhtml+xml>
       
   329     >>> 'text/html' in req.accept
       
   330     True
       
   331 
       
   332 There's three methods for different strategies of finding a match.
       
   333 First, when you trust the server's preference over the client (a good
       
   334 idea for Accept):
       
   335 
       
   336 .. code-block::
       
   337 
       
   338     >>> req.accept.first_match(['text/html', 'application/xhtml+xml'])
       
   339     'text/html'
       
   340 
       
   341 Because ``text/html`` is at least *somewhat* acceptible, it is
       
   342 returned, even if the client says it prefers
       
   343 ``application/xhtml+xml``.  If we trust the client more:
       
   344 
       
   345 .. code-block::
       
   346 
       
   347     >>> req.accept.best_match(['text/html', 'application/xhtml+xml'])
       
   348     'application/xhtml+xml'
       
   349 
       
   350 If we just want to know everything the client prefers, in the order it
       
   351 is preferred:
       
   352 
       
   353 .. code-block::
       
   354 
       
   355     >>> req.accept.best_matches()
       
   356     ['application/xhtml+xml', 'text/html']
       
   357 
       
   358 For languages you'll often have a "fallback" language.  E.g., if there's
       
   359 nothing better then use ``en-US`` (and if ``en-US`` is okay, ignore
       
   360 any less preferrable languages):
       
   361 
       
   362 .. code-block::
       
   363 
       
   364     >>> req.accept_language = 'es, pt-BR'
       
   365     >>> req.accept_language.best_matches('en-US')
       
   366     ['es', 'pt-BR', 'en-US']
       
   367     >>> req.accept_language.best_matches('es')
       
   368     ['es']
       
   369 
       
   370 Conditional Requests
       
   371 ~~~~~~~~~~~~~~~~~~~~
       
   372 
       
   373 There a number of ways to make a conditional request.  A conditional
       
   374 request is made when the client has a document, but it is not sure if
       
   375 the document is up to date.  If it is not, it wants a new version.  If
       
   376 the document is up to date then it doesn't want to waste the
       
   377 bandwidth, and expects a ``304 Not Modified`` response.
       
   378 
       
   379 ETags are generally the best technique for these kinds of requests;
       
   380 this is an opaque string that indicates the identity of the object.
       
   381 For instance, it's common to use the mtime (last modified) of the file,
       
   382 plus the number of bytes, and maybe a hash of the filename (if there's
       
   383 a possibility that the same URL could point to two different
       
   384 server-side filenames based on other variables).  To test if a 304
       
   385 response is appropriate, you can use:
       
   386 
       
   387 .. code-block::
       
   388 
       
   389     >>> server_token = 'opaque-token'
       
   390     >>> server_token in req.if_none_match # You shouldn't return 304
       
   391     False
       
   392     >>> req.if_none_match = server_token
       
   393     >>> req.if_none_match
       
   394     <ETag opaque-token>
       
   395     >>> server_token in req.if_none_match # You *should* return 304
       
   396     True
       
   397 
       
   398 For date-based comparisons If-Modified-Since is used:
       
   399 
       
   400 .. code-block::
       
   401 
       
   402     >>> from webob import UTC
       
   403     >>> from datetime import datetime
       
   404     >>> req.if_modified_since = datetime(2006, 1, 1, 12, 0, tzinfo=UTC)
       
   405     >>> req.headers['If-Modified-Since']
       
   406     'Sun, 01 Jan 2006 12:00:00 GMT'
       
   407     >>> server_modified = datetime(2005, 1, 1, 12, 0, tzinfo=UTC)
       
   408     >>> req.if_modified_since and req.if_modified_since >= server_modified
       
   409     True
       
   410 
       
   411 For range requests there are two important headers, If-Range (which is
       
   412 form of conditional request) and Range (which requests a range).  If
       
   413 the If-Range header fails to match then the full response (not a
       
   414 range) should be returned:
       
   415 
       
   416 .. code-block::
       
   417 
       
   418     >>> req.if_range
       
   419     <Empty If-Range>
       
   420     >>> req.if_range.match(etag='some-etag', last_modified=datetime(2005, 1, 1, 12, 0))
       
   421     True
       
   422     >>> req.if_range = 'opaque-etag'
       
   423     >>> req.if_range.match(etag='other-etag')
       
   424     False
       
   425     >>> req.if_range.match(etag='opaque-etag')
       
   426     True
       
   427 
       
   428 You can also pass in a response object with:
       
   429 
       
   430 .. code-block::
       
   431 
       
   432     >>> from webob import Response
       
   433     >>> res = Response(etag='opaque-etag')
       
   434     >>> req.if_range.match_response(res)
       
   435     True
       
   436 
       
   437 To get the range information:
       
   438 
       
   439     >>> req.range = 'bytes=0-100'
       
   440     >>> req.range
       
   441     <Range ranges=(0, 99)>
       
   442     >>> cr = req.range.content_range(length=1000)
       
   443     >>> cr.start, cr.stop, cr.length
       
   444     (0, 99, 1000)
       
   445 
       
   446 Note that the range headers use *inclusive* ranges (the last byte
       
   447 indexed is included), where Python always uses a range where the last
       
   448 index is excluded from the range.  The ``.stop`` index is in the
       
   449 Python form.
       
   450 
       
   451 Another kind of conditional request is a request (typically PUT) that
       
   452 includes If-Match or If-Unmodified-Since.  In this case you are saying
       
   453 "here is an update to a resource, but don't apply it if someone else
       
   454 has done something since I last got the resource".  If-Match means "do
       
   455 this if the current ETag matches the ETag I'm giving".
       
   456 If-Unmodified-Since means "do this if the resource has remained
       
   457 unchanged".
       
   458 
       
   459 .. code-block::
       
   460 
       
   461     >>> server_token in req.if_match # No If-Match means everything is ok
       
   462     True
       
   463     >>> req.if_match = server_token
       
   464     >>> server_token in req.if_match # Still OK
       
   465     True
       
   466     >>> req.if_match = 'other-token'
       
   467     >>> # Not OK, should return 412 Precondition Failed:
       
   468     >>> server_token in req.if_match 
       
   469     False
       
   470 
       
   471 For more on this kind of conditional request, see `Detecting the Lost
       
   472 Update Problem Using Unreserved Checkout
       
   473 <http://www.w3.org/1999/04/Editing/>`_.
       
   474 
       
   475 Calling WSGI Applications
       
   476 -------------------------
       
   477 
       
   478 The request object can be used to make handy subrequests or test
       
   479 requests against WSGI applications.  If you want to make subrequests,
       
   480 you should copy the request (with ``req.copy()``) before sending it to
       
   481 multiple applications, since applications might modify the request
       
   482 when they are run.
       
   483 
       
   484 There's two forms of the subrequest.  The more primitive form is
       
   485 this:
       
   486 
       
   487 .. code-block::
       
   488 
       
   489     >>> req = Request.blank('/')
       
   490     >>> def wsgi_app(environ, start_response):
       
   491     ...     start_response('200 OK', [('Content-type', 'text/plain')])
       
   492     ...     return ['Hi!']
       
   493     >>> req.call_application(wsgi_app)
       
   494     ('200 OK', [('Content-type', 'text/plain')], ['Hi!'])
       
   495 
       
   496 Note it returns ``(status_string, header_list, app_iter)``.  If
       
   497 ``app_iter.close()`` exists, it is your responsibility to call it.
       
   498 
       
   499 A handier response can be had with:
       
   500 
       
   501 .. code-block::
       
   502 
       
   503     >>> res = req.get_response(wsgi_app)
       
   504     >>> res
       
   505     <Response ... 200 OK>
       
   506     >>> res.status
       
   507     '200 OK'
       
   508     >>> res.headers
       
   509     HeaderDict([('Content-type', 'text/plain')])
       
   510     >>> res.body
       
   511     'Hi!'
       
   512 
       
   513 You can learn more about this response object in the Response_ section.
       
   514 
       
   515 Thread-local Request Wrappers
       
   516 -----------------------------
       
   517 
       
   518 You can also give the ``Request`` object a function to get the
       
   519 environment.  This can be used to make a single global request object
       
   520 dynamic.  An example:
       
   521 
       
   522 .. code-block::
       
   523 
       
   524     >>> import threading
       
   525     >>> import webob
       
   526     >>> environments = threading.local()
       
   527     >>> def get_environ():
       
   528     ...     return environments.environ
       
   529     >>> def set_thread_environ(environ):
       
   530     ...     environments.environ = environ
       
   531     >>> request = webob.Request(environ_getter=get_environ)
       
   532     >>> set_thread_environ({'SCRIPT_NAME': '/test'})
       
   533     >>> request.script_name
       
   534     '/test'
       
   535 
       
   536 Ad-Hoc Attributes
       
   537 -----------------
       
   538 
       
   539 You can assign attributes to your request objects.  They will all go
       
   540 in ``environ['webob.adhoc_attrs']`` (a dictionary).  
       
   541 
       
   542 .. code-block::
       
   543 
       
   544     >>> req = Request.blank('/')
       
   545     >>> req.some_attr = 'blah blah blah'
       
   546     >>> new_req = Request(req.environ)
       
   547     >>> new_req.some_attr
       
   548     'blah blah blah'
       
   549     >>> req.environ['webob.adhoc_attrs']
       
   550     {'some_attr': 'blah blah blah'}
       
   551 
       
   552 Response
       
   553 ========
       
   554 
       
   555 The ``webob.Response`` object contains everything necessary to make a
       
   556 WSGI response.  Instances of it are in fact WSGI applications, but it
       
   557 can also represent the result of calling a WSGI application (as noted
       
   558 in `Calling WSGI Applications`_).  It can also be a way of
       
   559 accumulating a response in your WSGI application.
       
   560 
       
   561 A WSGI response is made up of a status (like ``200 OK``), a list of
       
   562 headers, and a body (or iterator that will produce a body).
       
   563 
       
   564 Core Attributes
       
   565 ---------------
       
   566 
       
   567 The core attributes are unsurprising:
       
   568 
       
   569 .. code-block::
       
   570 
       
   571     >>> from webob import Response
       
   572     >>> res = Response()
       
   573     >>> res.status
       
   574     '200 OK'
       
   575     >>> res.headerlist
       
   576     [('Content-Length', '0')]
       
   577     >>> res.body
       
   578     ''
       
   579 
       
   580 You can set any of these attributes, e.g.:
       
   581 
       
   582 .. code-block::
       
   583 
       
   584     >>> res.status = 404
       
   585     >>> res.status
       
   586     '404 Not Found'
       
   587     >>> res.status_int
       
   588     404
       
   589     >>> res.headerlist = [('Content-type', 'text/html')]
       
   590     >>> res.body = 'test'
       
   591     >>> print res
       
   592     404 Not Found
       
   593     Content-type: text/html
       
   594     Content-Length: 4
       
   595     <BLANKLINE>
       
   596     test
       
   597     >>> res.body = u"test"
       
   598     Traceback (most recent call last):
       
   599         ...
       
   600     TypeError: You cannot set Response.body to a unicode object (use Response.unicode_body)
       
   601     >>> res.unicode_body = u"test"
       
   602     Traceback (most recent call last):
       
   603         ...
       
   604     AttributeError: You cannot access Response.unicode_body unless charset is set
       
   605     >>> res.charset = 'utf8'
       
   606     >>> res.unicode_body = u"test"
       
   607     >>> res.body
       
   608     'test'
       
   609 
       
   610 You can set any attribute with the constructor, like
       
   611 ``Response(charset='utf8')``
       
   612 
       
   613 Headers
       
   614 -------
       
   615 
       
   616 In addition to ``res.headerlist``, there is dictionary-like view on
       
   617 the list in ``res.headers``:
       
   618 
       
   619 .. code-block::
       
   620 
       
   621     >>> res.headers
       
   622     HeaderDict([('content-type', 'text/html; charset=utf8'), ('Content-Length', '4')])
       
   623 
       
   624 This is case-insensitive.  It can support multiple values for a key,
       
   625 though only if you use ``res.headers.add(key, value)`` or read them
       
   626 with ``res.headers.getall(key)``.
       
   627 
       
   628 Body & app_iter
       
   629 ---------------
       
   630 
       
   631 The ``res.body`` attribute represents the entire body of the request
       
   632 as a single string (not unicode, though you can set it to unicode if
       
   633 you have a charset defined).  There is also a ``res.app_iter``
       
   634 attribute that reprsents the body as an iterator.  WSGI applications
       
   635 return these ``app_iter`` iterators instead of strings, and sometimes
       
   636 it can be problematic to load the entire iterator at once (for
       
   637 instance, if it returns the contents of a very large file).  Generally
       
   638 it is not a problem, and often the iterator is something simple like a
       
   639 one-item list containing a string with the entire body.
       
   640 
       
   641 If you set the body then Content-Length will also be set, and an
       
   642 ``res.app_iter`` will be created for you.  If you set ``res.app_iter``
       
   643 then Content-Length will be cleared, but it won't be set for you.
       
   644 
       
   645 There is also a file-like object you can access, which will update the
       
   646 app_iter in-place (turning the app_iter into a list if necessary):
       
   647 
       
   648 .. code-block::
       
   649 
       
   650     >>> res = Response(content_type='text/plain')
       
   651     >>> f = res.body_file
       
   652     >>> f.write('hey')
       
   653     >>> f.write(u'test')
       
   654     Traceback (most recent call last):
       
   655       . . .
       
   656     TypeError: You can only write unicode to Response.body_file if charset has been set
       
   657     >>> f.encoding
       
   658     >>> res.charset = 'utf8'
       
   659     >>> f.encoding
       
   660     'utf8'
       
   661     >>> f.write(u'test')
       
   662     >>> res.app_iter
       
   663     ['hey', 'test']
       
   664     >>> res.body
       
   665     'heytest'
       
   666 
       
   667 Header Getters
       
   668 --------------
       
   669 
       
   670 Like Request, HTTP response headers are also available as individual
       
   671 properties.  These represent parsed forms of the headers.
       
   672 
       
   673 Content-Type is a special case, as the type and the charset are
       
   674 handled through two separate properties:
       
   675 
       
   676 .. code-block::
       
   677 
       
   678     >>> res = Response()
       
   679     >>> res.content_type = 'text/html'
       
   680     >>> res.charset = 'utf8'
       
   681     >>> res.content_type
       
   682     'text/html'
       
   683     >>> res.headers['content-type']
       
   684     'text/html; charset=utf8'
       
   685     >>> res.content_type = 'application/atom+xml'
       
   686     >>> res.content_type_params
       
   687     {'charset': 'utf8'}
       
   688     >>> res.content_type_params = {'type': 'entry', 'charset': 'utf8'}
       
   689     >>> res.headers['content-type']
       
   690     'application/atom+xml; charset=utf8; type=entry'
       
   691 
       
   692 Other headers:
       
   693 
       
   694 .. code-block::
       
   695 
       
   696     >>> # Used with a redirect:
       
   697     >>> res.location = 'http://localhost/foo'
       
   698 
       
   699     >>> # Indicates that the server accepts Range requests:
       
   700     >>> res.accept_ranges = 'bytes'
       
   701 
       
   702     >>> # Used by caching proxies to tell the client how old the
       
   703     >>> # response is:
       
   704     >>> res.age = 120
       
   705 
       
   706     >>> # Show what methods the client can do; typically used in
       
   707     >>> # a 405 Method Not Allowed response:
       
   708     >>> res.allow = ['GET', 'PUT']
       
   709 
       
   710     >>> # Set the cache-control header:
       
   711     >>> res.cache_control.max_age = 360
       
   712     >>> res.cache_control.no_transform = True
       
   713 
       
   714     >>> # Used if you had gzipped the body:
       
   715     >>> res.content_encoding = 'gzip'
       
   716 
       
   717     >>> # What language(s) are in the content:
       
   718     >>> res.content_language = ['en']
       
   719 
       
   720     >>> # Seldom used header that tells the client where the content
       
   721     >>> # is from:
       
   722     >>> res.content_location = 'http://localhost/foo'
       
   723 
       
   724     >>> # Seldom used header that gives a hash of the body:
       
   725     >>> res.content_md5 = 'big-hash'
       
   726 
       
   727     >>> # Means we are serving bytes 0-500 inclusive, out of 1000 bytes total:
       
   728     >>> # you can also use the range setter shown earlier
       
   729     >>> res.content_range = (0, 499, 1000)
       
   730 
       
   731     >>> # The length of the content; set automatically if you set
       
   732     >>> # res.body:
       
   733     >>> res.content_length = 4
       
   734 
       
   735     >>> # Used to indicate the current date as the server understands
       
   736     >>> # it:
       
   737     >>> res.date = datetime.now()
       
   738 
       
   739     >>> # The etag:
       
   740     >>> res.etag = 'opaque-token'
       
   741     >>> # You can generate it from the body too:
       
   742     >>> res.md5_etag()
       
   743     >>> res.etag
       
   744     '1B2M2Y8AsgTpgAmY7PhCfg'
       
   745 
       
   746     >>> # When this page should expire from a cache (Cache-Control
       
   747     >>> # often works better):
       
   748     >>> import time
       
   749     >>> res.expires = time.time() + 60*60 # 1 hour
       
   750 
       
   751     >>> # When this was last modified, of course:
       
   752     >>> res.last_modified = datetime(2007, 1, 1, 12, 0, tzinfo=UTC)
       
   753 
       
   754     >>> # Used with 503 Service Unavailable to hint the client when to
       
   755     >>> # try again:
       
   756     >>> res.retry_after = 160
       
   757 
       
   758     >>> # Indicate the server software:
       
   759     >>> res.server = 'WebOb/1.0'
       
   760 
       
   761     >>> # Give a list of headers that the cache should vary on:
       
   762     >>> res.vary = ['Cookie']
       
   763 
       
   764 Note in each case you can general set the header to a string to avoid
       
   765 any parsing, and set it to None to remove the header (or do something
       
   766 like ``del res.vary``).
       
   767 
       
   768 In the case of date-related headers you can set the value to a
       
   769 ``datetime`` instance (ideally with a UTC timezone), a time tuple, an
       
   770 integer timestamp, or a properly-formatted string.
       
   771 
       
   772 After setting all these headers, here's the result:
       
   773 
       
   774 .. code-block::
       
   775 
       
   776     >>> for name, value in res.headerlist:
       
   777     ...     print '%s: %s' % (name, value)
       
   778     content-type: application/atom+xml; charset=utf8; type=entry
       
   779     location: http://localhost/foo
       
   780     Accept-Ranges: bytes
       
   781     Age: 120
       
   782     Allow: GET, PUT
       
   783     Cache-Control: max-age=360, no-transform
       
   784     Content-Encoding: gzip
       
   785     Content-Language: en
       
   786     Content-Location: http://localhost/foo
       
   787     Content-MD5: big-hash
       
   788     Content-Range: bytes 0-500/1000
       
   789     Content-Length: 4
       
   790     Date: ... GMT
       
   791     ETag: ...
       
   792     Expires: ... GMT
       
   793     Last-Modified: Mon, 01 Jan 2007 12:00:00 GMT
       
   794     Retry-After: 160
       
   795     Server: WebOb/1.0
       
   796     Vary: Cookie
       
   797 
       
   798 You can also set Cache-Control related attributes with
       
   799 ``req.cache_expires(seconds, **attrs)``, like:
       
   800 
       
   801 .. code-block::
       
   802 
       
   803     >>> res = Response()
       
   804     >>> res.cache_expires(10)
       
   805     >>> res.headers['Cache-Control']
       
   806     'max-age=10'
       
   807     >>> res.cache_expires(0)
       
   808     >>> res.headers['Cache-Control']
       
   809     'max-age=0, must-revalidate, no-cache, no-store'
       
   810     >>> res.headers['Expires']
       
   811     '... GMT'
       
   812 
       
   813 You can also use the `timedelta
       
   814 <http://python.org/doc/current/lib/datetime-timedelta.html>`_
       
   815 constants defined, e.g.:
       
   816 
       
   817 .. code-block::
       
   818 
       
   819     >>> from webob import *
       
   820     >>> res = Response()
       
   821     >>> res.cache_expires(2*day+4*hour)
       
   822     >>> res.headers['Cache-Control']
       
   823     'max-age=187200'
       
   824 
       
   825 Cookies
       
   826 -------
       
   827 
       
   828 Cookies (and the Set-Cookie header) are handled with a couple
       
   829 methods.  Most importantly:
       
   830 
       
   831 .. code-block::
       
   832 
       
   833     >>> res.set_cookie('key', 'value', max_age=360, path='/',
       
   834     ...                domain='example.org', secure=True)
       
   835     >>> res.headers['Set-Cookie']
       
   836     'key=value; Domain=example.org; Max-Age=360; Path=/; secure;'
       
   837     >>> # To delete a cookie previously set in the client:
       
   838     >>> res.delete_cookie('bad_cookie')
       
   839     >>> res.headers['Set-Cookie']
       
   840     'bad_cookie=; Max-Age=0; Path=/;'
       
   841 
       
   842 The only other real method of note (note that this does *not* delete
       
   843 the cookie from clients, only from the response object):
       
   844 
       
   845 .. code-block::
       
   846 
       
   847     >>> res.unset_cookie('key')
       
   848     >>> res.unset_cookie('bad_cookie')
       
   849     >>> print res.headers.get('Set-Cookie')
       
   850     None
       
   851 
       
   852 Binding a Request
       
   853 -----------------
       
   854 
       
   855 You can bind a request (or request WSGI environ) to the response
       
   856 object.  This is available through ``res.request`` or
       
   857 ``res.environ``.  This is currently only used in setting
       
   858 ``res.location``, to make the location absolute if necessary.
       
   859 
       
   860 Response as a WSGI application
       
   861 ------------------------------
       
   862 
       
   863 A response is a WSGI application, in that you can do:
       
   864 
       
   865 .. code-block::
       
   866 
       
   867     >>> req = Request.blank('/')
       
   868     >>> status, headers, app_iter = req.call_application(res)
       
   869 
       
   870 A possible pattern for your application might be:
       
   871 
       
   872 .. code-block::
       
   873 
       
   874     >>> def my_app(environ, start_response):
       
   875     ...     req = Request(environ)
       
   876     ...     res = Response()
       
   877     ...     res.content_type = 'text/plain'
       
   878     ...     parts = []
       
   879     ...     for name, value in sorted(req.environ.items()):
       
   880     ...         parts.append('%s: %r' % (name, value))
       
   881     ...     res.body = '\n'.join(parts)
       
   882     ...     return res(environ, start_response)
       
   883     >>> req = Request.blank('/')
       
   884     >>> res = req.get_response(my_app)
       
   885     >>> print res
       
   886     200 OK
       
   887     content-type: text/plain
       
   888     Content-Length: 394
       
   889     <BLANKLINE>
       
   890     HTTP_HOST: 'localhost:80'
       
   891     PATH_INFO: '/'
       
   892     QUERY_STRING: ''
       
   893     REQUEST_METHOD: 'GET'
       
   894     SCRIPT_NAME: ''
       
   895     SERVER_NAME: 'localhost'
       
   896     SERVER_PORT: '80'
       
   897     SERVER_PROTOCOL: 'HTTP/1.0'
       
   898     wsgi.errors: <open file '<stderr>', mode 'w' at ...>
       
   899     wsgi.input: <cStringIO.StringI object at ...>
       
   900     wsgi.multiprocess: False
       
   901     wsgi.multithread: False
       
   902     wsgi.run_once: False
       
   903     wsgi.url_scheme: 'http'
       
   904     wsgi.version: (1, 0)
       
   905 
       
   906 Exceptions
       
   907 ==========
       
   908 
       
   909 In addition to Request and Response objects, there are a set of Python
       
   910 exceptions for different HTTP responses (3xx, 4xx, 5xx codes).
       
   911 
       
   912 These provide a simple way to provide these non-200 response.  A very
       
   913 simple body is provided.
       
   914 
       
   915 .. code-block::
       
   916 
       
   917     >>> from webob.exc import *
       
   918     >>> exc = HTTPTemporaryRedirect(location='foo')
       
   919     >>> req = Request.blank('/path/to/something')
       
   920     >>> print str(req.get_response(exc)).strip()
       
   921     307 Temporary Redirect
       
   922     content-type: text/html
       
   923     location: http://localhost/path/to/foo
       
   924     Content-Length: 126
       
   925     <BLANKLINE>
       
   926     307 Temporary Redirect
       
   927     <BLANKLINE>
       
   928     The resource has been moved to http://localhost/path/to/foo; you should be redirected automatically.
       
   929 
       
   930 Note that only if there's an ``Accept: text/html`` header in the
       
   931 request will an HTML response be given:
       
   932 
       
   933 .. code-block::
       
   934 
       
   935     >>> req.accept += 'text/html'
       
   936     >>> print str(req.get_response(exc)).strip()
       
   937     307 Temporary Redirect
       
   938     content-type: text/html
       
   939     location: http://localhost/path/to/foo
       
   940     Content-Length: 270
       
   941     <BLANKLINE>
       
   942     <html>
       
   943      <head>
       
   944       <title>307 Temporary Redirect</title>
       
   945      </head>
       
   946      <body>
       
   947       <h1>307 Temporary Redirect</h1>
       
   948       The resource has been moved to <a href="http://localhost/path/to/foo">http://localhost/path/to/foo</a>;
       
   949     you should be redirected automatically.
       
   950     <BLANKLINE>
       
   951     <BLANKLINE>
       
   952      </body>
       
   953     </html>
       
   954 
       
   955 
       
   956 This is taken from `paste.httpexceptions
       
   957 <http://pythonpaste.org/module-paste.httpexceptions.html>`_, and if
       
   958 you have Paste installed then these exceptions will be subclasses of
       
   959 the Paste exceptions.
       
   960 
       
   961 Note that on Python 2.4 and before, new-style classes could not be
       
   962 used as exceptions.  ``Response`` objects must be new-style classes,
       
   963 so this causes a bit of a conflict.  The base class
       
   964 ``webob.exc.HTTPException`` *is* an exception, so you can catch that,
       
   965 and it *is* a WSGI application.  But you may not be able to use
       
   966 Response methods.  You can always get ``obj.exception`` to get an
       
   967 exception that you can raise, and ``obj.wsgi_response`` to get the
       
   968 ``Response`` object that you can use.
       
   969 
       
   970 Conditional WSGI Application
       
   971 ----------------------------
       
   972 
       
   973 The Response object can handle your conditional responses for you,
       
   974 checking If-None-Match, If-Modified-Since, and Range/If-Range.
       
   975 
       
   976 To enable this you must create the response like
       
   977 ``Response(conditional_request=True)``, or make a subclass like:
       
   978 
       
   979 .. code-block::
       
   980 
       
   981     >>> class AppResponse(Response):
       
   982     ...     default_content_type = 'text/html'
       
   983     ...     default_conditional_response = True
       
   984     >>> res = AppResponse(body='0123456789',
       
   985     ...                   last_modified=datetime(2005, 1, 1, 12, 0, tzinfo=UTC))
       
   986     >>> req = Request.blank('/')
       
   987     >>> req.if_modified_since = datetime(2006, 1, 1, 12, 0, tzinfo=UTC)
       
   988     >>> req.get_response(res)
       
   989     <Response ... 304 Not Modified>
       
   990     >>> del req.if_modified_since
       
   991     >>> res.etag = 'opaque-tag'
       
   992     >>> req.if_none_match = 'opaque-tag'
       
   993     >>> req.get_response(res)
       
   994     <Response ... 304 Not Modified>
       
   995     >>> del req.if_none_match
       
   996     >>> req.range = (1, 5)
       
   997     >>> result = req.get_response(res)
       
   998     >>> result.headers['content-range']
       
   999     'bytes 1-6/10'
       
  1000     >>> result.body
       
  1001     '1234'