diff options
Diffstat (limited to 'websdk/werkzeug/contrib/kickstart.py')
-rw-r--r-- | websdk/werkzeug/contrib/kickstart.py | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/websdk/werkzeug/contrib/kickstart.py b/websdk/werkzeug/contrib/kickstart.py new file mode 100644 index 0000000..43c0e7c --- /dev/null +++ b/websdk/werkzeug/contrib/kickstart.py @@ -0,0 +1,288 @@ +# -*- coding: utf-8 -*- +""" + werkzeug.contrib.kickstart + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + 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: http://genshi.edgewall.org/ + .. _Django: http://www.djangoproject.com/ + + :copyright: (c) 2011 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]) + else: + # create a new session + self.session = session_store.new() + + def url_for(self, callback, **values): + return self.url_adapter.build(callback, 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 + request.session_store.save_if_modified(request.session) + + # 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 + <http://dev.pocoo.org/projects/werkzeug/wiki/RequestResponseProcessor>`_ + """ + + 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 + self.name = 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: + self.store = session + else: + self.store = None + + def __call__(self, environ, start_response): + # create a request - with or without session support + if self.store is not None: + request = Request(environ, self.url_map, + session_store=self.store, cookie_name='%s_sid' % self.name) + else: + request = Request(environ, self.url_map) + + # apply the request processors + for processor in self.processors: + request = processor.process_request(request) + + try: + # find the callback to which the URL is mapped + callback, args = request.url_adapter.match(request.path) + except (HTTPException, RequestRedirect), e: + response = e + else: + # 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) + + try: + 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 + 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. + """ + self.store = 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) + 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.""" + try: + 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 + Genshi. + 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""" + try: + 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) |