Skip to content

Commit

Permalink
flask.g is now on the app context and not the request context
Browse files Browse the repository at this point in the history
  • Loading branch information
mitsuhiko committed Dec 21, 2012
1 parent 61d43c7 commit 1949c4a
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 20 deletions.
5 changes: 5 additions & 0 deletions CHANGES
Expand Up @@ -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
-----------
Expand Down
4 changes: 4 additions & 0 deletions docs/api.rst
Expand Up @@ -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.

Copy link
@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.

Copy link
@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.


Expand Down
7 changes: 7 additions & 0 deletions docs/upgrading.rst
Expand Up @@ -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
Expand Down
22 changes: 19 additions & 3 deletions flask/app.py
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
11 changes: 9 additions & 2 deletions flask/ctx.py
Expand Up @@ -17,7 +17,7 @@
from .module import blueprint_is_module


class _RequestGlobals(object):
class _AppCtxGlobals(object):
"""A plain object."""
pass

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down
16 changes: 12 additions & 4 deletions flask/globals.py
Expand Up @@ -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:
Expand All @@ -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'))
15 changes: 8 additions & 7 deletions flask/templating.py
Expand Up @@ -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):
Expand Down
6 changes: 3 additions & 3 deletions flask/testsuite/appctx.py
Expand Up @@ -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')

Expand Down
5 changes: 4 additions & 1 deletion flask/testsuite/basic.py
Expand Up @@ -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):
Expand Down

0 comments on commit 1949c4a

Please sign in to comment.