Permalink
Browse files

flask.g is now on the app context and not the request context

  • Loading branch information...
1 parent 61d43c7 commit 1949c4a9abc174bf29620f6dd8ceab9ed3ace2eb @mitsuhiko mitsuhiko committed Dec 21, 2012
Showing with 71 additions and 20 deletions.
  1. +5 −0 CHANGES
  2. +4 −0 docs/api.rst
  3. +7 −0 docs/upgrading.rst
  4. +19 −3 flask/app.py
  5. +9 −2 flask/ctx.py
  6. +12 −4 flask/globals.py
  7. +8 −7 flask/templating.py
  8. +3 −3 flask/testsuite/appctx.py
  9. +4 −1 flask/testsuite/basic.py
View
5 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
-----------
View
4 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.
View
7 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
View
22 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
View
11 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.
View
16 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'))
View
15 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):
View
6 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')
View
5 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):

0 comments on commit 1949c4a

Please sign in to comment.