The response object ===================== .. testsetup:: * from pesto.response import Response from pesto.testing import TestApp, make_environ from pesto import to_wsgi The response object allows you to set headers and provides shortcuts for common handler responses, such as redirection. Constructing response objects ``````````````````````````````` 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. .. testcode:: def app(request): return Response(['

Hello World']) .. testcode:: :hide: print TestApp(to_wsgi(app)).get().text() This will output: .. testoutput:: 200 OK Content-Type: text/html; charset=UTF-8

Hello World 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: .. testcode:: from pesto.response import Response def app(request): return Response( status=405, # method not allowed content_type='text/html', allow=['GET', 'POST'], content=['Sorry, that method is not allowed'] ) .. testcode:: :hide: print TestApp(to_wsgi(app)).get().text() This will output: .. testoutput:: 405 Method Not Allowed Allow: GET Allow: POST Content-Type: text/html Sorry, that method is not allowed 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: .. testcode:: Response( ['Sorry, that method is not allowed'], status=405, headers=[('Content-Type', 'text/html'), ('Allow', 'GET'), ('Allow', 'POST')], ) Response( ['Sorry, that method is not allowed'], status=405, content_type='text/html', allow=['GET', 'POST'], ) Changing response objects ``````````````````````````` 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: .. testcode:: 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') .. doctest:: >>> 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: .. doctest:: >>> 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: .. doctest:: >>> 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. Integrating with WSGI ------------------------ 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 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 [ "" ... "" ... "

Hello World!

" ... "" ... "" ... ] ... >>> 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

Hello World!

Common responses ----------------- Many canned error responses are available as ``Response`` classmethods: .. doctest:: >>> 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']) ... Redirect responses ```````````````````` A temporary or permanent redirect may be achieved by returning ``pesto.response.Response.redirect()``. For example: .. doctest:: >>> def redirect(request): ... return Response.redirect("http://www.example.com/") ... pesto.response API documention ------------------------------- .. automodule:: pesto.response :members: