Source code for gimme.response

import traceback
import time
import datetime
import mimetypes
import re
import sys
from contextlib import nested
from .dotdict import DotDict
from .headers import ResponseHeaders, Header
from .controller import ErrorController
from .parsers.status import StatusCode
import gimme.errors


[docs]class Response(object): ''' The Response class is responsible for aggregating all of the information required for generating a response and for rendering said response. :ivar app: The Gimme application. :ivar route: The route that matched the request/response objects. :ivar request: The request object. :ivar headers: An instance of :class:`gimme.headers.ResponseHeaders` that will eventually be converted to a string appropriate for the HTTP response headers. :ivar body: The body of response. :ivar locals: An instance of :class:`gimme.dotdict.DotDict` that is intended to store arbitrary information that is unique to a given response object. ''' _charset_pattern = re.compile('(.*?); charset=(.*)$') mimetypes.init() def __init__(self, app, route, request): self.app = app self.route = route self.request = request self._status = StatusCode('200 OK') self._aborted = False try: self.headers = ResponseHeaders(dict(app.get('default headers'))) except KeyError, e: self.headers = ResponseHeaders() self._controller_class = (route.method.im_class if hasattr(route.method, 'im_class') else None) self.body = None # Storage for request-specific local data self.locals = DotDict() @property def status(self): ''' The response status code. This is an instance of :class:`StatusCode <gimme.parsers.status.StatusCode>` and can be set using any of the following methods:: response.status = 404 response.status = "404 Not Found" reponse.status.set(404) Checking a status is as simple as doing any of the following:: response.status == 404 response.status == "404 Not Found" ''' return self._status @status.setter
[docs] def status(self, status): self._status.set(status)
def _render(self, middleware=None): if self._controller_class: controller = self._controller_class(self.app, self.request, self) method = self.route.method else: controller = None def method(): return self.route.method if middleware is None: middleware = (self.app._middleware + self.route.middleware) elif not middleware: middleware = [] instantiated_middleware = self._instantiate_middleware(middleware) with nested(*instantiated_middleware): try: if not self._aborted: self.body = method() except gimme.errors.AbortRender: self._aborted = True def _instantiate_middleware(self, middleware): result = [] for i in middleware: result.append(i(self.app, self.request, self)) return result
[docs] def set(self, key, value): ''' A shortcut for setting a response header. :param key: The key of the header to set. :param value: The value of the header. ''' self.headers[key] = value return self
[docs] def get(self, key): ''' A shortcut for getting a response header. :param key: The key of the header to get. ''' return self.headers[key]
[docs] def redirect(self, path, code=302): ''' A shortcut for setting the response status and HTTP "Location" header appropriately to redirect the requesting client. :param str path: The location to send the client to. :param code: The status code to set. ''' self.status = code self.location = path
@property def location(self): ''' A shortcut for getting and setting the HTTP "Location" header. ''' return self.get('Location') @location.setter
[docs] def location(self, path): self.set('Location', path)
@property def type(self): ''' A shortcut for getting and setting the HTTP "Content-Type" header. ''' return self.get('Content-Type') @type.setter
[docs] def type(self, content_type): self.set('Content-Type', content_type)
[docs] def cookie(self, key, value, expires=None, http_only=False, secure=False, path='/', domain=None): ''' A helper method for setting the HTTP "Set-Cookie" header. :param key: The key/name of the cookie. :param value: What to set the cookie to. :param expires: Number of seconds that the cookie should last before being expired. Defaults to ``None``, which makes the cookie last indefinitely. :param http_only: Set the HttpOnly flag. :param secure: Set the Secure flag. :param path: Limit the URI path that the cookie applies to. :param domain: Limit the domain that the cookie applies to. ''' cookie_string = ['%s=%s' % (key, value)] if domain: cookie_string.append('Domain=%s' % domain) if path: cookie_string.append('Path=%s' % path) if expires: if isinstance(expires, int): date = datetime.datetime.utcfromtimestamp(time.mktime( time.gmtime(time.time() + expires))) elif isinstance(expires, datetime.datetime): date = expires else: date = datetime.datetime.utcnow() cookie_string.append('Expires=%s' % date.strftime( '%a, %d %b %Y %H:%M:%S GMT')) if secure: cookie_string.append('Secure') if http_only: cookie_string.append('HttpOnly') self.headers.add_header(Header('Set-Cookie', '; '.join(cookie_string)))
[docs] def attachment(self, filename=None): ''' A helper for setting the "Content-Disposition" and "Content-Type" HTTP headers for a given file. This makes it easy to pass a file to the client for downloading. Note, this is implemented as a setter-only property. In other words, you cannot "get" the attachment information with this property, but only set it. This is valid:: response.attachment = "something.jpg" But this is **not**:: print response.attachment ''' if filename: self.headers['Content-Disposition'] = ('attachment; filename="%s"' % filename) mimetype, encoding = mimetypes.guess_type(filename) if mimetype: self.type = mimetype else: self.headers['Content-Disposition'] = 'attachment';
attachment = property(None, attachment, None, attachment.__doc__) @property def charset(self): ''' A helper for getting and setting the charset portion of the "Content-Type" header. ''' if 'Content-Type' in self.headers: header = self.headers['Content-Type'] match = self._charset_pattern.match(header.value) if match: return match.group(2) return None @charset.setter
[docs] def charset(self, value): if 'Content-Type' in self.headers: header = self.headers['Content-Type'] else: header = Header('Content-Type', 'text/html') self.headers.add_header(header) match = self._charset_pattern.match(header.value) if match: header.value = '%s; charset=%s' % ( match.group(1), value) else: header.value = '%s; charset=%s' % ( header.value, value)
links = property(None, links, None, links.__doc__) def render(self, template, params): raise NotImplementedError("Response.render() not implemented!")