n0v4pl4n3t / werkzeug / contrib /

Full commit
# -*- coding: utf-8 -*-

    This module provides some simple shortcuts to make using Werkzeug simpler
    for small scripts.

    These improvements include predefined `Request` and `Response` objects as
    well as a predefined `Application` object which can be customized in child
    classes, of course.  The `Request` and `Reponse` objects handle URL
    generation as well as sessions via `werkzeug.contrib.sessions` and are
    purely optional.

    There is also some integration of template engines.  The template loaders
    are, of course, not neccessary to use the template engines in Werkzeug,
    but they provide a common interface.  Currently supported template engines
    include Werkzeug's minitmpl and Genshi_.  Support for other engines can be
    added in a trivial way.  These loaders provide a template interface
    similar to the one used by Django_.

    .. _Genshi:
    .. _Django:

    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
    :license: BSD, see LICENSE for more details.
from os import path
from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase
from werkzeug.templates import Template
from werkzeug.exceptions import HTTPException
from werkzeug.routing import RequestRedirect

__all__ = ['Request', 'Response', 'TemplateNotFound', 'TemplateLoader',
           'GenshiTemplateLoader', 'Application']

from warnings import warn
warn(DeprecationWarning('werkzeug.contrib.kickstart is deprecated and '
                        'will be removed in Werkzeug 1.0'))

