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 in less bytes being transmitted over the network. It's disabled by
default to not cause confusion with existing libraries that might expect default to not cause confusion with existing libraries that might expect
``flask.json.dumps`` to return bytestrings by default. ``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 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 Just store on this whatever you want. For example a database
connection or the user that is currently logged in. 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. 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 In order to not break people's sessions it is possible to continue using
the old session system by using the `Flask-OldSessions_` extension. 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/ .. _Flask-OldSessions: http://packages.python.org/Flask-OldSessions/
Version 0.9 Version 0.9

22
flask/app.py

@ -28,7 +28,7 @@ from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \
from . import json from . import json
from .wrappers import Request, Response from .wrappers import Request, Response
from .config import ConfigAttribute, Config 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 .globals import _request_ctx_stack, request
from .sessions import SecureCookieSessionInterface from .sessions import SecureCookieSessionInterface
from .module import blueprint_is_module from .module import blueprint_is_module
@ -157,8 +157,24 @@ class Flask(_PackageBoundObject):
#: 3. Return None instead of AttributeError on expected attributes. #: 3. Return None instead of AttributeError on expected attributes.
#: 4. Raise exception if an unexpected attr is set, a "controlled" flask.g. #: 4. Raise exception if an unexpected attr is set, a "controlled" flask.g.
#: #:
#: .. versionadded:: 0.9 #: In Flask 0.9 this property was called `request_globals_class` but it
request_globals_class = _RequestGlobals #: 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 #: The debug flag. Set this to `True` to enable debugging of the
#: application. In debug mode the debugger will kick in when an unhandled #: 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 from .module import blueprint_is_module
class _RequestGlobals(object): class _AppCtxGlobals(object):
"""A plain object.""" """A plain object."""
pass pass
@ -101,6 +101,7 @@ class AppContext(object):
def __init__(self, app): def __init__(self, app):
self.app = app self.app = app
self.url_adapter = app.create_url_adapter(None) 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 # Like request context, app contexts can be pushed multiple times
# but there a basic "refcount" is enough to track them. # but there a basic "refcount" is enough to track them.
@ -164,7 +165,6 @@ class RequestContext(object):
self.app = app self.app = app
self.request = app.request_class(environ) self.request = app.request_class(environ)
self.url_adapter = app.create_url_adapter(self.request) self.url_adapter = app.create_url_adapter(self.request)
self.g = app.request_globals_class()
self.flashes = None self.flashes = None
self.session = None self.session = None
@ -195,6 +195,13 @@ class RequestContext(object):
if bp is not None and blueprint_is_module(bp): if bp is not None and blueprint_is_module(bp):
self.request._is_old_module = True 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): def match_request(self):
"""Can be overridden by a subclass to hook into the matching """Can be overridden by a subclass to hook into the matching
of the request. of the request.

16
flask/globals.py

@ -13,13 +13,21 @@
from functools import partial from functools import partial
from werkzeug.local import LocalStack, LocalProxy from werkzeug.local import LocalStack, LocalProxy
def _lookup_object(name):
def _lookup_req_object(name):
top = _request_ctx_stack.top top = _request_ctx_stack.top
if top is None: if top is None:
raise RuntimeError('working outside of request context') raise RuntimeError('working outside of request context')
return getattr(top, name) 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(): def _find_app():
top = _app_ctx_stack.top top = _app_ctx_stack.top
if top is None: if top is None:
@ -31,6 +39,6 @@ def _find_app():
_request_ctx_stack = LocalStack() _request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack() _app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app) current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_object, 'request')) request = LocalProxy(partial(_lookup_req_object, 'request'))
session = LocalProxy(partial(_lookup_object, 'session')) session = LocalProxy(partial(_lookup_req_object, 'session'))
g = LocalProxy(partial(_lookup_object, 'g')) g = LocalProxy(partial(_lookup_app_object, 'g'))

15
flask/templating.py

@ -22,13 +22,14 @@ def _default_template_ctx_processor():
`session` and `g`. `session` and `g`.
""" """
reqctx = _request_ctx_stack.top reqctx = _request_ctx_stack.top
if reqctx is None: appctx = _app_ctx_stack.top
return {} rv = {}
return dict( if appctx is not None:
request=reqctx.request, rv['g'] = appctx.g
session=reqctx.session, if reqctx is not None:
g=reqctx.g rv['request'] = reqctx.request
) rv['session'] = reqctx.session
return rv
class Environment(BaseEnvironment): class Environment(BaseEnvironment):

6
flask/testsuite/appctx.py

@ -65,13 +65,13 @@ class AppContextTestCase(FlaskTestCase):
self.assert_equal(cleanup_stuff, [None]) self.assert_equal(cleanup_stuff, [None])
def test_custom_request_globals_class(self): def test_custom_app_ctx_globals_class(self):
class CustomRequestGlobals(object): class CustomRequestGlobals(object):
def __init__(self): def __init__(self):
self.spam = 'eggs' self.spam = 'eggs'
app = flask.Flask(__name__) app = flask.Flask(__name__)
app.request_globals_class = CustomRequestGlobals app.app_ctx_globals_class = CustomRequestGlobals
with app.test_request_context(): with app.app_context():
self.assert_equal( self.assert_equal(
flask.render_template_string('{{ g.spam }}'), 'eggs') flask.render_template_string('{{ g.spam }}'), 'eggs')

5
flask/testsuite/basic.py

@ -1104,8 +1104,11 @@ class BasicFunctionalityTestCase(FlaskTestCase):
c.get('/fail') c.get('/fail')
self.assert_(flask._request_ctx_stack.top is not None) 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._request_ctx_stack.top is None)
self.assert_(flask._app_ctx_stack.top is None)
class ContextTestCase(FlaskTestCase): class ContextTestCase(FlaskTestCase):

Loading…
Cancel
Save