Source code for seshat.controller

#!/usr/bin/env python
"""
No app built with Seshat does much without controllers. This module provides a
base controller class which can be used right away in its current state, or can
be inherited from to create more advanced or custom controllers.

Basic use is like so::

    from seshat.controller import BaseController

    class index(BaseController):
      def GET(self):
        return "<h1>WAT</h1>"

If you see something along the lines of 'Content Generating Request Method' it
will usually mean ``GET()``, ``POST()``, or any other HTTP method verb which
might be given to the controller.
"""
"""
For more information and licensing, see: https://github.com/JoshAshby/seshat

http://xkcd.com/353/

Josh Ashby
2014
http://joshashby.com
joshuaashby@joshashby.com
"""
import traceback
import actions
import logging
from head import Head

logger = logging.getLogger("seshat.controller")


[docs]class BaseController(object): """ The parent of all controllers which Seshat will serve. To use this to make a controller, override or add the request method (in all caps) which will be called for this controller. Eg, with the controller:: from seshat.controller import BaseController class index(BaseController): def GET(self): return "<h1>WAT</h1>" then all GET method requests to this controller will return with the text `<h1>WAT</h1>` however all POST, PUT, DELETE calls will return as a blank page, since those methods are not overridden. .. note:: Support for `Not Supported` status codes may be added later, ironically. """ error = None def __init__(self, request): self.head = Head() self.request = request self.post_init_hook()
[docs] def post_init_hook(self): """ Called at the end of `__init__` this allows you to customize the creation process of your controller, without having to override `__init__ itself`. This should accept nothing and return nothing. """ pass
def _build(self): content = u"" try: c = self.pre_content_hook() if c is not None: if isinstance(c, actions.BaseAction): return u"", c.head elif isinstance(c, Head): return u"", c content = getattr(self, self.request.method)() if isinstance(content, actions.BaseAction): self.head = content.head self.post_content_hook(content) except Exception as e: tb = str(traceback.format_exc()) logger.exception(e) logger.error(tb) self.head = Head("500 INTERNAL SERVER ERROR", errors=[e, tb]) return content, self.head
[docs] def pre_content_hook(self): """ Called before the generating request method is called and should return either `None` or :py:class:`.Head` or :py:class:`.BaseAction` object. If there is a returned value other than None, this will skip calling the content generating request method and simply return directly to dispatch. A good example of the use for this hook would be for authentication. You could for example, check the id set through the cookie and compare it to a database entry. If the cookie is not currently in use (ie, user not logged in, or similar) then you could do:: return Head("401") or perhaps:: return actions.Unauthorized() :rtype: :py:class:`.Head` or :py:class:`.BaseAction` or `None` """ return None
[docs] def post_content_hook(self, content): """ Gets called after the content generating request method has been called. This can be to further modify the content which is returned, or perform some other action after each request. :param content: the content from the content generating request method that was called. :type content: `str` :return: The original or modified content :rtype: `str` """ return content
[docs] def HEAD(self): """ Will be called if the request method is HEAD By default this will call `GET()` but return nothing, so that only the Headers are returned to the client. """ self.GET()
[docs] def GET(self): """ Will be called if the request method is GET """ pass