Added optional kwargs to get_expiry_age/date.

This change allows for cleaner tests: we can test the exact output.

Refs #18194: this change makes it possible to compute session expiry
dates at times other than when the session is saved.

Fixed #18458: the existence of the `modification` kwarg implies that you
must pass it to get_expiry_age/date if you call these functions outside
of a short request - response cycle (the intended use case).
This commit is contained in:
Aymeric Augustin 2012-10-27 21:32:50 +02:00
parent fc2681b22b
commit cd17a24083
4 changed files with 74 additions and 27 deletions

View File

@ -170,28 +170,52 @@ class SessionBase(object):
_session = property(_get_session) _session = property(_get_session)
def get_expiry_age(self, expiry=None): def get_expiry_age(self, **kwargs):
"""Get the number of seconds until the session expires. """Get the number of seconds until the session expires.
expiry is an optional parameter specifying the datetime of expiry. Optionally, this function accepts `modification` and `expiry` keyword
arguments specifying the modification and expiry of the session.
""" """
if expiry is None: try:
modification = kwargs['modification']
except KeyError:
modification = timezone.now()
# Make the difference between "expiry=None passed in kwargs" and
# "expiry not passed in kwargs", in order to guarantee not to trigger
# self.load() when expiry is provided.
try:
expiry = kwargs['expiry']
except KeyError:
expiry = self.get('_session_expiry') expiry = self.get('_session_expiry')
if not expiry: # Checks both None and 0 cases if not expiry: # Checks both None and 0 cases
return settings.SESSION_COOKIE_AGE return settings.SESSION_COOKIE_AGE
if not isinstance(expiry, datetime): if not isinstance(expiry, datetime):
return expiry return expiry
delta = expiry - timezone.now() delta = expiry - modification
return delta.days * 86400 + delta.seconds return delta.days * 86400 + delta.seconds
def get_expiry_date(self): def get_expiry_date(self, **kwargs):
"""Get session the expiry date (as a datetime object).""" """Get session the expiry date (as a datetime object).
Optionally, this function accepts `modification` and `expiry` keyword
arguments specifying the modification and expiry of the session.
"""
try:
modification = kwargs['modification']
except KeyError:
modification = timezone.now()
# Same comment as in get_expiry_age
try:
expiry = kwargs['expiry']
except KeyError:
expiry = self.get('_session_expiry') expiry = self.get('_session_expiry')
if isinstance(expiry, datetime): if isinstance(expiry, datetime):
return expiry return expiry
if not expiry: # Checks both None and 0 cases if not expiry: # Checks both None and 0 cases
expiry = settings.SESSION_COOKIE_AGE expiry = settings.SESSION_COOKIE_AGE
return timezone.now() + timedelta(seconds=expiry) return modification + timedelta(seconds=expiry)
def set_expiry(self, value): def set_expiry(self, value):
""" """

View File

@ -40,7 +40,7 @@ class SessionStore(DBStore):
) )
data = self.decode(s.session_data) data = self.decode(s.session_data)
cache.set(self.cache_key, data, cache.set(self.cache_key, data,
self.get_expiry_age(s.expire_date)) self.get_expiry_age(expiry=s.expire_date))
except (Session.DoesNotExist, SuspiciousOperation): except (Session.DoesNotExist, SuspiciousOperation):
self.create() self.create()
data = {} data = {}

View File

@ -197,31 +197,43 @@ class SessionTestsMixin(object):
self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE) self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE)
def test_custom_expiry_seconds(self): def test_custom_expiry_seconds(self):
# Using seconds modification = timezone.now()
self.session.set_expiry(10)
delta = self.session.get_expiry_date() - timezone.now()
self.assertIn(delta.seconds, (9, 10))
age = self.session.get_expiry_age() self.session.set_expiry(10)
self.assertIn(age, (9, 10))
date = self.session.get_expiry_date(modification=modification)
self.assertEqual(date, modification + timedelta(seconds=10))
age = self.session.get_expiry_age(modification=modification)
self.assertEqual(age, 10)
def test_custom_expiry_timedelta(self): def test_custom_expiry_timedelta(self):
# Using timedelta modification = timezone.now()
self.session.set_expiry(timedelta(seconds=10))
delta = self.session.get_expiry_date() - timezone.now()
self.assertIn(delta.seconds, (9, 10))
age = self.session.get_expiry_age() # Mock timezone.now, because set_expiry calls it on this code path.
self.assertIn(age, (9, 10)) original_now = timezone.now
try:
timezone.now = lambda: modification
self.session.set_expiry(timedelta(seconds=10))
finally:
timezone.now = original_now
date = self.session.get_expiry_date(modification=modification)
self.assertEqual(date, modification + timedelta(seconds=10))
age = self.session.get_expiry_age(modification=modification)
self.assertEqual(age, 10)
def test_custom_expiry_datetime(self): def test_custom_expiry_datetime(self):
# Using fixed datetime modification = timezone.now()
self.session.set_expiry(timezone.now() + timedelta(seconds=10))
delta = self.session.get_expiry_date() - timezone.now()
self.assertIn(delta.seconds, (9, 10))
age = self.session.get_expiry_age() self.session.set_expiry(modification + timedelta(seconds=10))
self.assertIn(age, (9, 10))
date = self.session.get_expiry_date(modification=modification)
self.assertEqual(date, modification + timedelta(seconds=10))
age = self.session.get_expiry_age(modification=modification)
self.assertEqual(age, 10)
def test_custom_expiry_reset(self): def test_custom_expiry_reset(self):
self.session.set_expiry(None) self.session.set_expiry(None)

View File

@ -250,12 +250,23 @@ You can edit it multiple times.
with no custom expiration (or those set to expire at browser close), this with no custom expiration (or those set to expire at browser close), this
will equal :setting:`SESSION_COOKIE_AGE`. will equal :setting:`SESSION_COOKIE_AGE`.
This function accepts two optional keyword arguments:
- ``modification``: last modification of the session, as a
:class:`~datetime.datetime` object. Defaults to the current time.
- ``expiry``: expiry information for the session, as a
:class:`~datetime.datetime` object, an :class:`int` (in seconds), or
``None``. Defaults to the value stored in the session by
:meth:`set_expiry`, if there is one, or ``None``.
.. method:: get_expiry_date .. method:: get_expiry_date
Returns the date this session will expire. For sessions with no custom Returns the date this session will expire. For sessions with no custom
expiration (or those set to expire at browser close), this will equal the expiration (or those set to expire at browser close), this will equal the
date :setting:`SESSION_COOKIE_AGE` seconds from now. date :setting:`SESSION_COOKIE_AGE` seconds from now.
This function accepts the same keyword argumets as :meth:`get_expiry_age`.
.. method:: get_expire_at_browser_close .. method:: get_expire_at_browser_close
Returns either ``True`` or ``False``, depending on whether the user's Returns either ``True`` or ``False``, depending on whether the user's