Browse Source

Refactored session interface

pull/271/merge
Armin Ronacher 13 years ago
parent
commit
0fccfe711f
  1. 4
      CHANGES
  2. 25
      docs/api.rst
  3. 1
      flask/__init__.py
  4. 38
      flask/app.py
  5. 2
      flask/ctx.py
  6. 38
      flask/session.py

4
CHANGES

@ -8,6 +8,10 @@ Version 0.8
Relase date to be decided, codename to be chosen. Relase date to be decided, codename to be chosen.
- Refactored session support into a session interface so that
the implementation of the sessions can be changed without
having to override the Flask class.
Version 0.7.2 Version 0.7.2
------------- -------------

25
docs/api.rst

@ -195,9 +195,34 @@ To access the current session you can use the :class:`session` object:
session will be deleted when the user closes the browser. session will be deleted when the user closes the browser.
Session Interface
-----------------
.. versionadded:: 0.7
The session interface provides a simple way to replace the session
implementation that Flask is using.
.. currentmodule:: flask.sessions
.. autoclass:: SessionInterface
:members:
.. autoclass:: SecureCookieSessionInterface
:members:
.. autoclass:: NullSession
:members:
.. autoclass:: SessionMixin
:members:
Application Globals Application Globals
------------------- -------------------
.. currentmodule:: flask
To share data that is valid for one request only from one function to To share data that is valid for one request only from one function to
another, a global variable is not good enough because it would break in another, a global variable is not good enough because it would break in
threaded environments. Flask provides you with a special object that threaded environments. Flask provides you with a special object that

1
flask/__init__.py

@ -27,7 +27,6 @@ from .ctx import has_request_context
from .module import Module from .module import Module
from .blueprints import Blueprint from .blueprints import Blueprint
from .templating import render_template, render_template_string from .templating import render_template, render_template_string
from .session import Session
# the signals # the signals
from .signals import signals_available, template_rendered, request_started, \ from .signals import signals_available, template_rendered, request_started, \

38
flask/app.py

