Browse Source

implement session.new for the default session

improve documentation for session attributes
add test for session attributes
pull/2579/head
David Lord 7 years ago
parent
commit
f658409ee3
No known key found for this signature in database
GPG Key ID: 7A1C87E3F5BC42A8
  1. 8
      CHANGES
  2. 68
      flask/sessions.py
  3. 10
      tests/test_basic.py

8
CHANGES

@ -3,6 +3,7 @@ Flask Changelog
Here you can see the full list of changes between each Flask release. Here you can see the full list of changes between each Flask release.
Version 0.13 Version 0.13
------------ ------------
@ -114,7 +115,9 @@ Major release, unreleased
depending on ``app.debug``. No handlers are removed, and a handler is only depending on ``app.debug``. No handlers are removed, and a handler is only
added if no handlers are already configured. (`#2436`_) added if no handlers are already configured. (`#2436`_)
- Blueprint view function name may not contain dots. (`#2450`_) - Blueprint view function name may not contain dots. (`#2450`_)
- The dev server now uses threads by default. - The dev server now uses threads by default. (`#2529`_)
- ``session.new`` is implemented for the default session implementation.
(`#2579`_)
.. _pallets/meta#24: https://github.com/pallets/meta/issues/24 .. _pallets/meta#24: https://github.com/pallets/meta/issues/24
.. _#1421: https://github.com/pallets/flask/issues/1421 .. _#1421: https://github.com/pallets/flask/issues/1421
@ -149,6 +152,9 @@ Major release, unreleased
.. _#2430: https://github.com/pallets/flask/pull/2430 .. _#2430: https://github.com/pallets/flask/pull/2430
.. _#2436: https://github.com/pallets/flask/pull/2436 .. _#2436: https://github.com/pallets/flask/pull/2436
.. _#2450: https://github.com/pallets/flask/pull/2450 .. _#2450: https://github.com/pallets/flask/pull/2450
.. _#2529: https://github.com/pallets/flask/pull/2529
.. _#2579: https://github.com/pallets/flask/pull/2579
Version 0.12.3 Version 0.12.3
-------------- --------------

68
flask/sessions.py

@ -10,6 +10,7 @@
""" """
import hashlib import hashlib
import warnings import warnings
from collections import MutableMapping
from datetime import datetime from datetime import datetime
from itsdangerous import BadSignature, URLSafeTimedSerializer from itsdangerous import BadSignature, URLSafeTimedSerializer
@ -19,52 +20,67 @@ from flask.helpers import is_ip, total_seconds
from flask.json.tag import TaggedJSONSerializer from flask.json.tag import TaggedJSONSerializer
class SessionMixin(object): class SessionMixin(MutableMapping):
"""Expands a basic dictionary with an accessors that are expected """Expands a basic dictionary with session attributes."""
by Flask extensions and users for the session.
"""
def _get_permanent(self): @property
def permanent(self):
"""This reflects the ``'_permanent'`` key in the dict."""
return self.get('_permanent', False) return self.get('_permanent', False)
def _set_permanent(self, value): @permanent.setter
def permanent(self, value):
self['_permanent'] = bool(value) self['_permanent'] = bool(value)
#: this reflects the ``'_permanent'`` key in the dict. #: Some implementations can track whether a session is new, but
permanent = property(_get_permanent, _set_permanent) #: that is not guaranteed. Use with caution. The mixin default is
del _get_permanent, _set_permanent #: hard-coded ``False``.
#: some session backends can tell you if a session is new, but that is
#: not necessarily guaranteed. Use with caution. The default mixin
#: implementation just hardcodes ``False`` in.
new = False new = False
#: for some backends this will always be ``True``, but some backends will #: Some implementations can detect changes to the session and set
#: default this to false and detect changes in the dictionary for as #: this when that happens. The mixin default is hard coded to
#: long as changes do not happen on mutable structures in the session. #: ``True``.
#: The default mixin implementation just hardcodes ``True`` in.
modified = True modified = True
#: the accessed variable indicates whether or not the session object has #: Some implementations can detect when session data is read or
#: been accessed in that request. This allows flask to append a `Vary: #: written and set this when that happens. The mixin default is hard
#: Cookie` header to the response if the session is being accessed. This #: coded to ``True``.
#: allows caching proxy servers, like Varnish, to use both the URL and the
#: session cookie as keys when caching pages, preventing multiple users
#: from being served the same cache.
accessed = True accessed = True
class SecureCookieSession(CallbackDict, SessionMixin): class SecureCookieSession(CallbackDict, SessionMixin):
"""Base class for sessions based on signed cookies.""" """Base class for sessions based on signed cookies.
This session backend will set the :attr:`new`, :attr:`modified`,
and :attr:`accessed` attributes.
"""
#: The session is considered "new" if there was no session data
#: loaded. This means that the session will be new until there is
#: data in the session, because no cookie will be written otherwise.
new = True
#: When data is changed, this is set to ``True``. Only the session
#: dictionary itself is tracked; if the session contains mutable
#: data (for example a nested dict) then this must be set to
#: ``True`` manually when modifying that data. The session cookie
#: will only be written to the response if this is ``True``.
modified = False
#: When data is read or written, this is set to ``True``. Used by
# :class:`.SecureCookieSessionInterface` to add a ``Vary: Cookie``
#: header, which allows caching proxies to cache different pages for
#: different users.
accessed = False
def __init__(self, initial=None): def __init__(self, initial=None):
self.new = initial is None
def on_update(self): def on_update(self):
self.modified = True self.modified = True
self.accessed = True self.accessed = True
super(SecureCookieSession, self).__init__(initial, on_update) super(SecureCookieSession, self).__init__(initial, on_update)
self.modified = False
self.accessed = False
def __getitem__(self, key): def __getitem__(self, key):
self.accessed = True self.accessed = True

10
tests/test_basic.py

@ -17,6 +17,7 @@ from threading import Thread
import pytest import pytest
import werkzeug.serving import werkzeug.serving
from werkzeug.datastructures import MultiDict
from werkzeug.exceptions import BadRequest, Forbidden, NotFound from werkzeug.exceptions import BadRequest, Forbidden, NotFound
from werkzeug.http import parse_date from werkzeug.http import parse_date
from werkzeug.routing import BuildError from werkzeug.routing import BuildError
@ -221,13 +222,20 @@ def test_endpoint_decorator(app, client):
def test_session(app, client): def test_session(app, client):
@app.route('/set', methods=['POST']) @app.route('/set', methods=['POST'])
def set(): def set():
assert not flask.session.modified
flask.session['value'] = flask.request.form['value'] flask.session['value'] = flask.request.form['value']
assert flask.session.modified
return 'value set' return 'value set'
@app.route('/get') @app.route('/get')
def get(): def get():
return flask.session['value'] assert flask.session.new == ('new' in flask.request.args)
assert not flask.session.accessed
v = flask.session.get('value', 'None')
assert flask.session.accessed
return v
client.get('/get?new=1')
assert client.post('/set', data={'value': '42'}).data == b'value set' assert client.post('/set', data={'value': '42'}).data == b'value set'
assert client.get('/get').data == b'42' assert client.get('/get').data == b'42'

Loading…
Cancel
Save