class Request(RequestBase):
    """A handy subclass of the base request that adds a URL builder.
    It when supplied a session store, it is also able to handle sessions.

    def __init__(self, environ, url_map,
            session_store=None, cookie_name=None):
        # call the parent for initialization
        RequestBase.__init__(self, environ)
        # create an adapter
        self.url_adapter = url_map.bind_to_environ(environ)
        # create all stuff for sessions
        self.session_store = session_store
        self.cookie_name = cookie_name

        if session_store is not None and cookie_name is not None:
            if cookie_name in self.cookies:
                # get the session out of the storage
                self.session = session_store.get(self.cookies[cookie_name])
                # create a new session
                self.session =

    def url_for(self, callback, **values):
        return, values)

class Response(ResponseBase):
    A subclass of base response which sets the default mimetype to text/html.
    It the `Request` that came in is using Werkzeug sessions, this class
    takes care of saving that session.
    default_mimetype = 'text/html'

    def __call__(self, environ, start_response):
        # get the request object
        request = environ['werkzeug.request']

        if request.session_store is not None:
            # save the session if neccessary

            # set the cookie for the browser if it is not there:
            if request.cookie_name not in request.cookies:
                self.set_cookie(request.cookie_name, request.session.sid)

        # go on with normal response business
        return ResponseBase.__call__(self, environ, start_response)

class Processor(object):
    """A request and response processor - it is what Django calls a
    middleware, but Werkzeug also includes straight-foward support for real
    WSGI middlewares, so another name was chosen.

    The code of this processor is derived from the example in the Werkzeug
    trac, called `Request and Response Processor

    def process_request(self, request):
        return request

    def process_response(self, request, response):
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        """process_view() is called just before the Application calls the
        function specified by view_func.

        If this returns None, the Application processes the next Processor,
        and if it returns something else (like a Response instance), that
        will be returned without any further processing.
        return None

    def process_exception(self, request, exception):
        return None

class Application(object):
    """A generic WSGI application which can be used to start with Werkzeug in
    an easy, straightforward way.

    def __init__(self, name, url_map, session=False, processors=None):
        # save the name and the URL-map, as it'll be needed later on = name
        self.url_map = url_map
        # save the list of processors if supplied
        self.processors = processors or []
        # create an instance of the storage
        if session:
   = session
   = None

    def __call__(self, environ, start_response):
        # create a request - with or without session support
        if is not None:
            request = Request(environ, self.url_map,
      , cookie_name='%s_sid' %
            request = Request(environ, self.url_map)

        # apply the request processors
        for processor in self.processors:
            request = processor.process_request(request)

            # find the callback to which the URL is mapped
            callback, args = request.url_adapter.match(request.path)
        except (HTTPException, RequestRedirect), e:
            response = e
            # check all view processors
            for processor in self.processors:
                action = processor.process_view(request, callback, (), args)
                if action is not None:
                    # it is overriding the default behaviour, this is
                    # short-circuiting the processing, so it returns here
                    return action(environ, start_response)

                response = callback(request, **args)
            except Exception, exception:
                # the callback raised some exception, need to process that
                for processor in reversed(self.processors):
                    # filter it through the exception processor
                    action = processor.process_exception(request, exception)
                    if action is not None:
                        # the exception processor returned some action
                        return action(environ, start_response)
                # still not handled by a exception processor, so re-raise

        # apply the response processors
        for processor in reversed(self.processors):
            response = processor.process_response(request, response)

        # return the completely processed response
        return response(environ, start_response)

    def config_session(self, store, expiration='session'):
        Configures the setting for cookies. You can also disable cookies by
        setting store to None.
        """ = store
        # expiration=session is the default anyway
        # TODO: add settings to define the expiration date, the domain, the
        # path any maybe the secure parameter.

class TemplateNotFound(IOError, LookupError):
    A template was not found by the template loader.

    def __init__(self, name):
        IOError.__init__(self, name) = name

class TemplateLoader(object):
    A simple loader interface for the werkzeug minitmpl
    template language.

    def __init__(self, search_path, encoding='utf-8'):
        self.search_path = path.abspath(search_path)
        self.encoding = encoding

    def get_template(self, name):
        """Get a template from a given name."""
        filename = path.join(self.search_path, *[p for p in name.split('/')
                                                 if p and p[0] != '.'])
        if not path.exists(filename):
            raise TemplateNotFound(name)
        return Template.from_file(filename, self.encoding)

    def render_to_response(self, *args, **kwargs):
        """Load and render a template into a response object."""
        return Response(self.render_to_string(*args, **kwargs))

    def render_to_string(self, *args, **kwargs):
        """Load and render a template into a unicode string."""
            template_name, args = args[0], args[1:]
        except IndexError:
            raise TypeError('name of template required')
        return self.get_template(template_name).render(*args, **kwargs)

class GenshiTemplateLoader(TemplateLoader):
    """A unified interface for loading Genshi templates. Actually a quite thin
    wrapper for Genshi's TemplateLoader.

    It sets some defaults that differ from the Genshi loader, most notably
    auto_reload is active. All imporant options can be passed through to
    The default output type is 'html', but can be adjusted easily by changing
    the `output_type` attribute.
    def __init__(self, search_path, encoding='utf-8', **kwargs):
        TemplateLoader.__init__(self, search_path, encoding)
        # import Genshi here, because we don't want a general Genshi
        # dependency, only a local one
        from genshi.template import TemplateLoader as GenshiLoader
        from genshi.template.loader import TemplateNotFound

        self.not_found_exception = TemplateNotFound
        # set auto_reload to True per default
        reload_template = kwargs.pop('auto_reload', True)
        # get rid of default_encoding as this template loaders overwrites it
        # with the value of encoding
        kwargs.pop('default_encoding', None)

        # now, all arguments are clean, pass them on
        self.loader = GenshiLoader(search_path, default_encoding=encoding,
                auto_reload=reload_template, **kwargs)

        # the default output is HTML but can be overridden easily
        self.output_type = 'html'
        self.encoding = encoding

    def get_template(self, template_name):
        """Get the template which is at the given name"""
            return self.loader.load(template_name, encoding=self.encoding)
        except self.not_found_exception, e:
            # catch the exception raised by Genshi, convert it into a werkzeug
            # exception (for the sake of consistency)
            raise TemplateNotFound(template_name)

    def render_to_string(self, template_name, context=None):
        """Load and render a template into an unicode string"""
        # create an empty context if no context was specified
        context = context or {}
        tmpl = self.get_template(template_name)
        # render the template into a unicode string (None means unicode)
        return tmpl. \
            generate(**context). \
            render(self.output_type, encoding=None)