[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