The Response Class

A response object encapsulates the output from a view to the web browser, including:

  • HTTP status (eg 200 OK or 404 Not Found)
  • HTTP headers (eg Content-Type: text/plain)
  • The response content (eg an html page)

Constructing response objects

At the minimum the Response constructor needs the response content. If this is all you specify, a successful (200 OK) response of type text/html will be generated with your specified content.

from fresco import Response

def app():
    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:

def app():
        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'],
)

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 view functions together, each operating on the output of the last:

def view1():
    return Response([b"Ten green bottles, hanging on the wall"],
                    content_type='text/plain')

def view2():
    response = view1()
    return response.replace(content=[chunk.replace(b'Ten', b'Nine')
                                     for chunk in response.content])

def view3():
    response = view2()
    return response.replace(content_type='text/html')
>>> view1().content_type
'text/plain'
>>> view1().content
[b'Ten green bottles, hanging on the wall']

>>> view2().content
[b'Nine green bottles, hanging on the wall']

>>> view3().content_type
'text/html'
>>> view3().content
[b'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')
>>> sorted(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')])
>>> sorted(r.headers)
[('Cache-Control', 'Private'), ('Content-Length', '11'), ('Content-Type', 'text/html; charset=UTF-8')]
>>> r = r.add_headers(x_powered_by='fresco')
>>> sorted(r.headers)
[('Cache-Control', 'Private'), ('Content-Length', '11'), ('Content-Type', 'text/html; charset=UTF-8'), ('X-Powered-By', 'fresco')]

Removing and replacing headers is the same. See the API documentation for Response for details.

Integrating with WSGI

It’s often useful to be able to switch between Fresco view functions and WSGI application functions – for example, when writing WSGI middleware.

To aid this, Response objects are themselves 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!

It is also 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='fresco')(environ, start_response)
...
>>> print(TestApp(altered_wsgi_app).get('/').text())
200 OK
Content-Type: text/html
X-Powered-By: fresco

<html><body><h1>Hello World!</h1></body></html>

Common responses

Many canned responses are available as Response class methods:

>>> from fresco.response import Response
>>> def view():
...     if not somecondition():
...         return Response.not_found()
...     return Response(['ok'])
...

>>> def view2():
...     if not somecondition():
...         return Response.forbidden()
...     return Response.redirect('http://www.example.com/')
...

fresco.response API reference

The Response class models the response from your application to a single request.

class fresco.response.Response(content=None, status=None, headers=None, onclose=None, _nocontent=[], passthrough=False, content_iterator=None, make_headers=<function make_headers>, **kwargs)[source]

Model an HTTP response

Return a new response object with the given cookie added.

Synopsis:

>>> r = Response(content_type='text/plain')
>>> r.headers
[('Content-Type', 'text/plain')]
>>> r.add_cookie('a', '1').headers
[('Content-Type', 'text/plain'), ('Set-Cookie', 'a=1;Version=1')]
add_header(name, value, make_header_name=<function make_header_name>)[source]

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
[('Content-Type', 'text/plain'), ('Cache-Control', 'no-cache')]
add_headers(headers=[], **kwheaders)[source]

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',
... ).headers
[('Content-Type', 'text/plain'), ('Cache-Control', 'no-cache')]
add_onclose(*funcs)[source]

Add functions to be called as part of the response iterator’s close method.

add_vary(*vary_on)[source]

Return a new response object with the given Vary header. Values specified will be added to any existing vary header.

Synopsis:

>>> r = Response().add_vary('Accept-Encoding')
>>> r.headers
[('Vary', 'Accept-Encoding')]
classmethod bad_request(request=None)[source]

Return an HTTP bad request response.

Synopsis:

>>> def view():
...     return Response.bad_request()
...
buffered()[source]

Return a new response object with the content buffered into a list. This will also generate a content-length header.

Example usage:

>>> def generate_content():
...     yield "one two "
...     yield "three four five"
...
>>> r = Response(content=generate_content())
>>> r.content  
<generator object ...>
>>> r = Response(content=generate_content()).buffered()
>>> r.content
['one two ', 'three four five']
content_type

Return the value of the Content-Type header if set, otherwise None.

