Browse Source

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

pull/655/head
Armin Ronacher 12 years ago
parent
commit
1949c4a9ab
  1. 5
      CHANGES
  2. 4
      docs/api.rst
  3. 7
      docs/upgrading.rst
  4. 22
      flask/app.py
  5. 11
      flask/ctx.py
  6. 16
      flask/globals.py
  7. 15
      flask/templating.py
  8. 6
      flask/testsuite/appctx.py
  9. 5
      flask/testsuite/basic.py

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
-----------

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.

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

22
flask/app.py

@ -28,7 +28,7 @@ from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \
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

11
flask/ctx.py

@ -17,7 +17,7 @@ from .globals import _request_ctx_stack, _app_ctx_stack
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 @@ class RequestContext(object):
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 @@ class RequestContext(object):
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.

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'))

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):

6
flask/testsuite/appctx.py

@ -65,13 +65,13 @@ class AppContextTestCase(FlaskTestCase):
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')

5
flask/testsuite/basic.py

@ -1104,8 +1104,11 @@ class BasicFunctionalityTestCase(FlaskTestCase):
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):

Loading…
Cancel
Save