From: Christopher Allan Webber Date: Sat, 29 Nov 2014 19:56:41 +0000 (-0600) Subject: Add context generator: first step towards removing globals from the application X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=d71499004fdf4800648f4106faead962f3e6b38c;p=mediagoblin.git Add context generator: first step towards removing globals from the application This allows you to generate a "context" object that gets threaded throughout the application... this object should keep track of the same things that currently we use global variables for. --- diff --git a/mediagoblin/app.py b/mediagoblin/app.py index 9014bf47..8b231163 100644 --- a/mediagoblin/app.py +++ b/mediagoblin/app.py @@ -46,6 +46,19 @@ from mediagoblin.auth.tools import check_auth_enabled, no_auth_logout _log = logging.getLogger(__name__) +class Context(object): + """ + MediaGoblin context object. + + If a web request is being used, a Flask Request object is used + instead, otherwise (celery tasks, etc), attach things to this + object. + + Usually appears as "ctx" in utilities as first argument. + """ + pass + + class MediaGoblinApp(object): """ WSGI application of MediaGoblin @@ -149,67 +162,104 @@ class MediaGoblinApp(object): self.meddleware = [common.import_component(m)(self) for m in meddleware.ENABLED_MEDDLEWARE] - def call_backend(self, environ, start_response): - request = Request(environ) - - # Compatibility with django, use request.args preferrably - request.GET = request.args + def gen_context(self, ctx=None): + """ + Attach contextual information to request, or generate a context object - ## Routing / controller loading stuff - map_adapter = self.url_map.bind_to_environ(request.environ) + This avoids global variables; various utilities and contextual + information (current translation, etc) are attached to this + object. + """ + # Set up context + # -------------- - # By using fcgi, mediagoblin can run under a base path - # like /mediagoblin/. request.path_info contains the - # path inside mediagoblin. If the something needs the - # full path of the current page, that should include - # the basepath. - # Note: urlgen and routes are fine! - request.full_path = environ["SCRIPT_NAME"] + request.path - # python-routes uses SCRIPT_NAME. So let's use that too. - # The other option would be: - # request.full_path = environ["SCRIPT_URL"] + # Is a context provided? + if ctx is not None: + # Do special things if this is a request + if isinstance(ctx, Request): + ctx = self._request_only_gen_context(ctx) - # Fix up environ for urlgen - # See bug: https://bitbucket.org/bbangert/routes/issue/55/cache_hostinfo-breaks-on-https-off - if environ.get('HTTPS', '').lower() == 'off': - environ.pop('HTTPS') + else: + ctx = Context() + + # Attach utilities + # ---------------- - ## Attach utilities to the request object - # Do we really want to load this via middleware? Maybe? - session_manager = self.session_manager - request.session = session_manager.load_session_from_cookie(request) # Attach self as request.app # Also attach a few utilities from request.app for convenience? - request.app = self + ctx.app = self - request.db = self.db - request.staticdirect = self.staticdirector + ctx.db = self.db + ctx.staticdirect = self.staticdirector + + return ctx + + def _request_only_gen_context(self, request): + """ + Requests get some extra stuff attached to them that's not relevant + otherwise. + """ + # Do we really want to load this via middleware? Maybe? + request.session = self.session_manager.load_session_from_cookie(request) request.locale = translate.get_locale_from_request(request) + + # This should be moved over for certain, but how to deal with + # request.locale? request.template_env = template.get_jinja_env( self.template_loader, request.locale) + mg_request.setup_user_in_request(request) + + ## Routing / controller loading stuff + request.map_adapter = self.url_map.bind_to_environ(request.environ) + def build_proxy(endpoint, **kw): try: qualified = kw.pop('qualified') except KeyError: qualified = False - return map_adapter.build( + return request.map_adapter.build( endpoint, values=dict(**kw), force_external=qualified) request.urlgen = build_proxy + return request + + def call_backend(self, environ, start_response): + request = Request(environ) + + # Compatibility with django, use request.args preferrably + request.GET = request.args + + # By using fcgi, mediagoblin can run under a base path + # like /mediagoblin/. request.path_info contains the + # path inside mediagoblin. If the something needs the + # full path of the current page, that should include + # the basepath. + # Note: urlgen and routes are fine! + request.full_path = environ["SCRIPT_NAME"] + request.path + # python-routes uses SCRIPT_NAME. So let's use that too. + # The other option would be: + # request.full_path = environ["SCRIPT_URL"] + + # Fix up environ for urlgen + # See bug: https://bitbucket.org/bbangert/routes/issue/55/cache_hostinfo-breaks-on-https-off + if environ.get('HTTPS', '').lower() == 'off': + environ.pop('HTTPS') + + ## Attach utilities to the request object + request = self.gen_context(request) + # Log user out if authentication_disabled no_auth_logout(request) - mg_request.setup_user_in_request(request) - request.controller_name = None try: - found_rule, url_values = map_adapter.match(return_rule=True) + found_rule, url_values = request.map_adapter.match(return_rule=True) request.matchdict = url_values except RequestRedirect as response: # Deal with 301 responses eg due to missing final slash @@ -225,6 +275,7 @@ class MediaGoblinApp(object): # used for lazy context modification request.controller_name = found_rule.endpoint + ## TODO: get rid of meddleware, turn it into hooks only # pass the request through our meddleware classes try: for m in self.meddleware: @@ -255,8 +306,9 @@ class MediaGoblinApp(object): response = render_http_exception( request, e, e.get_description(environ)) - session_manager.save_session_to_cookie(request.session, - request, response) + self.session_manager.save_session_to_cookie( + request.session, + request, response) return response(environ, start_response)