classmethod forbidden(message='Sorry, access is denied')[source]

Return an HTTP forbidden response (403).

Synopsis:

>>> def view():
...     return Response.forbidden()
...
classmethod from_wsgi(wsgi_callable, environ, start_response)[source]

Return a Response object constructed from the result of calling wsgi_callable with the given environ and start_response arguments.

get_header(name, default='')[source]

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'
get_headers(name)[source]

Return the list of headers set with the given name.

Synopsis:

>>> r = Response(set_cookie = ['cookie1', 'cookie2'])
>>> r.get_headers('set-cookie')
['cookie1', 'cookie2']
classmethod internal_server_error()[source]

Return an HTTP internal server error response (500).

Synopsis:

>>> def view():
...     return Response.internal_server_error()
...
Returns:A fresco.response.Response instance
classmethod json(data, indent=None, separators=(', ', ':'), content_type='application/json', status=None, headers=None, dumps=<function dumps>, **kwargs)[source]

Return an application/json response with the given data JSON serialized

Parameters:
  • data – The data to json encode.
  • indent – The indent level. Defaults to None (no pretty printing)
  • separators – Defaults to (',', ':') for the most compact JSON representation
  • kwargs – Other keyword arguments are passed to json.dumps. These may be used to change encoding paramters, for example overriding the default JSONEncoder class.
classmethod length_required(request=None)[source]

Return an HTTP Length Required response (411).

Synopsis:

>>> def view():
...     return Response.length_required()
...
classmethod meta_refresh(location, delay=1, request=None)[source]

Return an HTML page containing a <meta http-equiv=”refresh”> tag, causing the browser to redirect to the given location after delay seconds.

Parameters:location – the URI of the new location. If relative this will be converted to an absolute URL based on the current request.
classmethod method_not_allowed(valid_methods)[source]

Return an HTTP method not allowed response (405):

>>> from fresco import context
>>> def view():
...     if context.request.method == 'POST':
...         return Response.method_not_allowed(('POST', ))
...
Parameters:valid_methods – A list of HTTP methods valid for requested URL
Returns:A fresco.response.Response instance
classmethod not_found(request=None)[source]

Return an HTTP not found response (404).

Synopsis:

>>> def view():
...     return Response.not_found()
...
classmethod payload_too_large(request=None)[source]

Return an HTTP Payload Too Large response (413):

>>> response = Response.payload_too_large()
classmethod redirect(location, request=None, status=302, **kwargs)[source]

Return an HTTP redirect reponse (302 or 301).

Parameters:
  • location – The redirect location or a view specification.
  • status – HTTP status code for the redirect, default is STATUS_FOUND (temporary redirect)

Synopsis:

>>> def view():
...   return Response.redirect("/new-location")
...

The location parameter is interpreted as follows:

  • If it is a callable it is assumed to be a view, and passed to fresco.core.urlfor() to resolve it to a URL.
  • If it is a string and contains ‘://’ it is assumed to be an absolute URL and no further processing is done.
  • If it is a string and contains a slash, it is assumed to be a relative URL and resolved relative to the current request.
  • If it is any other string it is passed to fresco.core.urlfor() to resolve it to a URL. If this fails (ie raises RouteNotFound) it is assumed to be a relative URL and resolved relative to the current request.
classmethod redirect_permanent(*args, **kwargs)[source]

Return an HTTP permanent redirect reponse.

Parameters:location – the URI of the new location. If relative this will be converted to an absolute URL based on the current request.
classmethod redirect_temporary(*args, **kwargs)[source]

Return an HTTP permanent redirect reponse.

Parameters:location – the URI of the new location. If relative this will be converted to an absolute URL based on the current request.
remove_headers(*headers)[source]

Return a new response object with the named 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')]
replace(content=None, status=None, headers=None, **kwheaders)[source]

Return a new response object with any of content, status or headers changed.

Synopsis:

>>> r = Response(content_type='text/html')
>>> r = r.replace(content='foo',
...               status=404,
...               headers=[('Content-Type', 'text/plain')],
...               content_length=3)
classmethod request_entity_too_large(request=None)

Return an HTTP Payload Too Large response (413):

>>> response = Response.payload_too_large()
status_code

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