terom@92: from werkzeug.wrappers import Request, Response terom@92: from werkzeug.exceptions import ( terom@92: HTTPException, terom@92: BadRequest, # 400 terom@92: NotFound, # 404 terom@92: ) terom@92: from werkzeug.utils import redirect terom@92: terom@92: class Application: terom@92: URLS = None terom@92: terom@92: def __init__ (self, urls=None): terom@92: """ terom@92: urls - werkzeug.routing.Map -> Handler terom@92: """ terom@92: terom@92: if not urls: terom@92: urls = self.URLS terom@92: terom@92: if not urls: terom@92: raise ValueError("Required URLS/urls=...") terom@92: terom@92: self.urls = urls terom@92: terom@103: def lookup (self, request): terom@92: """ terom@103: Lookup Request -> Handler, params terom@92: """ terom@92: terom@92: # bind to request terom@92: urls = self.urls.bind_to_environ(request) terom@92: terom@92: # lookup terom@92: handler, params = urls.match() terom@92: terom@92: # handler instance terom@92: handler = handler(self, request, urls) terom@103: terom@103: return handler, params terom@103: terom@103: def respond (self, request): terom@103: """ terom@103: Process Request -> Response, using lookup() terom@103: """ terom@103: terom@103: handler, params = self.lookup(request) terom@92: terom@92: try : terom@92: handler.init() terom@92: terom@92: # apply terom@92: return handler.respond(**params) terom@92: terom@92: finally : terom@92: handler.cleanup() terom@92: terom@92: @Request.application terom@92: def __call__ (self, request) : terom@92: """ terom@92: WSGI entry point, werkzeug Request -> Response terom@92: """ terom@92: terom@92: try : terom@92: return self.respond(request) terom@92: terom@92: except HTTPException as ex : terom@92: return ex terom@92: terom@92: class Handler (object) : terom@92: """ terom@92: Per-Request controller/view, containing the request context and generating the response. terom@92: """ terom@92: terom@92: # werkzeug defaults to UTF-8 terom@92: MIMETYPE = 'text/html' terom@92: terom@92: def __init__ (self, app, request, urls) : terom@92: """ terom@92: app - wsgi.Application terom@92: request - werkzeug.Request terom@92: urls - werkzeug.routing.Map.bind_to_environ() terom@92: """ terom@92: terom@92: self.app = app terom@92: self.request = request terom@92: self.urls = urls terom@92: terom@92: def url (self, handler=None, **params) : terom@92: """ terom@92: Return an URL for given endpoint, with parameters, terom@92: """ terom@92: terom@92: if not handler : terom@92: handler = self.__class__ terom@92: terom@92: return self.urls.build(handler, params) terom@92: terom@92: ## processing stages terom@92: def init (self) : terom@92: """ terom@92: Initialize on request start. terom@92: """ terom@92: terom@92: pass terom@92: terom@92: def process (self, **params) : terom@92: """ terom@92: Process request args to build internal request state. terom@92: terom@92: May optionally return a Response, to e.g. redirect after POST. terom@92: """ terom@92: terom@92: pass terom@92: terom@92: def render (self) : terom@92: """ terom@92: Render response. terom@92: """ terom@92: terom@92: raise NotImplementedError() terom@92: terom@109: def status (self): terom@109: return 200 terom@109: terom@109: def headers (self): terom@109: return [ ] terom@109: terom@92: def mimetype (self): terom@92: return self.MIMETYPE terom@92: terom@92: terom@92: def respond (self, **params) : terom@92: """ terom@92: Generate a response, or raise an HTTPException terom@92: """ terom@92: terom@92: # returning e.g. redirect? terom@92: response = self.process(**params) terom@92: terom@92: if response : terom@92: return response terom@92: terom@109: return Response(self.render_response(), mimetype=self.mimetype(), status=self.status(), headers=self.headers()) terom@103: terom@92: def cleanup (self) : terom@92: """ terom@92: After request processing. Do not fail :) terom@92: """ terom@92: terom@92: pass terom@92: