[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/session/ Merged Zope3/branches/stub-session r25035:HEAD

Stuart Bishop zen at shangri-la.dropbear.id.au
Fri May 28 22:26:55 EDT 2004


Log message for revision 25115:
Merged Zope3/branches/stub-session r25035:HEAD


-=-
Modified: Zope3/trunk/src/zope/app/session/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/session/__init__.py	2004-05-29 00:48:50 UTC (rev 25114)
+++ Zope3/trunk/src/zope/app/session/__init__.py	2004-05-29 02:26:54 UTC (rev 25115)
@@ -11,14 +11,12 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Simplistic session service implemented using cookies.
+"""
+Session implementation using cookies
 
-This is more of a demonstration than a full implementation, but it should
-work.
-
 $Id$
 """
-import sha, time, string, random, hmac, logging
+import sha, time, string, random, hmac, logging, warnings, thread
 from UserDict import IterableUserDict
 from heapq import heapify, heappop
 
@@ -26,15 +24,19 @@
 from zope.server.http.http_date import build_http_date
 from zope.interface import implements
 from zope.interface.common.mapping import IMapping
+from zope.component import ComponentLookupError
 from zope.app import zapi
 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
-from zope.app.container.interfaces import IContained
+from interfaces import \
+        IBrowserIdManager, IBrowserId, ICookieBrowserIdManager, \
+        ISessionDataContainer, ISession, ISessionProductData, ISessionData
 
+import ZODB
+import ZODB.MappingStorage
+
 cookieSafeTrans = string.maketrans("+/", "-.")
 
 def digestEncode(s):
@@ -43,10 +45,15 @@
 
 
 class BrowserId(str):
-    """A browser id"""
+    """See zope.app.interfaces.utilities.session.IBrowserId"""
     implements(IBrowserId)
 
+    def __new__(cls, request):
+        return str.__new__(
+                cls, zapi.getUtility(IBrowserIdManager).getBrowserId(request)
+                )
 
+
 class CookieBrowserIdManager(Persistent):
     """Session service implemented using cookies."""
 
@@ -69,11 +76,11 @@
         # 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 BrowserId(s + digestEncode(mac))
+        return s + digestEncode(mac)
 
     def getRequestId(self, request):
-        """Return the IBrowserId encoded in request or None if it's
-        non-existent."""
+        """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.
@@ -89,7 +96,7 @@
             != mac):
             return None
         else:
-            return BrowserId(sid)
+            return sid
 
     def setRequestId(self, request, id):
         """Set cookie with id on request."""
@@ -117,9 +124,8 @@
                     path=request.getApplicationURL(path_only=True)
                     )
 
-
     def getBrowserId(self, request):
-        ''' See zope.app.interfaces.utilities.session.IBrowserIdManager '''
+        """See zope.app.session.interfaces.IBrowserIdManager"""
         sid = self.getRequestId(request)
         if sid is None:
             sid = self.generateUniqueId()
@@ -137,20 +143,23 @@
         self.data = OOBTree()
         self.sweepInterval = 5*60
 
-    def __getitem__(self, key):
-        rv = IterableUserDict.__getitem__(self, key)
+    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 since this is being stored 
-        # persistently
+        # every hit, to avoid ZODB bloat and conflicts
         if rv.lastAccessTime + self.sweepInterval < now:
-            rv.lastAccessTime = 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
@@ -164,52 +173,88 @@
                 return
 
 
-class SessionData(Persistent, IterableUserDict):
-    ''' Mapping nodes in the ISessionDataContainer tree '''
-    implements(IMapping)
-
+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.data = OOBTree()
-        self.lastAccessTime = time.time()
+        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 = {}
 
-class Session(IterableUserDict):
+    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)
-    def __init__(self, data_manager, browser_id, product_id):
-        ''' See zope.app.interfaces.utilities.session.ISession '''
-        browser_id = str(browser_id)
-        product_id = str(product_id)
+    __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:
-            data = data_manager[browser_id]
+            sdc = zapi.getUtility(ISessionDataContainer, product_id)
+        except ComponentLookupError:
+            # XXX: Do we want this?
+            warnings.warn(
+                    'Unable to find ISessionDataContainer named %s. '
+                    'Using default' % repr(product_id),
+                    RuntimeWarning
+                    )
+            sdc = zapi.getUtility(ISessionDataContainer)
+
+        # The ISessionDataContainer contains two levels:
+        # ISessionDataContainer[product_id] == ISessionProductData
+        # ISessionDataContainer[product_id][browser_id] == ISessionData
+        try:
+            spd = sdc[product_id]
         except KeyError:
-            data_manager[browser_id] = SessionData()
-            data_manager[browser_id][product_id] = SessionData()
-            self.data = data_manager[browser_id][product_id]
-        else:
-            try:
-                self.data = data[product_id]
-            except KeyError:
-                data[product_id] = SessionData()
-                self.data = data[product_id]
+            sdc[product_id] = SessionProductData()
+            spd = sdc[product_id]
 
-# XXX: remove context arg
-def getSession(context, request, product_id, session_data_container=None):
-    ''' Retrieve an ISession. session_data_container defaults to 
-        an ISessionDataContainer utility registered with the name product_id
+        try:
+            return spd[self.browser_id]
+        except KeyError:
+            spd[self.browser_id] = SessionData()
+            return spd[self.browser_id]
 
-        XXX: This method will probably be changed when we have an
-            Interaction or other object that combines context & request
-            into a single object.
-    '''
-    if session_data_container is None:
-        dc = zapi.getUtility(ISessionDataContainer, product_id)
-    elif ISessionDataContainer.providedBy(session_data_container):
-        dc = session_data_container
-    else:
-        dc = zapi.getUtility(ISessionDataContainer, session_data_container)
 
-    bim = zapi.getUtility(IBrowserIdManager)
-    browser_id = bim.getBrowserId(request)
-    return Session(dc, browser_id, product_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()
+

Modified: Zope3/trunk/src/zope/app/session/browser.zcml
===================================================================
--- Zope3/trunk/src/zope/app/session/browser.zcml	2004-05-29 00:48:50 UTC (rev 25114)
+++ Zope3/trunk/src/zope/app/session/browser.zcml	2004-05-29 02:26:54 UTC (rev 25115)
@@ -1,8 +1,9 @@
 <configure 
   xmlns:zope="http://namespaces.zope.org/zope"
   xmlns="http://namespaces.zope.org/browser">
-<!-- Cookie Browser Id Manager -->
 
+  <!-- Cookie Browser Id Manager -->
+
   <addMenuItem
     title="Cookie Browser Id Manager"
     description="Uses a cookie to uniquely identify a browser, allowing 
@@ -26,7 +27,7 @@
     name="edit.html" menu="zmi_views" title="Edit"
     permission="zope.ManageContent" />
 
-<!-- PersistentSessionDataContainer -->
+  <!-- PersistentSessionDataContainer -->
 
   <addMenuItem
     title="Persistent Session Data Container"
@@ -34,9 +35,18 @@
     class=".PersistentSessionDataContainer"
     permission="zope.ManageContent" />
 
+  <!-- RAMSessionDataContainer -->
+
+  <addMenuItem
+    title="RAM Session Data Container"
+    description="Stores session data in RAM"
+    class=".RAMSessionDataContainer"
+    permission="zope.ManageContent" />
+
+  <!-- ISessionDataContainer -->
   <editform
     schema=".interfaces.ISessionDataContainer"
-    label="Persistent Session Data Container Properties"
+    label="Session Data Container Properties"
     name="edit.html" menu="zmi_views" title="Edit"
     permission="zope.ManageContent" />
 

Modified: Zope3/trunk/src/zope/app/session/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/session/configure.zcml	2004-05-29 00:48:50 UTC (rev 25114)
+++ Zope3/trunk/src/zope/app/session/configure.zcml	2004-05-29 02:26:54 UTC (rev 25115)
@@ -2,8 +2,20 @@
     xmlns="http://namespaces.zope.org/zope"
     xmlns:browser="http://namespaces.zope.org/browser">
 
-  <!-- Session machinery -->
+  <adapter
+      factory=".BrowserId"
+      provides=".interfaces.IBrowserId"
+      for="zope.publisher.interfaces.IRequest"
+      permission="zope.Public" 
+      />
 
+  <adapter
+      factory=".Session"
+      provides=".interfaces.ISession"
+      for="zope.publisher.interfaces.IRequest"
+      permission="zope.Public"
+      />
+
   <content class=".CookieBrowserIdManager">
     <require
         interface=".interfaces.ICookieBrowserIdManager"
@@ -13,11 +25,18 @@
         permission="zope.ManageContent" />
   </content>
 
-  <content class=".SessionData">
-    <allow interface="zope.interface.common.mapping.IMapping" />
+  <content class=".PersistentSessionDataContainer">
+    <implements
+        interface=".interfaces.ISessionDataContainer"/>
+    <require
+        interface=".interfaces.ISessionDataContainer"
+        permission="zope.Public" />
+    <require
+        set_schema=".interfaces.ISessionDataContainer"
+        permission="zope.ManageContent" />
   </content>
 
-  <content class=".PersistentSessionDataContainer">
+  <content class=".RAMSessionDataContainer">
     <implements
         interface=".interfaces.ISessionDataContainer"/>
     <require
@@ -28,6 +47,14 @@
         permission="zope.ManageContent" />
   </content>
 
+  <content class=".SessionData">
+    <allow interface="zope.interface.common.mapping.IMapping" />
+  </content>
+
+  <content class=".Session">
+    <allow interface=".interfaces.ISession" />
+  </content>
+
   <include file="browser.zcml" />
 
 </configure>

Modified: Zope3/trunk/src/zope/app/session/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/app/session/interfaces.py	2004-05-29 00:48:50 UTC (rev 25114)
+++ Zope3/trunk/src/zope/app/session/interfaces.py	2004-05-29 02:26:54 UTC (rev 25115)
@@ -27,7 +27,7 @@
     """Manages sessions - fake state over multiple browser requests."""
 
     def getBrowserId(request):
-        """Return the IBrowserId for the given request.
+        """Return the browser id for the given request as a string.
         
         If the request doesn't have an attached sessionId a new one will be
         generated.
@@ -83,15 +83,15 @@
         """As a unique ASCII string"""
 
 
-class ISessionDataContainer(IReadMapping, IWriteMapping):
+class ISessionDataContainer(IMapping):
     """Stores data objects for sessions.
 
     The object implementing this interface is responsible for expiring data as
     it feels appropriate.
 
-    Used like::
+    Usage::
 
-      session_data_container[browser_id][product_id][key] = value
+      session_data_container[product_id][browser_id][key] = value
 
     Attempting to access a key that does not exist will raise a KeyError.
     """
@@ -116,17 +116,47 @@
             min=1,
             )
 
+    def __getitem__(self, product_id):
+        """Return an ISessionProductData"""
 
-class ISession(IMapping):
-    """A session object that keeps the state of the user.
+    def __setitem__(self, product_id, value):
+        """Store an ISessionProductData"""
 
-    To access bits of data within an ISessionDataContainer, we
-    need to know the browser_id, the product_id, and the actual key.
-    An ISession is a wrapper around an ISessionDataContainer that
-    simplifies this by storing the browser_id and product_id enabling
-    access using just the key.
+
+class ISession(Interface):
+    """This object allows retrieval of the correct ISessionData
+    for a particular product id
+    
+    >>> session = ISession(request)[product_id]
+    >>> session['color'] = 'red'
     """
 
-    def __init__(session_data_container, browser_id, product_id):
-        """Construct an ISession"""
+    def __getitem__(product_id):
+        """Locate the correct ISessionDataContainer for the given product id
+        and return that product id's ISessionData"""
 
+
+class ISessionProductData(IReadMapping, IWriteMapping):
+    """Storage for a particular product id's session data, containing
+    0 or more ISessionData instances"""
+
+    lastAccessTime = schema.Int(
+            title=_("Last Access Time"),
+            description=_(
+                "Approximate epoch time this ISessionData was last retrieved "
+                "from its ISessionDataContainer"
+                ),
+            default=0,
+            required=True,
+            )
+
+    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"""
+
+

Modified: Zope3/trunk/src/zope/app/session/session.stx
===================================================================
--- Zope3/trunk/src/zope/app/session/session.stx	2004-05-29 00:48:50 UTC (rev 25114)
+++ Zope3/trunk/src/zope/app/session/session.stx	2004-05-29 02:26:54 UTC (rev 25115)
@@ -9,27 +9,40 @@
 for propagating this id so that future requests from the browser get
 the same id (eg. by setting an HTTP cookie)
 
-ISessionDataContainer Utilities provide a mapping interface to store
-session data. The ISessionDataContainer is responsible for expiring
-data.
+ISessionDataContainer Utilities store session data. The ISessionDataContainer
+is responsible for expiring data.
 
+ISessionDataContainer[product_id] returns ISessionProductData
+ISessionDataContainer[product_id][browser_id] returns ISessionData
 
+ISession(request)[product_id] returns ISessionData
+
+An ISession determines what ISessionDataContainer to use by looking
+up an ISessionDataContainer using the product_id as the name, and
+falling back to the unnamed ISessionDataContainer utility. This allows
+site administrators to select which ISessionDataContainer a particular
+product stores its session data in by registering the utility with
+the relevant name(s).
+
 Python example::
 
-    >>> browser_id = getAdapter(request, IBrowserId))
+    >>> browser_id = IBrowserId(request)
 
-    >>> explicit_dm = getUtility(context, ISessionDataContainer, 
-    ...     'zopeproducts.fooprod')
-    >>> session = Session(explicit_dm, browser_id, 'zopeproducts.foorprod')
-    >>> session['color'] = 'red'
+    >>> session_data = ISession(request)['zopeproducts.fooprod']
+    >>> session_data['color'] = 'red'
 
-    or....
+    or for the adventurous....
 
-    >>> session = zapi.getSession(context, request, 'zopeproducts.fooprod')
-    >>> session['color'] = 'red'
+    >>> explicit_dc = getUtility(ISessionDataContainer, 'zopeproducts.fooprod')
+    >>> session_data = explicit_dc['zopeproducts.fooprod'][str(browser_id)]
+    >>> session_data = Session(explicit_dc, browser_id)['zopeproducts.fooprod']
+    >>> session_data['color'] = 'red'
 
+
 Page Template example::
 
+    XXX: Needs update when TALES adapter syntax decided
+
     <tal:x condition="exists:session/zopeproducts.fooprod/count">
        <tal:x condition="python:
         session['zopeproducts.fooprod']['count'] += 1" />
@@ -40,8 +53,3 @@
     </tal:x>
     <span content="session/zopeproducts.fooprod/count">6</span>
 
-TODO
-----
-Do we want to provide one or more 'default' ISessionDataContainer's out of the
-box (eg. 'persistant' and 'transient')?
-

Modified: Zope3/trunk/src/zope/app/session/tests.py
===================================================================
--- Zope3/trunk/src/zope/app/session/tests.py	2004-05-29 00:48:50 UTC (rev 25114)
+++ Zope3/trunk/src/zope/app/session/tests.py	2004-05-29 02:26:54 UTC (rev 25115)
@@ -26,15 +26,46 @@
 from zope.app.annotation.interfaces import IAttributeAnnotatable
 
 from zope.app.session.interfaces import \
-        IBrowserId, IBrowserIdManager, ISession, ISessionDataContainer
+        IBrowserId, IBrowserIdManager, \
+        ISession, ISessionDataContainer, ISessionData, ISessionProductData
 
 from zope.app.session import \
-        CookieBrowserIdManager, Session, SessionData, getSession, \
-        PersistentSessionDataContainer
+        BrowserId, CookieBrowserIdManager, \
+        PersistentSessionDataContainer, RAMSessionDataContainer, \
+        Session, SessionData, SessionProductData
 
+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):
+
+    # 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
+    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
+    request = HTTPRequest(None, None, {}, None)
+    return request
+
+def tearDown():
+    setup.placefulTearDown()
+
+
 def test_CookieBrowserIdManager():
     """
     CookieBrowserIdManager.generateUniqueId should generate a unique
@@ -45,12 +76,8 @@
     >>> id2 = bim.generateUniqueId()
     >>> id1 != id2
     True
-    >>> IBrowserId.providedBy(id1)
-    True
-    >>> IBrowserId.providedBy(id2)
-    True
 
-    CookieBrowserIdManager.getRequestId pulls the IBrowserId from an
+    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.
@@ -74,16 +101,18 @@
     >>> bim.getRequestId(request) == bim.getRequestId(request2)
     True
 
-    CookieBrowserIdManager.getBrowserId pulls the IBrowserId from an
+    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)
-    >>> str(id3) == str(id4)
+    >>> 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
@@ -117,8 +146,22 @@
     True
     """
 
-def test_PersistentSessionIdContainer():
+
+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()
@@ -157,12 +200,49 @@
     >>> 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_Session():
+
+def test_RAMSessionDataContainer(self):
+    pass
+test_RAMSessionDataContainer.__doc__ = \
+        test_PersistentSessionDataContainer.__doc__.replace(
+            'PersistentSessionDataContainer', 'RAMSessionDataContainer'
+            )
+
+
+def test_SessionProductData():
     """