@ -27,7 +27,7 @@ from .wrappers import Request, Response
from .config import ConfigAttribute, Config from .config import ConfigAttribute, Config
from .ctx import RequestContext from .ctx import RequestContext
from .globals import _request_ctx_stack, request from .globals import _request_ctx_stack, request
from .session import Session, _NullSession from .sessions import SecureCookieSessionInterface
from .module import blueprint_is_module from .module import blueprint_is_module
from .templating import DispatchingJinjaLoader, Environment, \ from .templating import DispatchingJinjaLoader, Environment, \
_default_template_ctx_processor _default_template_ctx_processor
@ -211,6 +211,12 @@ class Flask(_PackageBoundObject):
#: .. versionadded:: 0.7 #: .. versionadded:: 0.7
test_client_class = None test_client_class = None
#: the session interface to use. By default an instance of
#: :class:`~flask.sessions.SecureCookieSessionInterface` is used here.
#:
#: .. versionadded:: 0.7
session_interface = SecureCookieSessionInterface()
def __init__(self, import_name, static_path=None, static_url_path=None, def __init__(self, import_name, static_path=None, static_url_path=None,
static_folder='static', template_folder='templates'): static_folder='static', template_folder='templates'):
_PackageBoundObject.__init__(self, import_name, _PackageBoundObject.__init__(self, import_name,
@ -580,32 +586,32 @@ class Flask(_PackageBoundObject):
def open_session(self, request): def open_session(self, request):
"""Creates or opens a new session. Default implementation stores all """Creates or opens a new session. Default implementation stores all
session data in a signed cookie. This requires that the session data in a signed cookie. This requires that the
:attr:`secret_key` is set. :attr:`secret_key` is set. Instead of overriding this method
we recommend replacing the :class:`session_interface`.
:param request: an instance of :attr:`request_class`. :param request: an instance of :attr:`request_class`.
""" """
key = self.secret_key return self.session_interface.open_session(self, request)
if key is not None:
return Session.load_cookie(request, self.session_cookie_name,
secret_key=key)
def save_session(self, session, response): def save_session(self, session, response):
"""Saves the session if it needs updates. For the default """Saves the session if it needs updates. For the default
implementation, check :meth:`open_session`. implementation, check :meth:`open_session`. Instead of overriding this
method we recommend replacing the :class:`session_interface`.
:param session: the session to be saved (a :param session: the session to be saved (a
:class:`~werkzeug.contrib.securecookie.SecureCookie` :class:`~werkzeug.contrib.securecookie.SecureCookie`
object) object)
:param response: an instance of :attr:`response_class` :param response: an instance of :attr:`response_class`
""" """
expires = domain = None return self.session_interface.save_session(self, session, response)
if session.permanent:
expires = datetime.utcnow() + self.permanent_session_lifetime def make_null_session(self):
if self.config['SERVER_NAME'] is not None: """Creates a new instance of a missing session. Instead of overriding
# chop of the port which is usually not supported by browsers this method we recommend replacing the :class:`session_interface`.
domain = '.' + self.config['SERVER_NAME'].rsplit(':', 1)[0]
session.save_cookie(response, self.session_cookie_name, .. versionadded:: 0.7
expires=expires, httponly=True, domain=domain) """
return self.session_interface.make_null_session(self)
def register_module(self, module, **options): def register_module(self, module, **options):
"""Registers a module with this application. The keyword argument """Registers a module with this application. The keyword argument
@ -1184,7 +1190,7 @@ class Flask(_PackageBoundObject):
""" """
ctx = _request_ctx_stack.top ctx = _request_ctx_stack.top
bp = ctx.request.blueprint bp = ctx.request.blueprint
if not isinstance(ctx.session, _NullSession): if not self.session_interface.is_null_session(ctx.session):
self.save_session(ctx.session, response) self.save_session(ctx.session, response)
funcs = () funcs = ()
if bp is not None and bp in self.after_request_funcs: if bp is not None and bp in self.after_request_funcs:

2
flask/ctx.py

@ -122,7 +122,7 @@ class RequestContext(object):
# request context (e.g. flask-sqlalchemy). # request context (e.g. flask-sqlalchemy).
self.session = self.app.open_session(self.request) self.session = self.app.open_session(self.request)
if self.session is None: if self.session is None:
self.session = _NullSession() self.session = self.app.make_null_session()
def pop(self): def pop(self):
"""Pops the request context and unbinds it by doing that. This will """Pops the request context and unbinds it by doing that. This will

38
flask/session.py

@ -3,41 +3,17 @@
flask.session flask.session
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
Implements cookie based sessions based on Werkzeug's secure cookie This module used to flask with the session global so we moved it
system. over to flask.sessions
:copyright: (c) 2010 by Armin Ronacher. :copyright: (c) 2010 by Armin Ronacher.
:license: BSD, see LICENSE for more details. :license: BSD, see LICENSE for more details.
""" """
from werkzeug.contrib.securecookie import SecureCookie from warnings import warn
warn(DeprecationWarning('please use flask.sessions instead'))
from .sessions import *
class Session(SecureCookie): Session = SecureCookieSession
"""Expands the session with support for switching between permanent _NullSession = NullSession
and non-permanent sessions.
"""
def _get_permanent(self):
return self.get('_permanent', False)
def _set_permanent(self, value):
self['_permanent'] = bool(value)
permanent = property(_get_permanent, _set_permanent)
del _get_permanent, _set_permanent
class _NullSession(Session):
"""Class used to generate nicer error messages if sessions are not
available. Will still allow read-only access to the empty session
but fail on setting.
"""
def _fail(self, *args, **kwargs):
raise RuntimeError('the session is unavailable because no secret '
'key was set. Set the secret_key on the '
'application to something unique and secret.')
__setitem__ = __delitem__ = clear = pop = popitem = \
update = setdefault = _fail
del _fail

Loading…
Cancel
Save