Permalink
Browse files

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

  • Loading branch information...
mitsuhiko committed Dec 21, 2012
1 parent 61d43c7 commit 1949c4a9abc174bf29620f6dd8ceab9ed3ace2eb
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
@@ -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
@@ -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

This comment has been minimized.

Show comment Hide comment
@kaos

kaos Jan 30, 2018

Just adding this section is not clear enough, as it now contradicts what was already documented.

We spent some time debugging a session-mixup-bug where we stored user data on the flask.g object, with really bad results.

@kaos

kaos Jan 30, 2018

Just adding this section is not clear enough, as it now contradicts what was already documented.

We spent some time debugging a session-mixup-bug where we stored user data on the flask.g object, with really bad results.

This comment has been minimized.

Show comment Hide comment
@davidism

davidism Jan 30, 2018

Member

Please don't leave notes on random commits, especially 5 year old ones. They are really annoying to track on GitHub.

Duplicate of #1151.

If you are going to open an issue, don't just say "this isn't enough" because the maintainers can't really act on that. What did you read, specifically, that was confusing? What did you expect to find instead?

@davidism

davidism Jan 30, 2018

Member

Please don't leave notes on random commits, especially 5 year old ones. They are really annoying to track on GitHub.

Duplicate of #1151.

If you are going to open an issue, don't just say "this isn't enough" because the maintainers can't really act on that. What did you read, specifically, that was confusing? What did you expect to find instead?

+ 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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):
@@ -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
@@ -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.