-    >>> data_container = PersistentSessionDataContainer()
-    >>> session = Session(data_container, 'browser id', 'zopeproducts.foo')
+    >>> 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 ?
@@ -173,15 +253,6 @@
     >>> session['color']
     'red'
 
-    And make sure no namespace conflicts...
-
-    >>> session2 = Session(data_container, 'browser id', 'zopeproducts.bar')
-    >>> session2['color'] = 'blue'
-    >>> session['color']
-    'red'
-    >>> session2['color']
-    'blue'
-
     Test the rest of the dictionary interface...
 
     >>> 'foo' in session
@@ -207,64 +278,48 @@
     True
     """
 
-
-def test_localutilities():
+def test_Session():
     """
-    Setup a placeful environment with a IBrowserIdManager
-    and ISessionDataContainer
+    >>> request = setUp(PersistentSessionDataContainer)
+    >>> request2 = HTTPRequest(None, None, {}, None)
+  
+    >>> ISession.providedBy(Session(request))
+    True
 
-    >>> root = setup.placefulSetUp(site=True)
-    >>> setup.createStandardServices(root)
-    >>> sm = setup.createServiceManager(root, True)
-    >>> us = setup.addService(sm, Utilities, LocalUtilityService())
-    >>> idmanager = CookieBrowserIdManager()
-    >>> zope.interface.directlyProvides(idmanager,
-    ...                                 IAttributeAnnotatable, ILocalUtility)
-    >>> bim = setup.addUtility(
-    ...     sm, '', IBrowserIdManager, idmanager, 'test')
-    >>> pdc = PersistentSessionDataContainer()
-    >>> zope.interface.directlyProvides(pdc,
-    ...                                 IAttributeAnnotatable, ILocalUtility)
-    >>> sdc = setup.addUtility(sm, 'persistent', ISessionDataContainer, pdc)
-    >>> sdc = setup.addUtility(sm, 'products.foo',ISessionDataContainer, pdc)
-    >>> sdc = setup.addUtility(sm, 'products.bar', ISessionDataContainer, pdc)
-    >>> request = HTTPRequest(None, None, {}, None)
-   
-    Make sure we can access utilities
+    >>> session1 = Session(request)['products.foo']
+    >>> session2 = Session(request)['products.bar']
+    >>> session3 = Session(request)['products.bar']  # dupe
+    >>> session4 = Session(request2)['products.bar'] # not dupe
 
-    >>> sdc = zapi.getUtility(ISessionDataContainer, 'persistent',
-    ...                       context=root)
-    >>> bim = zapi.getUtility(IBrowserIdManager, context=root)
-
-    Make sure getSession works
-
-    >>> session1 = getSession(root, request, 'products.foo')
-    >>> session2 = getSession(root, request, 'products.bar', 'persistent')
-    >>> session3 = getSession(root, request, 'products.baz', pdc)
-
     Make sure it returned sane values
 
-    >>> ISession.providedBy(session1)
+    >>> ISessionData.providedBy(session1)
     True
-    >>> ISession.providedBy(session2)
+    >>> ISessionData.providedBy(session2)
     True
-    >>> ISession.providedBy(session3)
+    >>> session2 == session3
     True
+    >>> ISessionData.providedBy(session4)
+    True
 
-    Make sure that product_ids don't share a namespace
+    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'
 
-    >>> setup.placefulTearDown()
-    >>> 'Thats all folks!'
-    'Thats all folks!'
+    >>> tearDown()
     """
 
+
 def test_suite():
     return unittest.TestSuite((
         doctest.DocTestSuite(),




More information about the Zope3-Checkins mailing list