[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/session/ svn merge
-r 26090:26417 branches/stub-session
Stuart Bishop
zen at shangri-la.dropbear.id.au
Sun Jul 11 19:15:54 EDT 2004
Log message for revision 26418:
svn merge -r 26090:26417 branches/stub-session
The API should be finalized after one more argument with Jim ;-)
-=-
Modified: Zope3/trunk/src/zope/app/session/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/session/__init__.py 2004-07-11 21:54:37 UTC (rev 26417)
+++ Zope3/trunk/src/zope/app/session/__init__.py 2004-07-11 23:15:54 UTC (rev 26418)
@@ -16,243 +16,4 @@
$Id$
"""
-import sha, time, string, random, hmac, warnings, thread
-from UserDict import IterableUserDict
-from heapq import heapify, heappop
-from persistent import Persistent
-from zope.server.http.http_date import build_http_date
-from zope.interface import implements
-from zope.component import ComponentLookupError
-from zope.app.zapi import getUtility
-from BTrees.OOBTree import OOBTree
-from zope.app.utility.interfaces import ILocalUtility
-from zope.app.annotation.interfaces import IAttributeAnnotatable
-
-from interfaces import \
- IBrowserIdManager, IBrowserId, ICookieBrowserIdManager, \
- ISessionDataContainer, ISession, ISessionProductData, ISessionData
-
-import ZODB
-import ZODB.MappingStorage
-
-cookieSafeTrans = string.maketrans("+/", "-.")
-
-def digestEncode(s):
- """Encode SHA digest for cookie."""
- return s.encode("base64")[:-2].translate(cookieSafeTrans)
-
-
-class BrowserId(str):
- """See zope.app.interfaces.utilities.session.IBrowserId"""
- implements(IBrowserId)
-
- def __new__(cls, request):
- return str.__new__(
- cls, getUtility(IBrowserIdManager).getBrowserId(request)
- )
-
-
-class CookieBrowserIdManager(Persistent):
- """Session service implemented using cookies."""
-
- implements(IBrowserIdManager, ICookieBrowserIdManager,
- ILocalUtility, IAttributeAnnotatable,
- )
-
- __parent__ = __name__ = None
-
- def __init__(self):
- self.namespace = "zope3_cs_%x" % (int(time.time()) - 1000000000)
- self.secret = "%.20f" % random.random()
- self.cookieLifetime = None
-
- def generateUniqueId(self):
- """Generate a new, random, unique id."""
- data = "%.20f%.20f%.20f" % (random.random(), time.time(), time.clock())
- digest = sha.sha(data).digest()
- s = digestEncode(digest)
- # we store a HMAC of the random value together with it, which makes
- # our session ids unforgeable.
- mac = hmac.new(s, self.secret, digestmod=sha).digest()
- return s + digestEncode(mac)
-
- def getRequestId(self, request):
- """Return the browser id encoded in request as a string,
- or None if it's non-existent."""
- # If there is an id set on the response, use that but don't trust it.
- # We need to check the response in case there has already been a new
- # session created during the course of this request.
- response_cookie = request.response.getCookie(self.namespace)
- if response_cookie:
- sid = response_cookie['value']
- else:
- sid = request.cookies.get(self.namespace)
- if sid is None or len(sid) != 54:
- return None
- s, mac = sid[:27], sid[27:]
- if (digestEncode(hmac.new(s, self.secret, digestmod=sha).digest())
- != mac):
- return None
- else:
- return sid
-
- def setRequestId(self, request, id):
- """Set cookie with id on request."""
- # Currently, the path is the ApplicationURL. This is reasonable, and
- # will be adequate for most purposes.
- # TODO: A better path to use would be that of the folder that contains
- # the service-manager this service is registered within. However, that
- # would be expensive to look up on each request, and would have to be
- # altered to take virtual hosting into account. Seeing as this
- # service instance has a unique namespace for its cookie, using
- # ApplicationURL shouldn't be a problem.
-
- if self.cookieLifetime is not None:
- if self.cookieLifetime:
- expires = build_http_date(time.time() + self.cookieLifetime)
- else:
- expires = 'Tue, 19 Jan 2038 00:00:00 GMT'
- request.response.setCookie(
- self.namespace, id, expires=expires,
- path=request.getApplicationURL(path_only=True)
- )
- else:
- request.response.setCookie(
- self.namespace, id,
- path=request.getApplicationURL(path_only=True)
- )
-
- def getBrowserId(self, request):
- """See zope.app.session.interfaces.IBrowserIdManager"""
- sid = self.getRequestId(request)
- if sid is None:
- sid = self.generateUniqueId()
- self.setRequestId(request, sid)
- return sid
-
-
-class PersistentSessionDataContainer(Persistent, IterableUserDict):
- ''' A SessionDataContainer that stores data in the ZODB '''
- __parent__ = __name__ = None
-
- implements(ISessionDataContainer, ILocalUtility, IAttributeAnnotatable)
-
- def __init__(self):
- self.data = OOBTree()
- self.sweepInterval = 5*60
-
- def __getitem__(self, product_id):
- rv = IterableUserDict.__getitem__(self, product_id)
- now = time.time()
- # Only update lastAccessTime once every few minutes, rather than
- # every hit, to avoid ZODB bloat and conflicts
- if rv.lastAccessTime + self.sweepInterval < now:
- rv.lastAccessTime = int(now)
- # XXX: When scheduler exists, this method should just schedule
- # a sweep later since we are currently busy handling a request
- # and may end up doing simultaneous sweeps
- self.sweep()
- return rv
-
- def __setitem__(self, product_id, session_data):
- session_data.lastAccessTime = int(time.time())
- return IterableUserDict.__setitem__(self, product_id, session_data)
-
- def sweep(self):
- ''' Clean out stale data '''
- expire_time = time.time() - self.sweepInterval
- heap = [(v.lastAccessTime, k) for k,v in self.data.items()]
- heapify(heap)
- while heap:
- lastAccessTime, key = heappop(heap)
- if lastAccessTime < expire_time:
- del self.data[key]
- else:
- return
-
-
-class RAMSessionDataContainer(PersistentSessionDataContainer):
- ''' A SessionDataContainer that stores data in RAM. Currently session
- data is not shared between Zope clients, so server affinity will
- need to be maintained to use this in a ZEO cluster.
- '''
- def __init__(self):
- self.sweepInterval = 5*60
- self.key = sha.new(str(time.time() + random.random())).hexdigest()
-
- _ram_storage = ZODB.MappingStorage.MappingStorage()
- _ram_db = ZODB.DB(_ram_storage)
- _conns = {}
-
- def _getData(self):
-
- # Open a connection to _ram_storage per thread
- tid = thread.get_ident()
- if not self._conns.has_key(tid):
- self._conns[tid] = self._ram_db.open()
-
- root = self._conns[tid].root()
- if not root.has_key(self.key):
- root[self.key] = OOBTree()
- return root[self.key]
-
- data = property(_getData, None)
-
- def sweep(self):
- super(RAMSessionDataContainer, self).sweep()
- self._ram_db.pack(time.time())
-
-
-class Session:
- """See zope.app.session.interfaces.ISession"""
- implements(ISession)
- __slots__ = ('browser_id',)
- def __init__(self, request):
- self.browser_id = str(IBrowserId(request))
-
- def __getitem__(self, product_id):
- """See zope.app.session.interfaces.ISession"""
-
- # First locate the ISessionDataContainer by looking up
- # the named Utility, and falling back to the unnamed one.
- try:
- sdc = getUtility(ISessionDataContainer, product_id)
- except ComponentLookupError:
- warnings.warn(
- 'Unable to find ISessionDataContainer named %s. '
- 'Using default' % repr(product_id),
- RuntimeWarning
- )
- sdc = getUtility(ISessionDataContainer)
-
- # The ISessionDataContainer contains two levels:
- # ISessionDataContainer[product_id] == ISessionProductData
- # ISessionDataContainer[product_id][browser_id] == ISessionData
- try:
- spd = sdc[product_id]
- except KeyError:
- sdc[product_id] = SessionProductData()
- spd = sdc[product_id]
-
- try:
- return spd[self.browser_id]
- except KeyError:
- spd[self.browser_id] = SessionData()
- return spd[self.browser_id]
-
-
-class SessionProductData(Persistent, IterableUserDict):
- """See zope.app.session.interfaces.ISessionProductData"""
- implements(ISessionProductData)
- lastAccessTime = 0
- def __init__(self):
- self.data = OOBTree()
-
-
-class SessionData(Persistent, IterableUserDict):
- """See zope.app.session.interfaces.ISessionData"""
- implements(ISessionData)
- def __init__(self):
- self.data = OOBTree()
-
Copied: Zope3/trunk/src/zope/app/session/api.txt (from rev 26417, Zope3/branches/stub-session/src/zope/app/session/api.txt)
Property changes on: Zope3/trunk/src/zope/app/session/api.txt
___________________________________________________________________
Name: cvs2svn:cvs-rev
+ 1.1
Name: svn:eol-style
+ native
Modified: Zope3/trunk/src/zope/app/session/bootstrap.py
===================================================================
--- Zope3/trunk/src/zope/app/session/bootstrap.py 2004-07-11 21:54:37 UTC (rev 26417)
+++ Zope3/trunk/src/zope/app/session/bootstrap.py 2004-07-11 23:15:54 UTC (rev 26418)
@@ -24,19 +24,19 @@
from zope.app.session.interfaces import \
- IBrowserIdManager, ISessionDataContainer
-from zope.app.session import \
- CookieBrowserIdManager, PersistentSessionDataContainer
+ IClientIdManager, ISessionDataContainer
+from zope.app.session.http import CookieClientIdManager
+from zope.app.session.session import PersistentSessionDataContainer
class BootstrapInstance(BootstrapSubscriberBase):
def doSetup(self):
self.ensureUtility(
- IBrowserIdManager, 'CookieBrowserIdManager',
- CookieBrowserIdManager,
+ IClientIdManager, 'CookieClientIdManager',
+ CookieClientIdManager,
)
self.ensureUtility(
- ISessionDataContainer, 'PersistentSessionData',
+ ISessionDataContainer, 'PersistentSessionDataContainer',
PersistentSessionDataContainer,
)
Modified: Zope3/trunk/src/zope/app/session/browser.zcml
===================================================================
--- Zope3/trunk/src/zope/app/session/browser.zcml 2004-07-11 21:54:37 UTC (rev 26417)
+++ Zope3/trunk/src/zope/app/session/browser.zcml 2004-07-11 23:15:54 UTC (rev 26418)
@@ -6,49 +6,39 @@
<!-- Cookie Browser Id Manager -->
<addMenuItem
- title="Cookie Browser Id Manager"
- description="Uses a cookie to uniquely identify a browser, allowing
+ title="Cookie Client Id Manager"
+ description="Uses a cookie to uniquely identify a client, allowing
state to be maintained between requests"
- class=".CookieBrowserIdManager"
- permission="zope.ManageContent" />
+ class=".http.CookieClientIdManager"
+ permission="zope.ManageServices" />
- <!-- XXX: We want an add form, but namespace needs to default to a unique
- cookie name
- <addform
- schema=".interfaces.ICookieBrowserIdManager"
- label="Add a Cookie Browser ID Manager"
- content_factory=".CookieBrowserIdManager"
- name="zope.app.interfaces.utilities.session"
- permission="zope.ManageContent" />
- -->
-
<editform
- schema=".interfaces.ICookieBrowserIdManager"
- label="Cookie Browser ID Manager Properties"
+ schema=".http.ICookieClientIdManager"
+ label="Cookie Client Id Manager Properties"
name="edit.html" menu="zmi_views" title="Edit"
- permission="zope.ManageContent" />
+ permission="zope.ManageServices" />
<!-- PersistentSessionDataContainer -->
<addMenuItem
title="Persistent Session Data Container"
description="Stores session data persistently in the ZODB"
- class=".PersistentSessionDataContainer"
- permission="zope.ManageContent" />
+ class=".session.PersistentSessionDataContainer"
+ permission="zope.ManageServices" />
<!-- RAMSessionDataContainer -->
<addMenuItem
title="RAM Session Data Container"
description="Stores session data in RAM"
- class=".RAMSessionDataContainer"
- permission="zope.ManageContent" />
+ class=".session.RAMSessionDataContainer"
+ permission="zope.ManageServices" />
<!-- ISessionDataContainer -->
<editform
schema=".interfaces.ISessionDataContainer"
label="Session Data Container Properties"
name="edit.html" menu="zmi_views" title="Edit"
- permission="zope.ManageContent" />
+ permission="zope.ManageServices" />
</configure>
Modified: Zope3/trunk/src/zope/app/session/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/session/configure.zcml 2004-07-11 21:54:37 UTC (rev 26417)
+++ Zope3/trunk/src/zope/app/session/configure.zcml 2004-07-11 23:15:54 UTC (rev 26418)
@@ -3,56 +3,56 @@
xmlns:browser="http://namespaces.zope.org/browser">
<adapter
- factory=".BrowserId"
- provides=".interfaces.IBrowserId"
+ factory=".session.ClientId"
+ provides=".interfaces.IClientId"
for="zope.publisher.interfaces.IRequest"
permission="zope.Public"
/>
<adapter
- factory=".Session"
+ factory=".session.Session"
provides=".interfaces.ISession"
for="zope.publisher.interfaces.IRequest"
permission="zope.Public"
/>
- <content class=".CookieBrowserIdManager">
+ <content class=".session.Session">
+ <allow interface=".interfaces.ISession" />
+ </content>
+
+ <content class=".http.CookieClientIdManager">
<require
- interface=".interfaces.ICookieBrowserIdManager"
+ interface=".http.ICookieClientIdManager"
permission="zope.Public" />
<require
- set_schema=".interfaces.ICookieBrowserIdManager"
- permission="zope.ManageContent" />
+ set_schema=".http.ICookieClientIdManager"
+ permission="zope.ManageServices" />
</content>
- <content class=".PersistentSessionDataContainer">
- <implements
- interface=".interfaces.ISessionDataContainer"/>
+ <content class=".session.PersistentSessionDataContainer">
<require
interface=".interfaces.ISessionDataContainer"
permission="zope.Public" />
<require
set_schema=".interfaces.ISessionDataContainer"
- permission="zope.ManageContent" />
+ permission="zope.ManageServices" />
</content>
- <content class=".RAMSessionDataContainer">
- <implements
- interface=".interfaces.ISessionDataContainer"/>
+ <content class=".session.RAMSessionDataContainer">
<require
interface=".interfaces.ISessionDataContainer"
permission="zope.Public" />
<require
set_schema=".interfaces.ISessionDataContainer"
- permission="zope.ManageContent" />
+ permission="zope.ManageServices" />
</content>
- <content class=".SessionData">
- <allow interface="zope.interface.common.mapping.IMapping" />
+ <content class=".session.SessionData">
+ <allow interface=".interfaces.ISessionData" />
</content>
- <content class=".Session">
- <allow interface=".interfaces.ISession" />
+ <content class=".session.SessionPkgData">
+ <allow interface=".interfaces.ISessionPkgData" />
</content>
<subscriber
Copied: Zope3/trunk/src/zope/app/session/design.txt (from rev 26417, Zope3/branches/stub-session/src/zope/app/session/design.txt)
Property changes on: Zope3/trunk/src/zope/app/session/design.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Copied: Zope3/trunk/src/zope/app/session/http.py (from rev 26417, Zope3/branches/stub-session/src/zope/app/session/http.py)
Property changes on: Zope3/trunk/src/zope/app/session/http.py
___________________________________________________________________
Name: cvs2svn:cvs-rev
+ 1.4
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Modified: Zope3/trunk/src/zope/app/session/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/app/session/interfaces.py 2004-07-11 21:54:37 UTC (rev 26417)
+++ Zope3/trunk/src/zope/app/session/interfaces.py 2004-07-11 23:15:54 UTC (rev 26418)
@@ -15,7 +15,6 @@
$Id$
"""
-import re
from zope.interface import Interface
from zope.interface.common.mapping import IMapping, IReadMapping, IWriteMapping
from zope import schema
@@ -23,11 +22,11 @@
from zope.app.i18n import ZopeMessageIDFactory as _
-class IBrowserIdManager(Interface):
- """Manages sessions - fake state over multiple browser requests."""
+class IClientIdManager(Interface):
+ """Manages sessions - fake state over multiple client requests."""
- def getBrowserId(request):
- """Return the browser id for the given request as a string.
+ def getClientId(request):
+ """Return the client id for the given request as a string.
If the request doesn't have an attached sessionId a new one will be
generated.
@@ -39,51 +38,14 @@
"""
- """ TODO: Want this:
- def invalidate(browser_id):
- ''' Expire the browser_id, and remove any matching ISessionData data
- '''
- """
+class IClientId(Interface):
+ """A unique id representing a session"""
-
-class ICookieBrowserIdManager(IBrowserIdManager):
- """Manages sessions using a cookie"""
-
- namespace = schema.TextLine(
- title=_('Cookie Name'),
- description=_(
- "Name of cookie used to maintain state. "
- "Must be unique to the site domain name, and only contain "
- "ASCII letters, digits and '_'"
- ),
- required=True,
- min_length=1,
- max_length=30,
- constraint=re.compile("^[\d\w_]+$").search,
- )
-
- cookieLifetime = schema.Int(
- title=_('Cookie Lifetime'),
- description=_(
- "Number of seconds until the browser expires the cookie. "
- "Leave blank expire the cookie when the browser is quit. "
- "Set to 0 to never expire. "
- ),
- min=0,
- required=False,
- default=None,
- missing_value=None,
- )
-
-
-class IBrowserId(Interface):
- """A unique ID representing a session"""
-
def __str__():
"""As a unique ASCII string"""
-class ISessionDataContainer(IMapping):
+class ISessionDataContainer(IReadMapping, IWriteMapping):
"""Stores data objects for sessions.
The object implementing this interface is responsible for expiring data as
@@ -93,34 +55,38 @@
session_data_container[product_id][browser_id][key] = value
- Attempting to access a key that does not exist will raise a KeyError.
+ Note that this interface does not support the full mapping interface -
+ the keys need to remain secret so we can't give access to keys(),
+ values() etc.
"""
-
timeout = schema.Int(
title=_(u"Timeout"),
description=_(
"Number of seconds before data becomes stale and may "
- "be removed"),
+ "be removed. A value of '0' means no expiration."),
default=3600,
required=True,
- min=1,
+ min=0,
)
- sweepInterval = schema.Int(
- title=_(u"Purge Interval"),
+ resolution = schema.Int(
+ title=_("Timeout resolution (in seconds)"),
description=_(
- "How often stale data is purged in seconds. "
- "Higer values improve performance."
+ "Defines what the 'resolution' of item timeout is. "
+ "Setting this higher allows the transience machinery to "
+ "do fewer 'writes' at the expense of causing items to time "
+ "out later than the 'Data object timeout value' by a factor "
+ "of (at most) this many seconds."
),
default=5*60,
required=True,
- min=1,
+ min=0,
)
def __getitem__(self, product_id):
- """Return an ISessionProductData"""
+ """Return an ISessionPkgData"""
def __setitem__(self, product_id, value):
- """Store an ISessionProductData"""
+ """Store an ISessionPkgData"""
class ISession(Interface):
@@ -129,6 +95,10 @@
>>> session = ISession(request)[product_id]
>>> session['color'] = 'red'
+ True
+
+ >>> ISessionData.providedBy(session)
+ True
"""
def __getitem__(product_id):
@@ -136,9 +106,9 @@
and return that product id's ISessionData"""
-class ISessionProductData(IReadMapping, IWriteMapping):
+class ISessionData(IReadMapping, IMapping):
"""Storage for a particular product id's session data, containing
- 0 or more ISessionData instances"""
+ 0 or more ISessionPkgData instances"""
lastAccessTime = schema.Int(
title=_("Last Access Time"),
@@ -150,13 +120,20 @@
required=True,
)
+ # Note that only IReadMapping and IWriteMaping are implemented.
+ # We cannot give access to the keys, as they need to remain secret.
+
def __getitem__(self, browser_id):
"""Return an ISessionData"""
def __setitem__(self, browser_id, session_data):
"""Store an ISessionData"""
-class ISessionData(IMapping):
- """Storage for a particular product id and browser id's session data"""
+class ISessionPkgData(IMapping):
+ """Storage for a particular product id and browser id's session data
+ Data is stored persistently and transactionally. Data stored must
+ be persistent or pickable.
+ """
+
Copied: Zope3/trunk/src/zope/app/session/session.py (from rev 26417, Zope3/branches/stub-session/src/zope/app/session/session.py)
Property changes on: Zope3/trunk/src/zope/app/session/session.py
___________________________________________________________________
Name: cvs2svn:cvs-rev
+ 1.4
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Modified: Zope3/trunk/src/zope/app/session/tests.py
===================================================================
--- Zope3/trunk/src/zope/app/session/tests.py 2004-07-11 21:54:37 UTC (rev 26417)
+++ Zope3/trunk/src/zope/app/session/tests.py 2004-07-11 23:15:54 UTC (rev 26418)
@@ -15,310 +15,40 @@
'''
$Id$
'''
-import unittest, doctest, time, rfc822
+import unittest, doctest, os, os.path, sys
from zope.app import zapi
-from zope.app.tests import ztapi
-from zope.app.tests import setup
-import zope.interface
-from zope.app.utility.interfaces import ILocalUtility
-from zope.app.utility import LocalUtilityService
-from zope.app.servicenames import Utilities
-from zope.app.annotation.interfaces import IAttributeAnnotatable
+from zope.app.tests import ztapi, placelesssetup
from zope.app.session.interfaces import \
- IBrowserId, IBrowserIdManager, \
- ISession, ISessionDataContainer, ISessionData, ISessionProductData
+ IClientId, IClientIdManager, ISession, ISessionDataContainer, \
+ ISessionPkgData, ISessionData
-from zope.app.session import \
- BrowserId, CookieBrowserIdManager, \
- PersistentSessionDataContainer, RAMSessionDataContainer, \
- Session, SessionData, SessionProductData
+from zope.app.session.session import \
+ ClientId, Session, \
+ PersistentSessionDataContainer, RAMSessionDataContainer
+from zope.app.session.http import CookieClientIdManager
+
from zope.publisher.interfaces import IRequest
-from zope.publisher.interfaces.http import IHTTPRequest
from zope.publisher.http import HTTPRequest
-def setUp(session_data_container_class):
+from zope.pagetemplate.pagetemplate import PageTemplate
- # Placeful setup
- root = setup.placefulSetUp(site=True)
- setup.createStandardServices(root)
- sm = setup.createServiceManager(root, True)
- setup.addService(sm, Utilities, LocalUtilityService())
-
- # Add a CookieBrowserIdManager Utility
- setup.addUtility(sm, '', IBrowserIdManager, CookieBrowserIdManager())
-
- # Add an ISessionDataContainer, registered under a number of names
+def setUp(session_data_container_class=PersistentSessionDataContainer):
+ placelesssetup.setUp()
+ ztapi.provideAdapter(IRequest, IClientId, ClientId)
+ ztapi.provideAdapter(IRequest, ISession, Session)
+ ztapi.provideUtility(IClientIdManager, CookieClientIdManager())
sdc = session_data_container_class()
for product_id in ('', 'products.foo', 'products.bar', 'products.baz'):
- setup.addUtility(sm, product_id, ISessionDataContainer, sdc)
-
- # Register our adapters
- ztapi.provideAdapter(IRequest, IBrowserId, BrowserId)
- ztapi.provideAdapter(IRequest, ISession, Session)
-
- # Return a request
+ ztapi.provideUtility(ISessionDataContainer, sdc, product_id)
request = HTTPRequest(None, None, {}, None)
return request
def tearDown():
- setup.placefulTearDown()
+ placelesssetup.tearDown()
-def test_CookieBrowserIdManager():
- """
- CookieBrowserIdManager.generateUniqueId should generate a unique
- IBrowserId each time it is called
-
- >>> bim = CookieBrowserIdManager()
- >>> id1 = bim.generateUniqueId()
- >>> id2 = bim.generateUniqueId()
- >>> id1 != id2
- True
-
- CookieBrowserIdManager.getRequestId pulls the browser id from an
- IHTTPRequest, or returns None if there isn't one stored in it.
- Because cookies cannnot be trusted, we confirm that they are not forged,
- returning None if we have a corrupt or forged browser id.
-
- >>> request = HTTPRequest(None, None, {}, None)
- >>> bim.getRequestId(request) is None
- True
- >>> bim.setRequestId(request, id1)
- >>> bim.getRequestId(request) == id1
- True
- >>> bim.setRequestId(request, 'invalid_id')
- >>> bim.getRequestId(request) is None
- True
-
- Make sure that the same browser id is extracted from a cookie in
- request (sent from the browser) and a cookie in request.response
- (set during this transaction)
-
- >>> request2 = HTTPRequest(None, None, {}, None)
- >>> request2._cookies = request.response._cookies
- >>> bim.getRequestId(request) == bim.getRequestId(request2)
- True
-
- CookieBrowserIdManager.getBrowserId pulls the browser id from an
- IHTTPRequest, or generates a new one and returns it after storing
- it in the request.
-
- >>> id3 = bim.getBrowserId(request)
- >>> id4 = bim.getBrowserId(request)
- >>> id3 == id4
- True
- >>> id3 == id4
- True
- >>> bool(id3)
- True
-
- Confirm the path of the cookie is correct. The value being tested
- for here will eventually change - it should be the URL to the
- site containing the CookieBrowserIdManager
-
- >>> cookie = request.response.getCookie(bim.namespace)
- >>> cookie['path'] == request.getApplicationURL(path_only=True)
- True
-
- Confirm the expiry time of the cookie is correct.
- Default is no expires.
-
- >>> cookie.has_key('expires')
- False
-
- Expiry time of 0 means never (well - close enough)
-
- >>> bim.cookieLifetime = 0
- >>> request = HTTPRequest(None, None, {}, None)
- >>> bid = bim.getBrowserId(request)
- >>> cookie = request.response.getCookie(bim.namespace)
- >>> cookie['expires']
- 'Tue, 19 Jan 2038 00:00:00 GMT'
-
- >>> bim.cookieLifetime = 3600
- >>> request = HTTPRequest(None, None, {}, None)
- >>> bid = bim.getBrowserId(request)
- >>> cookie = request.response.getCookie(bim.namespace)
- >>> expires = time.mktime(rfc822.parsedate(cookie['expires']))
- >>> expires > time.mktime(time.gmtime()) + 55*60
- True
- """
-
-
-def test_BrowserId():
- """
- >>> request = setUp(PersistentSessionDataContainer)
-
- >>> id1 = BrowserId(request)
- >>> id2 = BrowserId(request)
- >>> id1 == id2
- True
-
- >>> tearDown()
- """
-
-
-def test_PersistentSessionDataContainer():
- """
- Ensure mapping interface is working as expected
-
- >>> sdc = PersistentSessionDataContainer()
- >>> sdc['a']
- Traceback (most recent call last):
- File "<stdin>", line 1, in ?
- File "/usr/python-2.3.3/lib/python2.3/UserDict.py", line 19, in __getitem__
- def __getitem__(self, key): return self.data[key]
- KeyError: 'a'
- >>> sdc['a'] = SessionData()
- >>> pdict = SessionData()
- >>> sdc['a'] = pdict
- >>> id(pdict) == id(sdc['a'])
- True
- >>> del sdc['a']
- >>> sdc['a']
- Traceback (most recent call last):
- File "<stdin>", line 1, in ?
- File "/usr/python-2.3.3/lib/python2.3/UserDict.py", line 19, in __getitem__
- def __getitem__(self, key): return self.data[key]
- KeyError: 'a'
- >>> del sdc['a']
- Traceback (most recent call last):
- File "<stdin>", line 1, in ?
- File "/usr/python-2.3.3/lib/python2.3/UserDict.py", line 21, in __delitem__
- def __delitem__(self, key): del self.data[key]
- KeyError: 'a'
-
- Make sure stale data is removed
-
- >>> sdc.sweepInterval = 60
- >>> sdc[1], sdc[2] = sd1, sd2 = SessionData(), SessionData()
- >>> ignore = sdc[1], sdc[2]
- >>> sd1.lastAccessTime = sd1.lastAccessTime - 62
- >>> sd2.lastAccessTime = sd2.lastAccessTime - 62
- >>> ignore = sdc[1]
- >>> sdc.get(2, 'stale')
- 'stale'
-
- Ensure lastAccessTime on the ISessionData is being updated
- occasionally. The ISessionDataContainer maintains this whenever
- the ISessionData is retrieved.
-
- >>> sd = SessionData()
- >>> sdc['product_id'] = sd
- >>> sd.lastAccessTime > 0
- True
- >>> last1 = sd.lastAccessTime - 62
- >>> sd.lastAccessTime = last1 # Wind back the clock
- >>> last1 < sdc['product_id'].lastAccessTime
- True
- """
-
-
-def test_RAMSessionDataContainer(self):
- pass
-test_RAMSessionDataContainer.__doc__ = \
- test_PersistentSessionDataContainer.__doc__.replace(
- 'PersistentSessionDataContainer', 'RAMSessionDataContainer'
- )
-
-
-def test_SessionProductData():
- """
- >>> session = SessionProductData()
- >>> ISessionProductData.providedBy(session)
- True
- """
-
-
-def test_SessionData():
- """
- >>> session = SessionData()
-
- Is the interface defined?
-
- >>> ISessionData.providedBy(session)
- True
-
- Make sure it actually works
-
- >>> session['color']
- Traceback (most recent call last):
- File "<stdin>", line 1, in ?
- File "zope/app/utilities/session.py", line 157, in __getitem__
- return self._data[key]
- KeyError: 'color'
- >>> session['color'] = 'red'
- >>> session['color']
- 'red'
-
- Test the rest of the dictionary interface...
-
- >>> 'foo' in session
- False
- >>> 'color' in session
- True
- >>> session.get('size', 'missing')
- 'missing'
- >>> session.get('color', 'missing')
- 'red'
- >>> list(session.keys())
- ['color']
- >>> list(session.values())
- ['red']
- >>> list(session.items())
- [('color', 'red')]
- >>> len(session)
- 1
- >>> [x for x in session]
- ['color']
- >>> del session['color']
- >>> session.get('color') is None
- True
- """
-
-def test_Session():
- """
- >>> request = setUp(PersistentSessionDataContainer)
- >>> request2 = HTTPRequest(None, None, {}, None)
-
- >>> ISession.providedBy(Session(request))
- True
-
- >>> session1 = Session(request)['products.foo']
- >>> session2 = Session(request)['products.bar']
- >>> session3 = Session(request)['products.bar'] # dupe
- >>> session4 = Session(request2)['products.bar'] # not dupe
-
- Make sure it returned sane values
-
- >>> ISessionData.providedBy(session1)
- True
- >>> ISessionData.providedBy(session2)
- True
- >>> session2 == session3
- True
- >>> ISessionData.providedBy(session4)
- True
-
- Make sure that product_ids don't share a namespace, except when they should
-
- >>> session1['color'] = 'red'
- >>> session2['color'] = 'blue'
- >>> session4['color'] = 'vomit'
- >>> session1['color']
- 'red'
- >>> session2['color']
- 'blue'
- >>> session3['color']
- 'blue'
- >>> session4['color']
- 'vomit'
-
- >>> tearDown()
- """
-
from zope.app.appsetup.tests import TestBootstrapSubscriberBase, EventStub
class TestBootstrapInstance(TestBootstrapSubscriberBase):
@@ -336,16 +66,29 @@
root_folder = root[ZopePublication.root_name]
setSite(root_folder)
- zapi.getUtility(IBrowserIdManager)
+ zapi.getUtility(IClientIdManager)
zapi.getUtility(ISessionDataContainer)
cx.close()
+# Test the code in our API documentation is correct
+def test_documentation():
+ pass
+test_documentation.__doc__ = '''
+ >>> request = setUp(RAMSessionDataContainer)
+ %s
+
+ >>> tearDown()
+
+ ''' % (open(os.path.join(os.path.dirname(__file__), 'api.txt')).read(),)
+
def test_suite():
return unittest.TestSuite((
doctest.DocTestSuite(),
+ doctest.DocTestSuite('zope.app.session.session'),
+ doctest.DocTestSuite('zope.app.session.http'),
unittest.makeSuite(TestBootstrapInstance),
))
@@ -354,4 +97,3 @@
# vim: set filetype=python ts=4 sw=4 et si
-
More information about the Zope3-Checkins
mailing list