The response object allows you to set headers and provides shortcuts for common handler responses, such as redirection.
At the minimum the pesto.response.Response constructor needs the response content. If this is all you specify, a successful response of type text/html will be generated with your specified content.
def app(request):
return Response(['<html><body><h1>Hello World</body></html>'])
This will output:
200 OK
Content-Type: text/html; charset=UTF-8
<html><body><h1>Hello World</body></html>
The content argument must be an iterable object – eg a list, a generator expression or any python object that implements the iteration interface)
HTTP headers and a status code can also be supplied. Here’s a longer example, showing more options:
from pesto.response import Response
def app(request):
return Response(
status=405, # method not allowed
content_type='text/html',
allow=['GET', 'POST'],
content=['<html><body>Sorry, that method is not allowed</body></html>']
)
This will output:
405 Method Not Allowed
Allow: GET
Allow: POST
Content-Type: text/html
<html><body>Sorry, that method is not allowed</body></html>
Headers can be supplied as a list of tuples (the same way the WSGI start_response function expects them), or as keyword arguments, or any mixture of the two:
Response(
['<html><body>Sorry, that method is not allowed</body></html>'],
status=405,
headers=[('Content-Type', 'text/html'), ('Allow', 'GET'), ('Allow', 'POST')],
)
Response(
['<html><body>Sorry, that method is not allowed</body></html>'],
status=405,
content_type='text/html',
allow=['GET', 'POST'],
)
Response objects have a range of methods allowing you to add, remove and replace the headers and content. This makes it easy to chain handler functions together, each operating on the output of the last:
def handler1(request):
return Response(["Ten green bottles, hanging on the wall"], content_type='text/plain')
def handler2(request):
response = handler1(request)
return response.replace(content=[chunk.replace('Ten', 'Nine') for chunk in response.content])
def handler3(request):
response = handler2(request)
return response.replace(content_type='text/html')
>>> from pesto.testing import TestApp
>>> print TestApp(to_wsgi(handler1)).get('/').text()
200 OK
Content-Type: text/plain
Ten green bottles, hanging on the wall
>>> print TestApp(to_wsgi(handler2)).get('/').text()
200 OK
Content-Type: text/plain
Nine green bottles, hanging on the wall
>>> print TestApp(to_wsgi(handler3)).get('/').text()
200 OK
Content-Type: text/html
Nine green bottles, hanging on the wall
Headers may be added, either singly:
>>> r = Response(content = ['Whoa nelly!'])
>>> r.headers
[('Content-Type', 'text/html; charset=UTF-8')]
>>> r = r.add_header('Cache-Control', 'private')
>>> r.headers
[('Cache-Control', 'private'), ('Content-Type', 'text/html; charset=UTF-8')]
or in groups:
>>> r = Response(content = ['Whoa nelly!'])
>>> r.headers
[('Content-Type', 'text/html; charset=UTF-8')]
>>> r = r.add_headers([('Content-Length', '11'), ('Cache-Control', 'Private')])
>>> r.headers
[('Cache-Control', 'Private'), ('Content-Length', '11'), ('Content-Type', 'text/html; charset=UTF-8')]
>>> r = r.add_headers(x_powered_by='pesto')
>>> r.headers
[('Cache-Control', 'Private'), ('Content-Length', '11'), ('Content-Type', 'text/html; charset=UTF-8'), ('X-Powered-By', 'pesto')]
Removing and replacing headers is the same. See the API documentation for pesto.response.Response for details.
It’s often useful to be able to switch between Pesto handler functions and WSGI application functions – for example, when writing WSGI middleware.
To aid this, Response objects are fully compliant WSGI applications:
>>> def mywsgi_app(environ, start_response):
... r = Response(content = ['Whoa nelly!'])
... return r(environ, start_response)
...
>>> print TestApp(mywsgi_app).get('/').text()
200 OK
Content-Type: text/html; charset=UTF-8
<BLANKLINE>
Whoa nelly!
Secondly, it is possible to proxy a WSGI application through a response object, capturing its output to allow further inspection and modification:
>>> def basic_wsgi_app(environ, start_response):
... start_response('200 OK', [('Content-Type', 'text/html')])
... return [ "<html>"
... "<body>"
... "<h1>Hello World!</h1>"
... "</body>"
... "</html>"
... ]
...
>>> def altered_wsgi_app(environ, start_response):
... response = Response.from_wsgi(wsgi_app1, environ, start_response)
... return response.add_headers(x_powered_by='pesto')(environ, start_response)
...
>>> print TestApp(altered_wsgi_app).get('/').text()
200 OK
Content-Type: text/html
X-Powered-By: pesto
<BLANKLINE>
<html><body><h1>Hello World!</h1></body></html>
Many canned error responses are available as Response classmethods:
>>> from pesto.response import Response
>>> def handler(request):
... if not somecondition():
... return Response.not_found()
... return Response(['ok'])
...
>>> def handler2(request):
... if not somecondition():
... return Response.forbidden()
... return Response(['ok'])
...
A temporary or permanent redirect may be achieved by returning pesto.response.Response.redirect(). For example:
>>> def redirect(request):
... return Response.redirect("http://www.example.com/")
...
Response object for WSGI applications
WSGI Response object.
Parameters: |
|
---|
Example usage:
>>> # Construct a response
>>> response = Response(
... content=['hello world\n'],
... status='200 OK',
... headers=[('Content-Type', 'text/plain')]
... )
>>>
>>> # We can manipulate the response before outputting it
>>> response = response.add_header('X-Header', 'hello!')
>>>
>>> # To output the response, we call it as a WSGI application
>>> from pesto.testing import TestApp
>>> print TestApp(response).get('/').text()
200 OK
Content-Type: text/plain
X-Header: hello!
<BLANKLINE>
hello world
<BLANKLINE>
>>>
Note that response objects are themselves callable WSGI applications:
def wsgiapp(environ, start_response):
response = Response(['hello world'], content_type='text/plain')
return response(environ, start_response)
Return a new response object with the given cookie added.
Synopsis:
>>> r = Response(content_type = 'text/plain', cache_control='no-cache')
>>> r.headers
[('Cache-Control', 'no-cache'), ('Content-Type', 'text/plain')]
>>> r.add_cookie('foo', 'bar').headers
[('Cache-Control', 'no-cache'), ('Content-Type', 'text/plain'), ('Set-Cookie', 'foo=bar;Version=1')]
Return a new response object with the given additional header.
Synopsis:
>>> r = Response(content_type = 'text/plain')
>>> r.headers
[('Content-Type', 'text/plain')]
>>> r.add_header('Cache-Control', 'no-cache').headers
[('Cache-Control', 'no-cache'), ('Content-Type', 'text/plain')]
Return a new response object with the given additional headers.
Synopsis:
>>> r = Response(content_type = 'text/plain')
>>> r.headers
[('Content-Type', 'text/plain')]
>>> r.add_headers(
... cache_control='no-cache',
... expires='Mon, 26 Jul 1997 05:00:00 GMT'
... ).headers
[('Cache-Control', 'no-cache'), ('Content-Type', 'text/plain'), ('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT')]
Add functions to be called as part of the response iterators close method.
Returns an HTTP bad request response.
Example usage:
>>> from pesto.testing import TestApp
>>> from pesto import to_wsgi
>>> @to_wsgi
... def app(request):
... return Response.bad_request()
...
>>> print TestApp(app).get('/')
400 Bad Request
Content-Type: text/html; charset=UTF-8
<html><body><h1>The server could not understand your request</h1></body></html>
Return a new response object with the content buffered into a list. This will also add a content-length header.
Example usage:
>>> def generate_content():
... yield "one two "
... yield "three four five"
...
>>> Response(content=generate_content()).content
<generator object ...>
>>> Response(content=generate_content()).buffered().content
['one two ', 'three four five']
Iterator over the response content part
Return the value of the Content-Type header if set, otherwise None.
Return an HTTP forbidden response (403):
>>> from pesto.testing import TestApp
>>> from pesto import to_wsgi
>>> @to_wsgi
... def app(request):
... return Response.forbidden()
...
>>> print TestApp(app).get('/')
403 Forbidden
Content-Type: text/html; charset=UTF-8
<html>
<body>
<h1>Sorry, access is denied</h1>
</body>
</html>
Return a Response object constructed from the result of calling wsgi_callable with the given environ and start_response arguments.
Return the concatenated values of the named header(s) or default if the header has not been set.
As specified in RFC2616 (section 4.2), multiple headers will be combined using a single comma.
Example usage:
>>> r = Response(set_cookie = ['cookie1', 'cookie2'])
>>> r.get_header('set-cookie')
'cookie1,cookie2'
Return the list of headers set with the given name.
Synopsis:
>>> r = Response(set_cookie = ['cookie1', 'cookie2'])
>>> r.get_headers('set-cookie')
['cookie1', 'cookie2']
Return a list of response headers in the format [(<header-name>, <value>), ...]
Return an HTTP internal server error response (500):
>>> from pesto.testing import TestApp
>>> from pesto import to_wsgi
>>> @to_wsgi
... def app(request):
... return Response.internal_server_error()
...
>>> print TestApp(app).get('/')
500 Internal Server Error
Content-Type: text/html; charset=UTF-8
<html><body><h1>Internal Server Error</h1></body></html>
Returns an HTTP 411 Length Required request response.
Example usage:
>>> from pesto.testing import TestApp
>>> from pesto import to_wsgi
>>> @to_wsgi
... def app(request):
... return Response.length_required()
...
>>> print TestApp(app).get('/')
411 Length Required
Content-Type: text/html; charset=UTF-8
<html><body><h1>The Content-Length header was missing from your request</h1></body></html>
Return a list of header (name, value) tuples from the combination of the header_list and header_dict.
Example usage:
>>> Response.make_headers(
... [('Content-Type', 'text/html')],
... {'content_length' : 54}
... )
[('Content-Type', 'text/html'), ('Content-Length', '54')]
>>> Response.make_headers(
... [('Content-Type', 'text/html')],
... {'x_foo' : ['a1', 'b2']}
... )
[('Content-Type', 'text/html'), ('X-Foo', 'a1'), ('X-Foo', 'b2')]
Return a status line from the given status, which may be a simple integer.
Example usage:
>>> Response.make_status(200)
'200 OK'
Returns an HTTP method not allowed response (404):
>>> from pesto.testing import TestApp
>>> from pesto import to_wsgi
>>> @to_wsgi
... def app(request):
... return Response.method_not_allowed(valid_methods=("GET", "HEAD"))
...
>>> print TestApp(app).get('/')
405 Method Not Allowed
Allow: GET,HEAD
Content-Type: text/html; charset=UTF-8
<html><body><h1>Method not allowed</h1></body></html>
Returns an HTTP not found response (404). This method also outputs the necessary HTML to be used as the return value for a pesto handler.
Synopsis:
>>> from pesto.testing import TestApp
>>> from pesto import to_wsgi
>>> @to_wsgi
... def app(request):
... return Response.not_found()
...
>>> print TestApp(app).get('/')
404 Not Found
Content-Type: text/html; charset=UTF-8
<html>
<body>
<h1>Not found</h1>
<p>The requested resource could not be found.</p>
</body>
</html>
Return an HTTP redirect reponse.
Synopsis:
>>> from pesto.testing import TestApp
>>> from pesto import to_wsgi
>>> @to_wsgi
... def app(request):
... return Response.redirect("/new-location", request)
...
>>> print TestApp(app).get('/')
302 Found
Content-Type: text/html; charset=UTF-8
Location: http://localhost/new-location
<html><head></head><body>
<h1>Page has moved</h1>
<p><a href='http://localhost/new-location'>http://localhost/new-location</a></p>
</body></html>
Note that we can also do the following:
>>> from functools import partial
>>> from pesto.testing import TestApp
>>> from pesto.dispatch import dispatcher_app
>>> d = dispatcher_app()
>>> d.match('/animals', GET=partial(Response.redirect, '/new-location'))
>>> print TestApp(d).get('/animals')
302 Found
Content-Type: text/html; charset=UTF-8
Location: http://localhost/new-location
<html><head></head><body>
<h1>Page has moved</h1>
<p><a href='http://localhost/new-location'>http://localhost/new-location</a></p>
</body></html>
Return a new response object with the given headers removed.
Synopsis:
>>> r = Response(content_type = 'text/plain', cache_control='no-cache')
>>> r.headers
[('Cache-Control', 'no-cache'), ('Content-Type', 'text/plain')]
>>> r.remove_headers('Cache-Control').headers
[('Content-Type', 'text/plain')]
Return a new response object with any of content, status or headers changed.
Synopsis:
>>> Response(allow='GET', foo='bar', add_default_content_type=False).replace(allow='POST').headers
[('Allow', 'POST'), ('Foo', 'bar')]
>>> Response(allow='GET', add_default_content_type=False).replace(headers=[('allow', 'POST')]).headers
[('Allow', 'POST')]
>>> Response(location='http://www.google.com').replace(status=301).status
'301 Moved Permanently'
>>> Response(content=['donald']).replace(content=['pluto']).content
['pluto']
Returns an HTTP 413 Request Entity Too Large response.
Example usage:
>>> from pesto.testing import TestApp
>>> from pesto import to_wsgi
>>> @to_wsgi
... def app(request):
... return Response.request_entity_too_large()
...
>>> print TestApp(app).get('/')
413 Request Entity Too Large
Content-Type: text/html; charset=UTF-8
<html><body><h1>Request Entity Too Large</h1></body></html>
HTTP status message for the response, eg 200 OK
Return the numeric status code for the response as an integer:
>>> Response(status='404 Not Found').status_code
404
>>> Response(status=200).status_code
200