From 1949c4a9abc174bf29620f6dd8ceab9ed3ace2eb Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 21 Dec 2012 11:45:42 +0100 Subject: [PATCH] flask.g is now on the app context and not the request context --- CHANGES | 5 +++++ docs/api.rst | 4 ++++ docs/upgrading.rst | 7 +++++++ flask/app.py | 22 +++++++++++++++++++--- flask/ctx.py | 11 +++++++++-- flask/globals.py | 16 ++++++++++++---- flask/templating.py | 15 ++++++++------- flask/testsuite/appctx.py | 6 +++--- flask/testsuite/basic.py | 5 ++++- 9 files changed, 71 insertions(+), 20 deletions(-) diff --git a/CHANGES b/CHANGES index 6a1ee7c726..ddba0928b4 100644 --- a/CHANGES +++ b/CHANGES @@ -34,6 +34,11 @@ Release date to be decided. in less bytes being transmitted over the network. It's disabled by default to not cause confusion with existing libraries that might expect ``flask.json.dumps`` to return bytestrings by default. +- ``flask.g`` is now stored on the app context instead of the request + context. +- ``flask.Flask.request_globals_class`` got renamed to + ``flask.Flask.app_ctx_globals_class`` which is a better name to what it + does since 0.10. Version 0.9 ----------- diff --git a/docs/api.rst b/docs/api.rst index dbd1877e26..12e4b96610 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -258,6 +258,10 @@ thing, like it does for :class:`request` and :class:`session`. Just store on this whatever you want. For example a database connection or the user that is currently logged in. + Starting with Flask 0.10 this is stored on the application context and + no longer on the request context which means it becomes available if + only the application context is bound and not yet a request. + This is a proxy. See :ref:`notes-on-proxies` for more information. diff --git a/docs/upgrading.rst b/docs/upgrading.rst index 1f67df051d..1d9239f59f 100644 --- a/docs/upgrading.rst +++ b/docs/upgrading.rst @@ -36,6 +36,13 @@ extensions for tuples and strings with HTML markup. In order to not break people's sessions it is possible to continue using the old session system by using the `Flask-OldSessions_` extension. +Flask also started storing the :data:`flask.g` object on the application +context instead of the request context. This change should be transparent +for you but it means that you now can store things on the ``g`` object +when there is no request context yet but an application context. The old +``flask.Flask.request_globals_class`` attribute was renamed to +:attr:`flask.Flask.app_ctx_globals_class`. + .. _Flask-OldSessions: http://packages.python.org/Flask-OldSessions/ Version 0.9 diff --git a/flask/app.py b/flask/app.py index 9d291b3aec..902c2ba875 100644 --- a/flask/app.py +++ b/flask/app.py @@ -28,7 +28,7 @@ from . import json from .wrappers import Request, Response from .config import ConfigAttribute, Config -from .ctx import RequestContext, AppContext, _RequestGlobals +from .ctx import RequestContext, AppContext, _AppCtxGlobals from .globals import _request_ctx_stack, request from .sessions import SecureCookieSessionInterface from .module import blueprint_is_module @@ -157,8 +157,24 @@ class Flask(_PackageBoundObject): #: 3. Return None instead of AttributeError on expected attributes. #: 4. Raise exception if an unexpected attr is set, a "controlled" flask.g. #: - #: .. versionadded:: 0.9 - request_globals_class = _RequestGlobals + #: In Flask 0.9 this property was called `request_globals_class` but it + #: was changed in 0.10 to :attr:`app_ctx_globals_class` because the + #: flask.g object is not application context scoped. + #: + #: .. versionadded:: 0.10 + app_ctx_globals_class = _AppCtxGlobals + + # Backwards compatibility support + def _get_request_globals_class(self): + return self.app_ctx_globals_class + def _set_request_globals_class(self, value): + from warnings import warn + warn(DeprecationWarning('request_globals_class attribute is now ' + 'called app_ctx_globals_class')) + self.app_ctx_globals_class = value + request_globals_class = property(_get_request_globals_class, + _set_request_globals_class) + del _get_request_globals_class, _set_request_globals_class #: The debug flag. Set this to `True` to enable debugging of the #: application. In debug mode the debugger will kick in when an unhandled diff --git a/flask/ctx.py b/flask/ctx.py index 84e96575ec..a1d9af6248 100644 --- a/flask/ctx.py +++ b/flask/ctx.py @@ -17,7 +17,7 @@ from .module import blueprint_is_module -class _RequestGlobals(object): +class _AppCtxGlobals(object): """A plain object.""" pass @@ -101,6 +101,7 @@ class AppContext(object): def __init__(self, app): self.app = app self.url_adapter = app.create_url_adapter(None) + self.g = app.app_ctx_globals_class() # Like request context, app contexts can be pushed multiple times # but there a basic "refcount" is enough to track them. @@ -164,7 +165,6 @@ def __init__(self, app, environ): self.app = app self.request = app.request_class(environ) self.url_adapter = app.create_url_adapter(self.request) - self.g = app.request_globals_class() self.flashes = None self.session = None @@ -195,6 +195,13 @@ def __init__(self, app, environ): if bp is not None and blueprint_is_module(bp): self.request._is_old_module = True + def _get_g(self): + return _app_ctx_stack.top.g + def _set_g(self, value): + _app_ctx_stack.top.g = value + g = property(_get_g, _set_g) + del _get_g, _set_g + def match_request(self): """Can be overridden by a subclass to hook into the matching of the request. diff --git a/flask/globals.py b/flask/globals.py index f6d6248537..67d41f5c10 100644 --- a/flask/globals.py +++ b/flask/globals.py @@ -13,13 +13,21 @@ from functools import partial from werkzeug.local import LocalStack, LocalProxy -def _lookup_object(name): + +def _lookup_req_object(name): top = _request_ctx_stack.top if top is None: raise RuntimeError('working outside of request context') return getattr(top, name) +def _lookup_app_object(name): + top = _app_ctx_stack.top + if top is None: + raise RuntimeError('working outside of application context') + return getattr(top, name) + + def _find_app(): top = _app_ctx_stack.top if top is None: @@ -31,6 +39,6 @@ def _find_app(): _request_ctx_stack = LocalStack() _app_ctx_stack = LocalStack() current_app = LocalProxy(_find_app) -request = LocalProxy(partial(_lookup_object, 'request')) -session = LocalProxy(partial(_lookup_object, 'session')) -g = LocalProxy(partial(_lookup_object, 'g')) +request = LocalProxy(partial(_lookup_req_object, 'request')) +session = LocalProxy(partial(_lookup_req_object, 'session')) +g = LocalProxy(partial(_lookup_app_object, 'g')) diff --git a/flask/templating.py b/flask/templating.py index 2bac22e987..2cc09c4d63 100644 --- a/flask/templating.py +++ b/flask/templating.py @@ -22,13 +22,14 @@ def _default_template_ctx_processor(): `session` and `g`. """ reqctx = _request_ctx_stack.top - if reqctx is None: - return {} - return dict( - request=reqctx.request, - session=reqctx.session, - g=reqctx.g - ) + appctx = _app_ctx_stack.top + rv = {} + if appctx is not None: + rv['g'] = appctx.g + if reqctx is not None: + rv['request'] = reqctx.request + rv['session'] = reqctx.session + return rv class Environment(BaseEnvironment): diff --git a/flask/testsuite/appctx.py b/flask/testsuite/appctx.py index 6454389e8b..aa71e11e77 100644 --- a/flask/testsuite/appctx.py +++ b/flask/testsuite/appctx.py @@ -65,13 +65,13 @@ def cleanup(exception): self.assert_equal(cleanup_stuff, [None]) - def test_custom_request_globals_class(self): + def test_custom_app_ctx_globals_class(self): class CustomRequestGlobals(object): def __init__(self): self.spam = 'eggs' app = flask.Flask(__name__) - app.request_globals_class = CustomRequestGlobals - with app.test_request_context(): + app.app_ctx_globals_class = CustomRequestGlobals + with app.app_context(): self.assert_equal( flask.render_template_string('{{ g.spam }}'), 'eggs') diff --git a/flask/testsuite/basic.py b/flask/testsuite/basic.py index 964d2c1865..efee244a94 100644 --- a/flask/testsuite/basic.py +++ b/flask/testsuite/basic.py @@ -1104,8 +1104,11 @@ def fail_func(): c.get('/fail') self.assert_(flask._request_ctx_stack.top is not None) - flask._request_ctx_stack.pop() + self.assert_(flask._app_ctx_stack.top is not None) + # implicit appctx disappears too + flask._request_ctx_stack.top.pop() self.assert_(flask._request_ctx_stack.top is None) + self.assert_(flask._app_ctx_stack.top is None) class ContextTestCase(FlaskTestCase):