Browse Source

Added support for long running sessions. This closes #16.

pull/1638/head
Armin Ronacher 15 years ago
parent
commit
36717b0273
  1. 2
      Makefile
  2. 7
      docs/api.rst
  3. 31
      flask.py
  4. 27
      tests/flask_tests.py

2
Makefile

@ -1,4 +1,4 @@
.PHONY: clean-pyc test .PHONY: clean-pyc test upload-docs
all: clean-pyc test all: clean-pyc test

7
docs/api.rst

@ -174,6 +174,13 @@ To access the current session you can use the :class:`session` object:
# so mark it as modified yourself # so mark it as modified yourself
session.modified = True session.modified = True
.. attribute:: permanent
If set to `True` the session life for
:attr:`~flask.Flask.permanent_session_lifetime` seconds. The
default is 31 days. If set to `False` (which is the default) the
session will be deleted when the user closes the browser.
Application Globals Application Globals
------------------- -------------------

31
flask.py

@ -13,6 +13,7 @@ from __future__ import with_statement
import os import os
import sys import sys
import types import types
from datetime import datetime, timedelta
from jinja2 import Environment, PackageLoader, FileSystemLoader from jinja2 import Environment, PackageLoader, FileSystemLoader
from werkzeug import Request as RequestBase, Response as ResponseBase, \ from werkzeug import Request as RequestBase, Response as ResponseBase, \
@ -86,7 +87,20 @@ class _RequestGlobals(object):
pass pass
class _NullSession(SecureCookie): class Session(SecureCookie):
"""Expands the session for support for switching between permanent
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 """Class used to generate nicer error messages if sessions are not
available. Will still allow read-only access to the empty session available. Will still allow read-only access to the empty session
but fail on setting. but fail on setting.
@ -317,6 +331,11 @@ class Flask(object):
#: The secure cookie uses this for the name of the session cookie #: The secure cookie uses this for the name of the session cookie
session_cookie_name = 'session' session_cookie_name = 'session'
#: A :class:`~datetime.timedelta` which is used to set the expiration
#: date of a permanent session. The default is 31 days which makes a
#: permanent session survive for roughly one month.
permanent_session_lifetime = timedelta(days=31)
#: options that are passed directly to the Jinja2 environment #: options that are passed directly to the Jinja2 environment
jinja_options = ImmutableDict( jinja_options = ImmutableDict(
autoescape=True, autoescape=True,
@ -493,8 +512,8 @@ class Flask(object):
""" """
key = self.secret_key key = self.secret_key
if key is not None: if key is not None:
return SecureCookie.load_cookie(request, self.session_cookie_name, return Session.load_cookie(request, self.session_cookie_name,
secret_key=key) 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
@ -505,7 +524,11 @@ class Flask(object):
object) object)
:param response: an instance of :attr:`response_class` :param response: an instance of :attr:`response_class`
""" """
session.save_cookie(response, self.session_cookie_name) expires = None
if session.permanent:
expires = datetime.utcnow() + self.permanent_session_lifetime
session.save_cookie(response, self.session_cookie_name,
expires=expires, httponly=True)
def add_url_rule(self, rule, endpoint, view_func=None, **options): def add_url_rule(self, rule, endpoint, view_func=None, **options):
"""Connects a URL rule. Works exactly like the :meth:`route` """Connects a URL rule. Works exactly like the :meth:`route`

27
tests/flask_tests.py

@ -11,11 +11,14 @@
""" """
from __future__ import with_statement from __future__ import with_statement
import os import os
import re
import sys import sys
import flask import flask
import unittest import unittest
import tempfile import tempfile
import warnings import warnings
from datetime import datetime
from werkzeug import parse_date
example_path = os.path.join(os.path.dirname(__file__), '..', 'examples') example_path = os.path.join(os.path.dirname(__file__), '..', 'examples')
@ -118,6 +121,30 @@ class BasicFunctionalityTestCase(unittest.TestCase):
expect_exception(flask.session.__setitem__, 'foo', 42) expect_exception(flask.session.__setitem__, 'foo', 42)
expect_exception(flask.session.pop, 'foo') expect_exception(flask.session.pop, 'foo')
def test_session_expiration(self):
permanent = True
app = flask.Flask(__name__)
app.secret_key = 'testkey'
@app.route('/')
def index():
flask.session['test'] = 42
flask.session.permanent = permanent
return ''
rv = app.test_client().get('/')
assert 'set-cookie' in rv.headers
match = re.search(r'\bexpires=([^;]+)', rv.headers['set-cookie'])
expires = parse_date(match.group())
expected = datetime.utcnow() + app.permanent_session_lifetime
assert expires.year == expected.year
assert expires.month == expected.month
assert expires.day == expected.day
permanent = False
rv = app.test_client().get('/')
assert 'set-cookie' in rv.headers
match = re.search(r'\bexpires=([^;]+)', rv.headers['set-cookie'])
assert match is None
def test_flashes(self): def test_flashes(self):
app = flask.Flask(__name__) app = flask.Flask(__name__)
app.secret_key = 'testkey' app.secret_key = 'testkey'

Loading…
Cancel
Save