[Zope-CVS] CVS: Products/Transience - Transience.py:1.2 TransienceInterfaces.py:1.3 __init__.py:1.2
Matthew T. Kromer
matt@zope.com
Mon, 22 Oct 2001 12:24:23 -0400
Update of /cvs-repository/Products/Transience
In directory cvs.zope.org:/tmp/cvs-serv31059
Modified Files:
Transience.py TransienceInterfaces.py __init__.py
Log Message:
updated transience checkin in proper place ;)
=== Products/Transience/Transience.py 1.1 => 1.2 ===
+#
+# Zope Public License (ZPL) Version 1.0
+# -------------------------------------
+#
+# Copyright (c) Digital Creations. All rights reserved.
+#
+# This license has been certified as Open Source(tm).
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# 1. Redistributions in source code must retain the above copyright
+# notice, this list of conditions, and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions, and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. Digital Creations requests that attribution be given to Zope
+# in any manner possible. Zope includes a "Powered by Zope"
+# button that is installed by default. While it is not a license
+# violation to remove this button, it is requested that the
+# attribution remain. A significant investment has been put
+# into Zope, and this effort will continue if the Zope community
+# continues to grow. This is one way to assure that growth.
+#
+# 4. All advertising materials and documentation mentioning
+# features derived from or use of this software must display
+# the following acknowledgement:
+#
+# "This product includes software developed by Digital Creations
+# for use in the Z Object Publishing Environment
+# (http://www.zope.org/)."
+#
+# In the event that the product being advertised includes an
+# intact Zope distribution (with copyright and license included)
+# then this clause is waived.
+#
+# 5. Names associated with Zope or Digital Creations must not be used to
+# endorse or promote products derived from this software without
+# prior written permission from Digital Creations.
+#
+# 6. Modified redistributions of any form whatsoever must retain
+# the following acknowledgment:
+#
+# "This product includes software developed by Digital Creations
+# for use in the Z Object Publishing Environment
+# (http://www.zope.org/)."
+#
+# Intact (re-)distributions of any official Zope release do not
+# require an external acknowledgement.
+#
+# 7. Modifications are encouraged but must be packaged separately as
+# patches to official Zope releases. Distributions that do not
+# clearly separate the patches from the original work must be clearly
+# labeled as unofficial distributions. Modifications which do not
+# carry the name Zope may be packaged in any form, as long as they
+# conform to all of the clauses above.
+#
+#
+# Disclaimer
+#
+# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
+# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+# This software consists of contributions made by Digital Creations and
+# many individuals on behalf of Digital Creations. Specific
+# attributions are listed in the accompanying credits file.
+#
+##############################################################################
+"""
+Core session tracking SessionData class.
+
+$Id$
+"""
+
+__version__='$Revision$'[11:-2]
+
+import Globals
+from Globals import HTMLFile, MessageDialog
from TransienceInterfaces import Transient, DictionaryLike, ItemWithId,\
TTWDictionary, ImmutablyValuedMappingOfPickleableObjects,\
StringKeyedHomogeneousItemContainer, TransientItemContainer
-from OFS.Item import SimpleItem
-from Persistent import Persistence
-from Acquisition import Implicit
+from OFS.SimpleItem import SimpleItem
+from Persistence import Persistent, PersistentMapping
+from Acquisition import Implicit, aq_base
+from AccessControl import ClassSecurityInfo
+import os.path
+import time
+
+_notfound = []
+
+# permissions
+ADD_DATAMGR_PERM = 'Add Transient Object Container'
+CHANGE_DATAMGR_PERM = 'Change Transient Object Containers'
+MGMT_SCREEN_PERM = 'View management screens'
+ACCESS_CONTENTS_PERM = 'Access contents information'
+ACCESS_SESSIONDATA_PERM = 'Access Transient Objects'
+MANAGE_CONTAINER_PERM = 'Manage Transient Object Container'
+
+
+constructTransientObjectContainerForm = HTMLFile(
+ 'dtml/addTransientObjectContainer', globals())
+
+def constructTransientObjectContainer(self, id, title='', timeout_mins=20,
+ addNotification=None, delNotification=None,
+ REQUEST=None):
+
+ """ """
+ ob = TransientObjectContainer(id, title, timeout_mins,
+ addNotification, delNotification)
+ self._setObject(id, ob)
+ if REQUEST is not None:
+ return self.manage_main(self, REQUEST, update_menu=1)
class TransientObjectContainer(SimpleItem):
""" akin to Session Data Container """
+
+ meta_type = "Transient Object Container"
+
+ icon = "misc_/Transience/datacontainer.gif"
+
__implements__ = (ItemWithId,
StringKeyedHomogeneousItemContainer,
TransientItemContainer
)
+
+ manage_options = (
+ { 'label': 'Manage',
+ 'action': 'manage_container',
+ 'help': ('Transience', 'Transience.stx')
+ },
+
+ { 'label': 'Security',
+ 'action': 'manage_access'
+ },
+
+ { 'label': 'Advanced',
+ 'action': 'manage_advanced'
+ }
+ )
+
+ security = ClassSecurityInfo()
+
+ security.setPermissionDefault(MANAGE_CONTAINER_PERM,
+ ['Manager',])
+ security.setPermissionDefault(MGMT_SCREEN_PERM,
+ ['Manager',])
+ security.setPermissionDefault(ACCESS_CONTENTS_PERM,
+ ['Manager','Anonymous'])
+ security.setPermissionDefault(ACCESS_SESSIONDATA_PERM,
+ ['Manager','Anonymous'])
+
+ security.declareProtected(MGMT_SCREEN_PERM, 'manage_container')
+ manage_container = HTMLFile('dtml/manageTransientObjectContainer',
+ globals())
+
+ security.declareProtected(MGMT_SCREEN_PERM, 'manage_advanced')
+ manage_advanced = HTMLFile('dtml/manageImpExpTransientObjects', globals())
+
+ security.setDefaultAccess('deny')
+
+ #
+ # Initializer
+ #
+
+ def __init__(self, id, title='', timeout_mins=20, addNotification=None,
+ delNotification=None):
+
+ self.id = id
+ self.title=title
+ self._container = {}
+ self._addCallback = None
+ self._delCallback = None
+ self.setTimeoutMinutes(timeout_mins)
+
+ self.setAddNotificationTarget(addNotification)
+ self.setDelNotificationTarget(delNotification)
+
+ # -----------------------------------------------------------------
+ # ItemWithID
+ #
+
+ def getId(self):
+ return self.id
+
+
+ # -----------------------------------------------------------------
+ # StringKeyedHomogenousItemContainer
+ #
+
+ def new(self, key):
+
+ if type(key) is not type(''):
+ raise TypeError, (key, "key is not a string type")
+
+ if self._container.has_key(key):
+ raise KeyError, key # Not allowed to dup keys
+
+ item = TransientObject(key, parent=self)
+
+ self._container[key] = item
+
+ return item
+
+
+ def new_or_existing(self, key):
+
+ item = self._container.get(key,_notfound)
+ if item is not _notfound: return item
+
+ return self.new(key)
+
+ # -----------------------------------------------------------------
+ # TransientItemContainer
+ #
+
+ security.declareProtected(MANAGE_CONTAINER_PERM, 'setTimeoutMinutes')
+ def setTimeoutMinutes(self, timeout_mins):
+ self._timeout = timeout_mins
+
+ security.declareProtected(MGMT_SCREEN_PERM, 'getTimeoutMinutes')
+ def getTimeoutMinutes(self):
+ return self._timeout
+
+ security.declareProtected(MGMT_SCREEN_PERM, 'getAddNotificationTarget')
+ def getAddNotificationTarget(self):
+ # What might we do here to help through the web stuff?
+ return self._addCallback
+
+ security.declareProtected(MANAGE_CONTAINER_PERM,
+ 'setAddNotificationTarget')
+ def setAddNotificationTarget(self, f):
+ # We should assert that the callback function 'f' implements
+ # the TransientNotification interface
+ self._addCallback = f
+
+ security.declareProtected(MGMT_SCREEN_PERM, 'getDelNotificationTarget')
+ def getDelNotificationTarget(self):
+ # What might we do here to help through the web stuff?
+ return self._delCallback
+
+ security.declareProtected(MANAGE_CONTAINER_PERM,
+ 'setDelNotificationTarget')
+ def setDelNotificationTarget(self, f):
+ # We should assert that the callback function 'f' implements
+ # the TransientNotification interface
+ self._delCallback = f
+
+ def notifyAdd(self, item):
+ if self._addCallback:
+ try:
+ self._addCallback(item, self) # Use self as context
+ except: pass # Eat all errors
+
+ def notifyDestruct(self, item):
+ if self._delCallback:
+ try:
+ self._delCallback(item, self) # Use self as context
+ except: pass # Eat all errors
+
+ # -----------------------------------------------------------------
+ # Management item support (non API)
+ #
+
+
+ security.declareProtected(MGMT_SCREEN_PERM, 'getLen')
+ def getLen(self):
+
+ """
+ Potentially expensive helper function to figure out how
+ many items are contained.
+ """
+ return len(self._container)
+
+
+ security.declareProtected(MANAGE_CONTAINER_PERM,
+ 'manage_changeTransientObjectContainer')
+ def manage_changeTransientObjectContainer(self, title='',
+ timeout_mins=20, addNotification=None, delNotification=None,
+ REQUEST=None):
+
+ """
+ Change an existing transient object container.
+ """
+
+ self.title = title
+ self.setTimeoutMinutes(timeout_mins)
+ self.setAddNotificationTarget(addNotification)
+ self.setDelNotificationTarget(delNotification)
+
+ if REQUEST is not None:
+ return self.manage_container(self, REQUEST)
+
+
+ security.declareProtected(MANAGE_CONTAINER_PERM,
+ 'manage_exportTransientObjects')
+ def manage_exportTransientObjects(self, REQUEST=None):
+
+ """
+ Export the transient objects to a named file in the var directory.
+ """
+
+ f = os.path.join(Globals.data_dir, "transientobjects.zexp")
+ self.c = PersistentMapping()
+ for k, v in self._container.items():
+ self.c[k] = v
+
+ get_transaction().commit()
+ self.c._p_jar.exportFile(self.c._p_oid, f)
+ del self.c
+ if REQUEST is not None:
+ return MessageDialog(
+ title="Transient objects exported",
+ message="Transient objects exported to %s" % f,
+ action="manage_container")
+
+ security.declareProtected(MANAGE_CONTAINER_PERM,
+ 'manage_importTransientObjects')
+ def manage_importTransientObjects(self, REQUEST=None):
+ """
+ Import the transient objects from a zexp file.
+ """
+ f = os.path.join(Globals.data_dir, "transientobjects.zexp")
+ conn = self._p_jar
+ ob = conn.importFile(f)
+ for k,v in ob.items():
+ self._container[k] = v
+ if REQUEST is not None:
+ return MessageDialog(
+ title="Transient objects imported",
+ message="Transient objects imported from %s" % f,
+ action="manage_container")
+
+
class TransientObject(Persistent, Implicit):
""" akin to Session Data Object """
__implements__ = (ItemWithId, # randomly generate an id
@@ -20,4 +356,132 @@
TTWDictionary,
ImmutablyValuedMappingOfPickleableObjects
)
-
+
+ security = ClassSecurityInfo()
+ security.setDefaultAccess('allow')
+ security.declareObjectPublic()
+
+ #
+ # Initialzer
+ #
+
+ def __init__(self, id, parent=None, time=time.time):
+ self.id = id
+ self._parent = parent
+ self._container = {}
+ self._created = self._last_accessed = time()
+ self._timergranularity = 30 # timer granularity for last accessed
+
+
+ # -----------------------------------------------------------------
+ # ItemWithId
+ #
+
+ def getId(self):
+ return self.id
+
+ # -----------------------------------------------------------------
+ # Transient
+ #
+
+ def invalidate(self):
+ parent = self._parent
+ if parent: parent.notifyDestruct(self)
+ self._invalid = None
+
+ def getLastAccessed(self):
+ return self._last_accessed
+
+ def setLastAccessed(self, time=time.time):
+ # check to see if the last_accessed time is too recent, and avoid
+ # setting if so, to cut down on heavy writes
+ t = time()
+ if self._last_accessed and (self._last_accessed +
+ self._timergranularity < t):
+
+ self._last_accessed = t
+
+ def getCreated(self):
+ return self._created
+
+
+ # -----------------------------------------------------------------
+ # DictionaryLike
+ #
+
+
+ def keys(self):
+ return self._container.keys()
+
+ def values(self):
+ return self._container.values()
+
+ def items(self):
+ return self._container.items()
+
+ def get(self, k, default=None):
+ return self._container.get(k, default)
+
+ def has_key(self, k, marker=_notfound):
+ if self._container.get(k, marker) is not _notfound: return 1
+
+ def clear(self):
+ self._container.clear()
+ self._p_changed = 1
+
+ def update(self, d):
+ for k in d.keys():
+ self[k] = d[k]
+
+
+ # -----------------------------------------------------------------
+ # ImmutablyValuedMappingOfPickleableObjects (what a mouthful!)
+ #
+
+ def __setitem__(self, k, v):
+ # if the key or value is a persistent instance,
+ # set up its _p_jar immediately
+ if hasattr(v, '_p_jar') and v._p_jar is None:
+ v._p_jar = self._p_jar
+ v._p_changed = 1
+ if hasattr(k, '_p_jar') and k._p_jar is None:
+ k._p_jar = self._p_jar
+ k._p_changed = 1
+ # unwrap this thing if it's wrapped
+ k = aq_base(k)
+ v = aq_base(v)
+ self._container[k] = v
+ self._p_changed = 1
+
+ def __getitem__(self, k):
+ return self._container[k]
+
+ def __delitem__(self, k):
+ del self._container[k]
+
+ # -----------------------------------------------------------------
+ # TTWDictionary
+ #
+
+ set = __setitem__
+
+ def delete(self, k):
+ del self._container[k]
+ self._p_changed = 1
+
+ __guarded_setitem__ = __setitem__
+
+
+ # -----------------------------------------------------------------
+ # Other non interface code
+ #
+
+ def _p_independent(self):
+ # My state doesn't depend on or materially effect the state of
+ # other objects (eliminates read conflicts).
+ return 1
+
+ getName = getId
+
+Globals.InitializeClass(TransientObjectContainer)
+Globals.InitializeClass(TransientObject)
=== Products/Transience/TransienceInterfaces.py 1.2 => 1.3 ===
"""
+ # DictionaryLike does NOT support copy()
+
class ItemWithId(Interface.Base):
def getId(self):
"""
@@ -178,7 +180,12 @@
before expiration.
"""
- def setExecuteAfterAddFunc(self, f):
+ def getAddNotificationTarget(self):
+ """
+ Returns the current 'after add' function, or None.
+ """
+
+ def setAddNotificationTarget(self, f):
"""
Cause the 'after add' function to be 'f'.
@@ -189,7 +196,12 @@
is the item being added to the container.
"""
- def setExecuteBeforeDestructFunc(self, f):
+ def getDelNotificationTarget(self):
+ """
+ Returns the current 'before destruction' function, or None.
+ """
+
+ def setDelNotificationTarget(self, f):
"""
Cause the 'before destruction' function to be 'f'.
@@ -200,3 +212,38 @@
which is the item being destroyed.
"""
+ def notifyAdd(self, item):
+ """
+ Calls the registered ExecuteAfterAdd function on item.
+
+ Raises no errors (traps errors).
+ """
+
+ def notifyDestruct(self, item):
+ """
+ Calls the registered ExecuteBeforeDestruct function on item.
+
+ Raises no errors (traps errors).
+ """
+
+
+class TransientNotification(Interface.Base):
+
+ """
+ Specfies what something must conform to to receive callbacks from
+ the setExecuteAfterAdd and SetExecuteBeforeDestruct functions. Note
+ that this isn't a true interface -- the callbacks are registered, not
+ evaluated by name.
+ """
+
+ def executeAfterAdd(self, item, context):
+ """
+ Called when an item is added. Item is the item being added,
+ context is the environment context, where appropriate.
+ """
+
+ def executeBeforeDestruct(self, item, context):
+ """
+ Called when an item is deleted. Item is the item being deleted,
+ context is the environment context, where appropriate.
+ """
=== Products/Transience/__init__.py 1.1.1.1 => 1.2 ===
+#
+# Zope Public License (ZPL) Version 1.0
+# -------------------------------------
+#
+# Copyright (c) Digital Creations. All rights reserved.
+#
+# This license has been certified as Open Source(tm).
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# 1. Redistributions in source code must retain the above copyright
+# notice, this list of conditions, and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions, and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. Digital Creations requests that attribution be given to Zope
+# in any manner possible. Zope includes a "Powered by Zope"
+# button that is installed by default. While it is not a license
+# violation to remove this button, it is requested that the
+# attribution remain. A significant investment has been put
+# into Zope, and this effort will continue if the Zope community
+# continues to grow. This is one way to assure that growth.
+#
+# 4. All advertising materials and documentation mentioning
+# features derived from or use of this software must display
+# the following acknowledgement:
+#
+# "This product includes software developed by Digital Creations
+# for use in the Z Object Publishing Environment
+# (http://www.zope.org/)."
+#
+# In the event that the product being advertised includes an
+# intact Zope distribution (with copyright and license included)
+# then this clause is waived.
+#
+# 5. Names associated with Zope or Digital Creations must not be used to
+# endorse or promote products derived from this software without
+# prior written permission from Digital Creations.
+#
+# 6. Modified redistributions of any form whatsoever must retain
+# the following acknowledgment:
+#
+# "This product includes software developed by Digital Creations
+# for use in the Z Object Publishing Environment
+# (http://www.zope.org/)."
+#
+# Intact (re-)distributions of any official Zope release do not
+# require an external acknowledgement.
+#
+# 7. Modifications are encouraged but must be packaged separately as
+# patches to official Zope releases. Distributions that do not
+# clearly separate the patches from the original work must be clearly
+# labeled as unofficial distributions. Modifications which do not
+# carry the name Zope may be packaged in any form, as long as they
+# conform to all of the clauses above.
+#
+#
+# Disclaimer
+#
+# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
+# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+# This software consists of contributions made by Digital Creations and
+# many individuals on behalf of Digital Creations. Specific
+# attributions are listed in the accompanying credits file.
+#
+##############################################################################
+"""
+Transience initialization routines
+
+$Id$
+"""
+
+import Transience
+
+def initialize(context):
+ context.registerClass(
+ Transience.TransientObjectContainer,
+ permission=Transience.ADD_DATAMGR_PERM,
+ icon='www/datacontainer.gif',
+ constructors=(Transience.constructTransientObjectContainerForm,
+ Transience.constructTransientObjectContainer)
+ )
+ context.registerHelp()